diff --git a/.changeset/restore-scout-codesearch.md b/.changeset/restore-scout-codesearch.md new file mode 100644 index 00000000000..6d83328eda1 --- /dev/null +++ b/.changeset/restore-scout-codesearch.md @@ -0,0 +1,5 @@ +--- +"@kilocode/cli": patch +--- + +Preserve the code search tool and Scout access after the OpenCode provider and agent updates. diff --git a/.changeset/session-patches-stay-whole.md b/.changeset/session-patches-stay-whole.md new file mode 100644 index 00000000000..05f145ceac8 --- /dev/null +++ b/.changeset/session-patches-stay-whole.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Preserve complete session state when applying partial session updates in VS Code. diff --git a/.changeset/steady-tui-streams.md b/.changeset/steady-tui-streams.md new file mode 100644 index 00000000000..65b122ba078 --- /dev/null +++ b/.changeset/steady-tui-streams.md @@ -0,0 +1,5 @@ +--- +"@kilocode/cli": patch +--- + +Restore streamed responses in the CLI TUI and move code indexing status into the session sidebar. diff --git a/.github/actions/setup-bun/action.yml b/.github/actions/setup-bun/action.yml index 9859174a2e3..5b44517ec51 100644 --- a/.github/actions/setup-bun/action.yml +++ b/.github/actions/setup-bun/action.yml @@ -23,7 +23,7 @@ runs: fi - name: Setup Bun - uses: oven-sh/setup-bun@v2 + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 with: bun-version-file: ${{ !steps.bun-url.outputs.url && 'package.json' || '' }} bun-download-url: ${{ steps.bun-url.outputs.url }} @@ -33,8 +33,9 @@ runs: shell: bash run: echo "dir=$(bun pm cache)" >> "$GITHUB_OUTPUT" - - name: Cache Bun dependencies - uses: actions/cache@v4 + - name: Restore Bun dependencies + id: bun-cache + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: ${{ steps.cache.outputs.dir }} key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} @@ -56,3 +57,10 @@ runs: bun install ${{ inputs.install-flags }} fi shell: bash + + - name: Save Bun dependencies + if: steps.bun-cache.outputs.cache-hit != 'true' && github.event_name != 'pull_request' && github.event_name != 'pull_request_target' + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: ${{ steps.cache.outputs.dir }} + key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} diff --git a/.github/actions/setup-git-committer/action.yml b/.github/actions/setup-git-committer/action.yml index 0474b670a6e..fa00f28b051 100644 --- a/.github/actions/setup-git-committer/action.yml +++ b/.github/actions/setup-git-committer/action.yml @@ -21,7 +21,7 @@ runs: steps: - name: Create app token id: apptoken - uses: actions/create-github-app-token@v2 + uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2.2.2 with: # kilocode_change start app-id: ${{ inputs['kilo-maintainer-app-id'] }} diff --git a/.github/workflows/close-issues.yml b/.github/workflows/close-issues.yml index c6575f750a2..c071fd5bc83 100644 --- a/.github/workflows/close-issues.yml +++ b/.github/workflows/close-issues.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v6 # kilocode_change - - uses: oven-sh/setup-bun@v2 + - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 with: bun-version: latest diff --git a/.github/workflows/close-stale-prs.yml b/.github/workflows/close-stale-prs.yml index 13b0cf02197..3e7b1db14b7 100644 --- a/.github/workflows/close-stale-prs.yml +++ b/.github/workflows/close-stale-prs.yml @@ -22,7 +22,7 @@ jobs: timeout-minutes: 15 steps: - name: Close inactive PRs - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/containers.yml b/.github/workflows/containers.yml index a261d6f5b3b..797252b7fd8 100644 --- a/.github/workflows/containers.yml +++ b/.github/workflows/containers.yml @@ -31,13 +31,13 @@ jobs: - uses: ./.github/actions/setup-bun - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 - name: Login to GHCR - uses: docker/login-action@v3 + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 with: registry: ghcr.io username: ${{ github.repository_owner }} diff --git a/.github/workflows/disabled/compliance-close.yml.disabled b/.github/workflows/disabled/compliance-close.yml.disabled index c3bcf9f686f..14e68701e57 100644 --- a/.github/workflows/disabled/compliance-close.yml.disabled +++ b/.github/workflows/disabled/compliance-close.yml.disabled @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Close non-compliant issues and PRs after 2 hours - uses: actions/github-script@v7 + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 with: script: | const { data: items } = await github.rest.issues.listForRepo({ diff --git a/.github/workflows/disabled/notify-discord.yml.disabled b/.github/workflows/disabled/notify-discord.yml.disabled index 6bd1ab67153..1cfb52990ad 100644 --- a/.github/workflows/disabled/notify-discord.yml.disabled +++ b/.github/workflows/disabled/notify-discord.yml.disabled @@ -12,6 +12,6 @@ jobs: runs-on: depot-ubuntu-24.04 steps: - name: Send nicely-formatted embed to Discord - uses: SethCohen/github-releases-to-discord@v1 + uses: SethCohen/github-releases-to-discord@24d166886aee4646d448c8a389ff9e1ebcab3682 # v1.20.0 with: webhook_url: ${{ secrets.DISCORD_WEBHOOK }} diff --git a/.github/workflows/disabled/pr-management.yml.disabled b/.github/workflows/disabled/pr-management.yml.disabled index 1e331bd6f2e..b43c6af9bab 100644 --- a/.github/workflows/disabled/pr-management.yml.disabled +++ b/.github/workflows/disabled/pr-management.yml.disabled @@ -12,7 +12,7 @@ jobs: pull-requests: write steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: fetch-depth: 1 @@ -78,7 +78,7 @@ jobs: issues: write steps: - name: Add Contributor Label - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: | const isPR = !!context.payload.pull_request; diff --git a/.github/workflows/disabled/pr-standards.yml.disabled b/.github/workflows/disabled/pr-standards.yml.disabled index 1edbd5d061d..06838089d35 100644 --- a/.github/workflows/disabled/pr-standards.yml.disabled +++ b/.github/workflows/disabled/pr-standards.yml.disabled @@ -12,7 +12,7 @@ jobs: pull-requests: write steps: - name: Check PR standards - uses: actions/github-script@v7 + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 with: script: | const pr = context.payload.pull_request; @@ -159,7 +159,7 @@ jobs: pull-requests: write steps: - name: Check PR template compliance - uses: actions/github-script@v7 + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 with: script: | const pr = context.payload.pull_request; diff --git a/.github/workflows/disabled/publish-github-action.yml.disabled b/.github/workflows/disabled/publish-github-action.yml.disabled index 3d8426a0dbc..e16d5f7919e 100644 --- a/.github/workflows/disabled/publish-github-action.yml.disabled +++ b/.github/workflows/disabled/publish-github-action.yml.disabled @@ -16,7 +16,7 @@ jobs: publish: runs-on: blacksmith-4vcpu-ubuntu-2404 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 with: fetch-depth: 0 diff --git a/.github/workflows/disabled/release-github-action.yml.disabled b/.github/workflows/disabled/release-github-action.yml.disabled index ddfb7a7fca8..6c2987c8eba 100644 --- a/.github/workflows/disabled/release-github-action.yml.disabled +++ b/.github/workflows/disabled/release-github-action.yml.disabled @@ -16,7 +16,7 @@ jobs: release: runs-on: blacksmith-4vcpu-ubuntu-2404 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: fetch-depth: 0 diff --git a/.github/workflows/disabled/review.yml.disabled b/.github/workflows/disabled/review.yml.disabled index b44d262df7c..7f7dc7e8b9b 100644 --- a/.github/workflows/disabled/review.yml.disabled +++ b/.github/workflows/disabled/review.yml.disabled @@ -29,7 +29,7 @@ jobs: fi - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: fetch-depth: 1 diff --git a/.github/workflows/disabled/stats.yml.disabled b/.github/workflows/disabled/stats.yml.disabled index 3da92d4691a..b7e1f43b9f4 100644 --- a/.github/workflows/disabled/stats.yml.disabled +++ b/.github/workflows/disabled/stats.yml.disabled @@ -15,7 +15,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Setup Bun uses: ./.github/actions/setup-bun diff --git a/.github/workflows/disabled/storybook.yml.disabled b/.github/workflows/disabled/storybook.yml.disabled index 6d143a8a22f..1e652104d69 100644 --- a/.github/workflows/disabled/storybook.yml.disabled +++ b/.github/workflows/disabled/storybook.yml.disabled @@ -29,7 +29,7 @@ jobs: runs-on: blacksmith-4vcpu-ubuntu-2404 steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Setup Bun uses: ./.github/actions/setup-bun diff --git a/.github/workflows/disabled/sync-zed-extension.yml.disabled b/.github/workflows/disabled/sync-zed-extension.yml.disabled index f14487cde97..6e4b44083c1 100644 --- a/.github/workflows/disabled/sync-zed-extension.yml.disabled +++ b/.github/workflows/disabled/sync-zed-extension.yml.disabled @@ -10,7 +10,7 @@ jobs: name: Release Zed Extension runs-on: blacksmith-4vcpu-ubuntu-2404 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: fetch-depth: 0 diff --git a/.github/workflows/nix-eval.yml b/.github/workflows/nix-eval.yml index 51c467e1b24..ff4bc3462a2 100644 --- a/.github/workflows/nix-eval.yml +++ b/.github/workflows/nix-eval.yml @@ -21,10 +21,10 @@ jobs: timeout-minutes: 15 steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup Nix - uses: nixbuild/nix-quick-install-action@v34 + uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34 - name: Evaluate flake outputs (all systems) run: | diff --git a/.github/workflows/nix-hashes.yml b/.github/workflows/nix-hashes.yml index be48bb895e1..8d9980049f1 100644 --- a/.github/workflows/nix-hashes.yml +++ b/.github/workflows/nix-hashes.yml @@ -42,10 +42,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup Nix - uses: nixbuild/nix-quick-install-action@v34 + uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34 - name: Compute node_modules hash id: hash diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c9301c042d3..76e7f06d2ce 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -46,7 +46,7 @@ jobs: - name: Setup Node id: setup-node continue-on-error: ${{ runner.os == 'Windows' }} - uses: actions/setup-node@v6 + uses: actions/setup-node@v6 # kilocode_change with: node-version: "24" @@ -86,7 +86,7 @@ jobs: - name: Publish unit reports if: always() - uses: mikepenz/action-junit-report@v6 + uses: mikepenz/action-junit-report@bccf2e31636835cf0874589931c4116687171386 # v6.4.0 with: report_paths: packages/*/.artifacts/unit/junit.xml check_name: "unit results (${{ matrix.settings.name }})" diff --git a/.kilo/skills/release-jetbrains/script/dispatch-prepare.ts b/.kilo/skills/release-jetbrains/script/dispatch-prepare.ts index 9589c795416..3fa3411cdcf 100644 --- a/.kilo/skills/release-jetbrains/script/dispatch-prepare.ts +++ b/.kilo/skills/release-jetbrains/script/dispatch-prepare.ts @@ -17,7 +17,9 @@ const { values } = parseArgs({ }) if (values.help) { - console.log(`Usage: bun .kilo/skills/release-jetbrains/script/dispatch-prepare.ts --kind --version [--from-tag ] [--run-id ]`) + console.log( + `Usage: bun .kilo/skills/release-jetbrains/script/dispatch-prepare.ts --kind --version [--from-tag ] [--run-id ]`, + ) process.exit(0) } diff --git a/.kilo/skills/release-jetbrains/script/resolve-version.ts b/.kilo/skills/release-jetbrains/script/resolve-version.ts index 57799b66f65..8245a643619 100644 --- a/.kilo/skills/release-jetbrains/script/resolve-version.ts +++ b/.kilo/skills/release-jetbrains/script/resolve-version.ts @@ -13,7 +13,9 @@ const { values } = parseArgs({ }) if (values.help) { - console.log(`Usage: bun .kilo/skills/release-jetbrains/script/resolve-version.ts --spec `) + console.log( + `Usage: bun .kilo/skills/release-jetbrains/script/resolve-version.ts --spec `, + ) process.exit(0) } diff --git a/.kilo/skills/release-jetbrains/script/update-changelog.ts b/.kilo/skills/release-jetbrains/script/update-changelog.ts index d7dca8b3a75..b6a3ff071fc 100644 --- a/.kilo/skills/release-jetbrains/script/update-changelog.ts +++ b/.kilo/skills/release-jetbrains/script/update-changelog.ts @@ -15,7 +15,9 @@ const { values } = parseArgs({ }) if (values.help) { - console.log(`Usage: bun .kilo/skills/release-jetbrains/script/update-changelog.ts --version --file `) + console.log( + `Usage: bun .kilo/skills/release-jetbrains/script/update-changelog.ts --version --file `, + ) process.exit(0) } @@ -39,7 +41,11 @@ const text = Buffer.from(current.content.replace(/\s/g, ""), "base64").toString( const pattern = regex(ver) if (!pattern.test(text)) throw new Error(`${path} is missing a section for ${ver} on ${branch}`) -const next = text.replace(pattern, `\n${section}\n`).replace(/\n{3,}/g, "\n\n").trimEnd() + "\n" +const next = + text + .replace(pattern, `\n${section}\n`) + .replace(/\n{3,}/g, "\n\n") + .trimEnd() + "\n" if (next === text) { console.log("CHANGELOG.md already contains the provided section") process.exit(0) diff --git a/.kilo/skills/release-jetbrains/script/watch-publish.ts b/.kilo/skills/release-jetbrains/script/watch-publish.ts index 719d46c4c1c..0719bcb2071 100644 --- a/.kilo/skills/release-jetbrains/script/watch-publish.ts +++ b/.kilo/skills/release-jetbrains/script/watch-publish.ts @@ -17,7 +17,9 @@ const { values } = parseArgs({ }) if (values.help) { - console.log(`Usage: bun .kilo/skills/release-jetbrains/script/watch-publish.ts --pr --version [--merge] [--run-id ]`) + console.log( + `Usage: bun .kilo/skills/release-jetbrains/script/watch-publish.ts --pr --version [--merge] [--run-id ]`, + ) process.exit(0) } @@ -70,11 +72,19 @@ async function find() { if (run) return String(run.databaseId) await Bun.sleep(1000) } - throw new Error(`No ${workflow} run found for ${branch}. Merge PR ${pr} first, or pass --merge to merge it automatically.`) + throw new Error( + `No ${workflow} run found for ${branch}. Merge PR ${pr} first, or pass --merge to merge it automatically.`, + ) } function active(status: string) { - return status === "queued" || status === "in_progress" || status === "waiting" || status === "requested" || status === "pending" + return ( + status === "queued" || + status === "in_progress" || + status === "waiting" || + status === "requested" || + status === "pending" + ) } async function runs() { diff --git a/.opencode-version b/.opencode-version index 6ce43629e9b..2bc9bb1ba3e 100644 --- a/.opencode-version +++ b/.opencode-version @@ -1 +1 @@ -v1.14.48 +v1.14.51 diff --git a/.opencode/skills/improve-codebase-architecture/DEEPENING.md b/.opencode/skills/improve-codebase-architecture/DEEPENING.md new file mode 100644 index 00000000000..c52fdfd99f3 --- /dev/null +++ b/.opencode/skills/improve-codebase-architecture/DEEPENING.md @@ -0,0 +1,37 @@ +# Deepening + +How to deepen a cluster of shallow modules safely, given its dependencies. Assumes the vocabulary in [LANGUAGE.md](LANGUAGE.md) — **module**, **interface**, **seam**, **adapter**. + +## Dependency categories + +When assessing a candidate for deepening, classify its dependencies. The category determines how the deepened module is tested across its seam. + +### 1. In-process + +Pure computation, in-memory state, no I/O. Always deepenable — merge the modules and test through the new interface directly. No adapter needed. + +### 2. Local-substitutable + +Dependencies that have local test stand-ins (PGLite for Postgres, in-memory filesystem). Deepenable if the stand-in exists. The deepened module is tested with the stand-in running in the test suite. The seam is internal; no port at the module's external interface. + +### 3. Remote but owned (Ports & Adapters) + +Your own services across a network boundary (microservices, internal APIs). Define a **port** (interface) at the seam. The deep module owns the logic; the transport is injected as an **adapter**. Tests use an in-memory adapter. Production uses an HTTP/gRPC/queue adapter. + +Recommendation shape: _"Define a port at the seam, implement an HTTP adapter for production and an in-memory adapter for testing, so the logic sits in one deep module even though it's deployed across a network."_ + +### 4. True external (Mock) + +Third-party services (Stripe, Twilio, etc.) you don't control. The deepened module takes the external dependency as an injected port; tests provide a mock adapter. + +## Seam discipline + +- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a port unless at least two adapters are justified (typically production + test). A single-adapter seam is just indirection. +- **Internal seams vs external seams.** A deep module can have internal seams (private to its implementation, used by its own tests) as well as the external seam at its interface. Don't expose internal seams through the interface just because tests use them. + +## Testing strategy: replace, don't layer + +- Old unit tests on shallow modules become waste once tests at the deepened module's interface exist — delete them. +- Write new tests at the deepened module's interface. The **interface is the test surface**. +- Tests assert on observable outcomes through the interface, not internal state. +- Tests should survive internal refactors — they describe behaviour, not implementation. If a test has to change when the implementation changes, it's testing past the interface. diff --git a/.opencode/skills/improve-codebase-architecture/INTERFACE-DESIGN.md b/.opencode/skills/improve-codebase-architecture/INTERFACE-DESIGN.md new file mode 100644 index 00000000000..3197723a0d0 --- /dev/null +++ b/.opencode/skills/improve-codebase-architecture/INTERFACE-DESIGN.md @@ -0,0 +1,44 @@ +# Interface Design + +When the user wants to explore alternative interfaces for a chosen deepening candidate, use this parallel sub-agent pattern. Based on "Design It Twice" (Ousterhout) — your first idea is unlikely to be the best. + +Uses the vocabulary in [LANGUAGE.md](LANGUAGE.md) — **module**, **interface**, **seam**, **adapter**, **leverage**. + +## Process + +### 1. Frame the problem space + +Before spawning sub-agents, write a user-facing explanation of the problem space for the chosen candidate: + +- The constraints any new interface would need to satisfy +- The dependencies it would rely on, and which category they fall into (see [DEEPENING.md](DEEPENING.md)) +- A rough illustrative code sketch to ground the constraints — not a proposal, just a way to make the constraints concrete + +Show this to the user, then immediately proceed to Step 2. The user reads and thinks while the sub-agents work in parallel. + +### 2. Spawn sub-agents + +Spawn 3+ sub-agents in parallel using the Agent tool. Each must produce a **radically different** interface for the deepened module. + +Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category from [DEEPENING.md](DEEPENING.md), what sits behind the seam). The brief is independent of the user-facing problem-space explanation in Step 1. Give each agent a different design constraint: + +- Agent 1: "Minimize the interface — aim for 1–3 entry points max. Maximise leverage per entry point." +- Agent 2: "Maximise flexibility — support many use cases and extension." +- Agent 3: "Optimise for the most common caller — make the default case trivial." +- Agent 4 (if applicable): "Design around ports & adapters for cross-seam dependencies." + +Include both [LANGUAGE.md](LANGUAGE.md) vocabulary and CONTEXT.md vocabulary in the brief so each sub-agent names things consistently with the architecture language and the project's domain language. + +Each sub-agent outputs: + +1. Interface (types, methods, params — plus invariants, ordering, error modes) +2. Usage example showing how callers use it +3. What the implementation hides behind the seam +4. Dependency strategy and adapters (see [DEEPENING.md](DEEPENING.md)) +5. Trade-offs — where leverage is high, where it's thin + +### 3. Present and compare + +Present designs sequentially so the user can absorb each one, then compare them in prose. Contrast by **depth** (leverage at the interface), **locality** (where change concentrates), and **seam placement**. + +After comparing, give your own recommendation: which design you think is strongest and why. If elements from different designs would combine well, propose a hybrid. Be opinionated — the user wants a strong read, not a menu. diff --git a/.opencode/skills/improve-codebase-architecture/LANGUAGE.md b/.opencode/skills/improve-codebase-architecture/LANGUAGE.md new file mode 100644 index 00000000000..dd9b60fea07 --- /dev/null +++ b/.opencode/skills/improve-codebase-architecture/LANGUAGE.md @@ -0,0 +1,53 @@ +# Language + +Shared vocabulary for every suggestion this skill makes. Use these terms exactly — don't substitute "component," "service," "API," or "boundary." Consistent language is the whole point. + +## Terms + +**Module** +Anything with an interface and an implementation. Deliberately scale-agnostic — applies equally to a function, class, package, or tier-spanning slice. +_Avoid_: unit, component, service. + +**Interface** +Everything a caller must know to use the module correctly. Includes the type signature, but also invariants, ordering constraints, error modes, required configuration, and performance characteristics. +_Avoid_: API, signature (too narrow — those refer only to the type-level surface). + +**Implementation** +What's inside a module — its body of code. Distinct from **Adapter**: a thing can be a small adapter with a large implementation (a Postgres repo) or a large adapter with a small implementation (an in-memory fake). Reach for "adapter" when the seam is the topic; "implementation" otherwise. + +**Depth** +Leverage at the interface — the amount of behaviour a caller (or test) can exercise per unit of interface they have to learn. A module is **deep** when a large amount of behaviour sits behind a small interface. A module is **shallow** when the interface is nearly as complex as the implementation. + +**Seam** _(from Michael Feathers)_ +A place where you can alter behaviour without editing in that place. The _location_ at which a module's interface lives. Choosing where to put the seam is its own design decision, distinct from what goes behind it. +_Avoid_: boundary (overloaded with DDD's bounded context). + +**Adapter** +A concrete thing that satisfies an interface at a seam. Describes _role_ (what slot it fills), not substance (what's inside). + +**Leverage** +What callers get from depth. More capability per unit of interface they have to learn. One implementation pays back across N call sites and M tests. + +**Locality** +What maintainers get from depth. Change, bugs, knowledge, and verification concentrate at one place rather than spreading across callers. Fix once, fixed everywhere. + +## Principles + +- **Depth is a property of the interface, not the implementation.** A deep module can be internally composed of small, mockable, swappable parts — they just aren't part of the interface. A module can have **internal seams** (private to its implementation, used by its own tests) as well as the **external seam** at its interface. +- **The deletion test.** Imagine deleting the module. If complexity vanishes, the module wasn't hiding anything (it was a pass-through). If complexity reappears across N callers, the module was earning its keep. +- **The interface is the test surface.** Callers and tests cross the same seam. If you want to test _past_ the interface, the module is probably the wrong shape. +- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a seam unless something actually varies across it. + +## Relationships + +- A **Module** has exactly one **Interface** (the surface it presents to callers and tests). +- **Depth** is a property of a **Module**, measured against its **Interface**. +- A **Seam** is where a **Module**'s **Interface** lives. +- An **Adapter** sits at a **Seam** and satisfies the **Interface**. +- **Depth** produces **Leverage** for callers and **Locality** for maintainers. + +## Rejected framings + +- **Depth as ratio of implementation-lines to interface-lines** (Ousterhout): rewards padding the implementation. We use depth-as-leverage instead. +- **"Interface" as the TypeScript `interface` keyword or a class's public methods**: too narrow — interface here includes every fact a caller must know. +- **"Boundary"**: overloaded with DDD's bounded context. Say **seam** or **interface**. diff --git a/.opencode/skills/improve-codebase-architecture/SKILL.md b/.opencode/skills/improve-codebase-architecture/SKILL.md new file mode 100644 index 00000000000..05984a60968 --- /dev/null +++ b/.opencode/skills/improve-codebase-architecture/SKILL.md @@ -0,0 +1,71 @@ +--- +name: improve-codebase-architecture +description: Find deepening opportunities in a codebase, informed by the domain language in CONTEXT.md and the decisions in docs/adr/. Use when the user wants to improve architecture, find refactoring opportunities, consolidate tightly-coupled modules, or make a codebase more testable and AI-navigable. +--- + +# Improve Codebase Architecture + +Surface architectural friction and propose **deepening opportunities** — refactors that turn shallow modules into deep ones. The aim is testability and AI-navigability. + +## Glossary + +Use these terms exactly in every suggestion. Consistent language is the point — don't drift into "component," "service," "API," or "boundary." Full definitions in [LANGUAGE.md](LANGUAGE.md). + +- **Module** — anything with an interface and an implementation (function, class, package, slice). +- **Interface** — everything a caller must know to use the module: types, invariants, error modes, ordering, config. Not just the type signature. +- **Implementation** — the code inside. +- **Depth** — leverage at the interface: a lot of behaviour behind a small interface. **Deep** = high leverage. **Shallow** = interface nearly as complex as the implementation. +- **Seam** — where an interface lives; a place behaviour can be altered without editing in place. (Use this, not "boundary.") +- **Adapter** — a concrete thing satisfying an interface at a seam. +- **Leverage** — what callers get from depth. +- **Locality** — what maintainers get from depth: change, bugs, knowledge concentrated in one place. + +Key principles (see [LANGUAGE.md](LANGUAGE.md) for the full list): + +- **Deletion test**: imagine deleting the module. If complexity vanishes, it was a pass-through. If complexity reappears across N callers, it was earning its keep. +- **The interface is the test surface.** +- **One adapter = hypothetical seam. Two adapters = real seam.** + +This skill is _informed_ by the project's domain model. The domain language gives names to good seams; ADRs record decisions the skill should not re-litigate. + +## Process + +### 1. Explore + +Read the project's domain glossary and any ADRs in the area you're touching first. + +Then use the Agent tool with `subagent_type=Explore` to walk the codebase. Don't follow rigid heuristics — explore organically and note where you experience friction: + +- Where does understanding one concept require bouncing between many small modules? +- Where are modules **shallow** — interface nearly as complex as the implementation? +- Where have pure functions been extracted just for testability, but the real bugs hide in how they're called (no **locality**)? +- Where do tightly-coupled modules leak across their seams? +- Which parts of the codebase are untested, or hard to test through their current interface? + +Apply the **deletion test** to anything you suspect is shallow: would deleting it concentrate complexity, or just move it? A "yes, concentrates" is the signal you want. + +### 2. Present candidates + +Present a numbered list of deepening opportunities. For each candidate: + +- **Files** — which files/modules are involved +- **Problem** — why the current architecture is causing friction +- **Solution** — plain English description of what would change +- **Benefits** — explained in terms of locality and leverage, and also in how tests would improve + +**Use CONTEXT.md vocabulary for the domain, and [LANGUAGE.md](LANGUAGE.md) vocabulary for the architecture.** If `CONTEXT.md` defines "Order," talk about "the Order intake module" — not "the FooBarHandler," and not "the Order service." + +**ADR conflicts**: if a candidate contradicts an existing ADR, only surface it when the friction is real enough to warrant revisiting the ADR. Mark it clearly (e.g. _"contradicts ADR-0007 — but worth reopening because…"_). Don't list every theoretical refactor an ADR forbids. + +Do NOT propose interfaces yet. Ask the user: "Which of these would you like to explore?" + +### 3. Grilling loop + +Once the user picks a candidate, drop into a grilling conversation. Walk the design tree with them — constraints, dependencies, the shape of the deepened module, what sits behind the seam, what tests survive. + +Side effects happen inline as decisions crystallize: + +- **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md` — same discipline as `/grill-with-docs` (see [CONTEXT-FORMAT.md](../grill-with-docs/CONTEXT-FORMAT.md)). Create the file lazily if it doesn't exist. +- **Sharpening a fuzzy term during the conversation?** Update `CONTEXT.md` right there. +- **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing — skip ephemeral reasons ("not worth it right now") and self-evident ones. See [ADR-FORMAT.md](../grill-with-docs/ADR-FORMAT.md). +- **Want to explore alternative interfaces for the deepened module?** See [INTERFACE-DESIGN.md](INTERFACE-DESIGN.md). diff --git a/.opencode/tui.json b/.opencode/tui.json index f52ed17f410..4cd0d06a261 100644 --- a/.opencode/tui.json +++ b/.opencode/tui.json @@ -1,3 +1,3 @@ { "$schema": "https://opencode.ai/tui.json" -} \ No newline at end of file +} diff --git a/bun.lock b/bun.lock index ed235037f24..e05ea04521b 100644 --- a/bun.lock +++ b/bun.lock @@ -38,22 +38,50 @@ "opencode": "./bin/opencode", }, "dependencies": { + "@ai-sdk/alibaba": "1.0.17", + "@ai-sdk/amazon-bedrock": "4.0.96", + "@ai-sdk/anthropic": "3.0.71", + "@ai-sdk/azure": "3.0.49", + "@ai-sdk/cerebras": "2.0.41", + "@ai-sdk/cohere": "3.0.27", + "@ai-sdk/deepinfra": "2.0.41", + "@ai-sdk/gateway": "3.0.104", + "@ai-sdk/google": "3.0.63", + "@ai-sdk/google-vertex": "4.0.112", + "@ai-sdk/groq": "3.0.31", + "@ai-sdk/mistral": "3.0.27", + "@ai-sdk/openai": "3.0.53", + "@ai-sdk/openai-compatible": "2.0.41", + "@ai-sdk/perplexity": "3.0.26", + "@ai-sdk/provider": "3.0.8", + "@ai-sdk/provider-utils": "4.0.23", + "@ai-sdk/togetherai": "2.0.41", + "@ai-sdk/vercel": "2.0.39", + "@ai-sdk/xai": "3.0.82", + "@aws-sdk/credential-providers": "3.993.0", "@effect/opentelemetry": "catalog:", "@effect/platform-node": "catalog:", + "@kilocode/kilo-gateway": "workspace:*", "@npmcli/arborist": "9.4.0", "@npmcli/config": "10.8.1", + "@openrouter/ai-sdk-provider": "2.8.1", "@opentelemetry/api": "1.9.0", "@opentelemetry/context-async-hooks": "2.6.1", "@opentelemetry/exporter-trace-otlp-http": "0.214.0", "@opentelemetry/sdk-trace-base": "2.6.1", + "ai-gateway-provider": "3.1.2", "cross-spawn": "catalog:", "effect": "catalog:", + "gitlab-ai-provider": "6.6.0", "glob": "13.0.5", + "google-auth-library": "10.5.0", + "immer": "11.1.4", "mime-types": "3.0.2", "minimatch": "10.2.5", "npm-package-arg": "13.0.2", "rotating-file-stream": "3.2.9", "semver": "^7.6.3", + "venice-ai-sdk-provider": "2.0.1", "xdg-basedir": "5.1.0", "zod": "catalog:", }, @@ -420,6 +448,7 @@ "@octokit/rest": "catalog:", "@openauthjs/openauth": "catalog:", "@opencode-ai/script": "workspace:*", + "@opencode-ai/ui": "workspace:*", "@openrouter/ai-sdk-provider": "2.8.1", "@opentelemetry/api": "1.9.0", "@opentelemetry/context-async-hooks": "2.6.1", @@ -458,6 +487,7 @@ "gray-matter": "4.0.3", "hono": "catalog:", "hono-openapi": "catalog:", + "htmlparser2": "8.0.2", "iconv-lite": "0.7.2", "ignore": "7.0.5", "immer": "11.1.4", @@ -548,9 +578,9 @@ "typescript": "catalog:", }, "peerDependencies": { - "@opentui/core": ">=0.2.6", - "@opentui/keymap": ">=0.2.6", - "@opentui/solid": ">=0.2.6", + "@opentui/core": ">=0.2.10", + "@opentui/keymap": ">=0.2.10", + "@opentui/solid": ">=0.2.10", }, "optionalPeers": [ "@opentui/core", @@ -689,6 +719,7 @@ "overrides": { "@effect/platform-node-shared": "4.0.0-beta.46", "@opentui/core": "catalog:", + "@opentui/keymap": "catalog:", "@opentui/solid": "catalog:", "@types/bun": "catalog:", "@types/node": "catalog:", @@ -707,17 +738,17 @@ }, "catalog": { "@cloudflare/workers-types": "4.20251008.0", - "@effect/opentelemetry": "4.0.0-beta.57", - "@effect/platform-node": "4.0.0-beta.57", + "@effect/opentelemetry": "4.0.0-beta.65", + "@effect/platform-node": "4.0.0-beta.65", "@hono/zod-validator": "0.4.2", "@kobalte/core": "0.13.11", "@lydell/node-pty": "1.2.0-beta.10", "@npmcli/arborist": "9.4.0", "@octokit/rest": "22.0.0", "@openauthjs/openauth": "0.0.0-20250322224806", - "@opentui/core": "0.2.6", - "@opentui/keymap": "0.2.6", - "@opentui/solid": "0.2.6", + "@opentui/core": "0.2.10", + "@opentui/keymap": "0.2.10", + "@opentui/solid": "0.2.10", "@pierre/diffs": "1.1.22", "@playwright/test": "1.59.1", "@solid-primitives/storage": "4.3.3", @@ -739,7 +770,7 @@ "dompurify": "3.4.2", "drizzle-kit": "1.0.0-beta.19-d95b7a4", "drizzle-orm": "1.0.0-beta.19-d95b7a4", - "effect": "4.0.0-beta.59", + "effect": "4.0.0-beta.65", "fuzzysort": "3.1.0", "hono": "4.12.12", "hono-openapi": "1.1.2", @@ -1098,9 +1129,9 @@ "@effect/language-service": ["@effect/language-service@0.84.2", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-l04qNxpiA8rY5yXWckRPJ7Mk5MNerXuNymSFf+IdflfI5i8jgL1bpBNLuP6ijg7wgjdHc/KmTnCj2kT0SCntuA=="], - "@effect/opentelemetry": ["@effect/opentelemetry@4.0.0-beta.57", "", { "peerDependencies": { "@opentelemetry/api": "^1.9", "@opentelemetry/resources": "^2.0.0", "@opentelemetry/sdk-logs": ">=0.203.0 <0.300.0", "@opentelemetry/sdk-metrics": "^2.0.0", "@opentelemetry/sdk-trace-base": "^2.0.0", "@opentelemetry/sdk-trace-node": "^2.0.0", "@opentelemetry/sdk-trace-web": "^2.0.0", "@opentelemetry/semantic-conventions": "^1.33.0", "effect": "^4.0.0-beta.57" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/resources", "@opentelemetry/sdk-logs", "@opentelemetry/sdk-metrics", "@opentelemetry/sdk-trace-base", "@opentelemetry/sdk-trace-node", "@opentelemetry/sdk-trace-web"] }, "sha512-gdjZPEP0QQg4qmI1vd+443kheeQZKytrjJIzCJncy6ZEpyk/SfrqeStLqLXdTRcms3IB0ls0vOV7KNq7YmBRVA=="], + "@effect/opentelemetry": ["@effect/opentelemetry@4.0.0-beta.65", "", { "peerDependencies": { "@opentelemetry/api": "^1.9", "@opentelemetry/api-logs": ">=0.203.0 <0.300.0", "@opentelemetry/resources": "^2.0.0", "@opentelemetry/sdk-logs": ">=0.203.0 <0.300.0", "@opentelemetry/sdk-metrics": "^2.0.0", "@opentelemetry/sdk-trace-base": "^2.0.0", "@opentelemetry/sdk-trace-node": "^2.0.0", "@opentelemetry/sdk-trace-web": "^2.0.0", "@opentelemetry/semantic-conventions": "^1.33.0", "effect": "^4.0.0-beta.65" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/api-logs", "@opentelemetry/resources", "@opentelemetry/sdk-logs", "@opentelemetry/sdk-metrics", "@opentelemetry/sdk-trace-base", "@opentelemetry/sdk-trace-node", "@opentelemetry/sdk-trace-web"] }, "sha512-0CD2fSsXrDM7FP2WFkbGJO1DwMqWR3UKHh6oBDXPHAPA+RsJSKoh3pLQsbQfldLuKnhOy87Bv0v9r9IdrIHCQw=="], - "@effect/platform-node": ["@effect/platform-node@4.0.0-beta.57", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-beta.57", "mime": "^4.1.0", "undici": "^8.0.2" }, "peerDependencies": { "effect": "^4.0.0-beta.57", "ioredis": "^5.7.0" } }, "sha512-la0xxPSAYOsY0d+uVxEBxok3jYB31iPQmIaZZRUj2SNWqcGGHJc6KorKtI8guqSLuv9FGZ255kBWXRbG6hMeeg=="], + "@effect/platform-node": ["@effect/platform-node@4.0.0-beta.65", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-beta.65", "mime": "^4.1.0", "undici": "^8.0.2" }, "peerDependencies": { "effect": "^4.0.0-beta.65", "ioredis": "^5.7.0" } }, "sha512-QQy3KRcMwP0TngQdfQGl2u1zp03B7k7DuF5SNS8aZhD0dDBpKZpCwFad1ODY5qdY3ycPgMwBwKRRK7y/aw0C9w=="], "@effect/platform-node-shared": ["@effect/platform-node-shared@4.0.0-beta.46", "", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.19.0" }, "peerDependencies": { "effect": "^4.0.0-beta.46" } }, "sha512-Yzci82XbZ1W3tuiownsJawrJZTGeTrTZKLD0uxdBWCBzlVyqDwoSwRwO5qh33DurJj9B7iS8MDf14fpGRBPNGQ=="], @@ -1560,23 +1591,23 @@ "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="], - "@opentui/core": ["@opentui/core@0.2.6", "", { "dependencies": { "bun-ffi-structs": "0.2.2", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.2.6", "@opentui/core-darwin-x64": "0.2.6", "@opentui/core-linux-arm64": "0.2.6", "@opentui/core-linux-x64": "0.2.6", "@opentui/core-win32-arm64": "0.2.6", "@opentui/core-win32-x64": "0.2.6" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-dBpMaWVM7wtW2/2TlGPrkPjg6gOL3MVU/5XXk+U1LDJB8L4q4NeYWVdzfAVNcEvgmuuCy/cVqdY2D4ei+e7MMg=="], + "@opentui/core": ["@opentui/core@0.2.10", "", { "dependencies": { "bun-ffi-structs": "0.2.2", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.2.10", "@opentui/core-darwin-x64": "0.2.10", "@opentui/core-linux-arm64": "0.2.10", "@opentui/core-linux-x64": "0.2.10", "@opentui/core-win32-arm64": "0.2.10", "@opentui/core-win32-x64": "0.2.10" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-oviCtx0jYjc7F8X2b8+0IkQLg6WH47Nwl6CFeZo5dU0k6OpSbTbi07ZleObaiECAp+S1YLhAtVdgzHU7hBZlaw=="], - "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.2.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-hR5nsxNj+059utzenTCF0kealUlibON6fLuebFUCGM/5kJnqa+shIh0XbUDFm0+F47vqVUgZufBdUuieQZIbvQ=="], + "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.2.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-+lbDDj42Og+UtTZEwlHhGXichmOlkxSqn0J+Jqjat5/Tt5oZykj1NZjFIQ7ZSz4Miz7EmZwgYKE2CyOmmm9MoQ=="], - "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.2.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-pJ/bH4WC/mbBaakM1YdH6TVo67jhy0KPd61bCz97w0I/PJGr8fmNKvhmMt/AwyFgOQi3FYZiEKLMpGdvUcSsrQ=="], + "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.2.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-5iAoA0aqMWWAQ93nh8Bb0ipwt9h+tvEFc88+YO9St43uUJ+XrXcmMj3T8wtl6dSu/SN0UoDWNaUMHUmtykiPtg=="], - "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.2.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Pnd3kOxig8ii+/IqYheOPEgferylsQA0L6tKBnHQ9jRlCJOcu0Rv65Jepueh212vevdV9DzPURJnhejG06J6g=="], + "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.2.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-EnrkxgH5K76Oi/Br1UHPZblXG5P60snmtySfnxuVaeECNZrbTkV6BV/A0WoBeWshJweGbx1D+eTF+sEEjQCi8w=="], - "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.2.6", "", { "os": "linux", "cpu": "x64" }, "sha512-458Mx9tBzEPzfft8cSt5ZaIpEepoxBXBOL6AUVmDTKWaZ3uouraPcEKraGAyvOTDQp2XDI3R8c/2GdaR77FaUQ=="], + "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.2.10", "", { "os": "linux", "cpu": "x64" }, "sha512-fI+r3kCPqIxsWwPVGpKUQy4zHK8y+jkDRCwa3UbaUy48RQ44jMuf2RhVhmi4xmCvSc8UPJBbYsw1tLuh9kmXjg=="], - "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.2.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-BDUrdrT1RCcVnQoHJmUut4y811jDBAEtc6GJFB4Gs265Be8SrTjVCus6p2fSQ7j9sZQ1OcjO+5+4NkheSZICDQ=="], + "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.2.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-8F4z2hIRgkVWcr6CMVeJ9N4+1rmURPt2Pq2GBPko8ch6rxHR+a//KD1MfphyuLTHBS1tJ4vfZSWSoiaESImtrA=="], - "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.2.6", "", { "os": "win32", "cpu": "x64" }, "sha512-SUYAzRJ9TSoD2Qt8kn6FJz6dbTrFEPVig5mScB4zFGgGQO/Bbod2/Q31vLS/IQrX+FDb67WaErD+kuMCnMPPLA=="], + "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.2.10", "", { "os": "win32", "cpu": "x64" }, "sha512-Ki+qNBlIFW5K2wcG/RHrlPp7yEQKXeiNX3mlje25iwX62Ac5w391HBpOmUjbPoq20McPyDRnhbLfbXQSPtickg=="], - "@opentui/keymap": ["@opentui/keymap@0.2.6", "", { "dependencies": { "@opentui/core": "0.2.6" }, "peerDependencies": { "@opentui/react": "0.2.6", "@opentui/solid": "0.2.6", "react": ">=19.2.0", "solid-js": "1.9.12" }, "optionalPeers": ["@opentui/react", "@opentui/solid", "react", "solid-js"] }, "sha512-+6OYuedrFCKVo4ryGFNwws++2VOmPcXU3PwpY0mP47gYQY2nvQ+etWIs2Y7r5eMIqUfxVCldkKsrzcEcA4tb/A=="], + "@opentui/keymap": ["@opentui/keymap@0.2.10", "", { "dependencies": { "@opentui/core": "0.2.10" }, "peerDependencies": { "@opentui/react": "0.2.10", "@opentui/solid": "0.2.10", "react": ">=19.2.0", "solid-js": "1.9.12" }, "optionalPeers": ["@opentui/react", "@opentui/solid", "react", "solid-js"] }, "sha512-80fU3Lr/98sNIpVYd8PApAeQw8A8D9BemyOGi6jGvTQCl0rxKgvaVBviDRGKxl1INTVjZy9By8UPncc2KJOuWQ=="], - "@opentui/solid": ["@opentui/solid@0.2.6", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.2.6", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-2y225WlOGi/fCaajkxBmLyVW8Cr+OmhowHdvrYcz5w2kBD15sKbJLIYu1G9DxceirT1uIyasGy2TGzRRcVkTDg=="], + "@opentui/solid": ["@opentui/solid@0.2.10", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.2.10", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-+4/MB90yIQiPwg8Y4wY092yva9BvRTsJeeeEO3e2H7P8k8zxYk4G9bzuhqYLxA9mTVQ+zVDlrmFoPQhT7vpIRw=="], "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="], @@ -2890,7 +2921,7 @@ "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], - "effect": ["effect@4.0.0-beta.59", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.6.0", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.9", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^13.0.0", "yaml": "^2.8.3" } }, "sha512-xyUDLeHSe8d6lWGOvR6Fgn2HL6gYeTZ/S4Jzk9uc4ZUxMPPsNZlNXrvk0C7/utQFzeX7uAWcVnG2BjbA0SRoAA=="], + "effect": ["effect@4.0.0-beta.65", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.6.0", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.9", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^13.0.0", "yaml": "^2.8.3" } }, "sha512-QYKvQPAj3CmtsvWkHQww15wX4KG2gNsszDWEcOO5sZCMknp66u6Si/Opmt3wwWCwsyvRmDAdIg+JIz5qzbbFIw=="], "electron-to-chromium": ["electron-to-chromium@1.5.340", "", {}, "sha512-908qahOGocRMinT2nM3ajCEM99H4iPdv84eagPP3FfZy/1ZGeOy2CZYzjhms81ckOPCXPlW7LkY4XpxD8r1DrA=="], @@ -3194,7 +3225,7 @@ "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], - "htmlparser2": ["htmlparser2@10.1.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "entities": "^7.0.1" } }, "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ=="], + "htmlparser2": ["htmlparser2@8.0.2", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1", "entities": "^4.4.0" } }, "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA=="], "http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="], @@ -4558,8 +4589,6 @@ "@antfu/install-pkg/package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="], - "@anthropic-ai/sdk/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - "@aws-crypto/sha1-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], "@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], @@ -4622,8 +4651,6 @@ "@kilocode/kilo-indexing/glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="], - "@manypkg/find-root/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - "@manypkg/find-root/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], "@manypkg/find-root/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], @@ -4678,6 +4705,8 @@ "@openauthjs/openauth/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="], + "@opencode-ai/core/@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.993.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.993.0", "@aws-sdk/core": "^3.973.11", "@aws-sdk/credential-provider-cognito-identity": "^3.972.3", "@aws-sdk/credential-provider-env": "^3.972.9", "@aws-sdk/credential-provider-http": "^3.972.11", "@aws-sdk/credential-provider-ini": "^3.972.9", "@aws-sdk/credential-provider-login": "^3.972.9", "@aws-sdk/credential-provider-node": "^3.972.10", "@aws-sdk/credential-provider-process": "^3.972.9", "@aws-sdk/credential-provider-sso": "^3.972.9", "@aws-sdk/credential-provider-web-identity": "^3.972.9", "@aws-sdk/nested-clients": "3.993.0", "@aws-sdk/types": "^3.973.1", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.23.2", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-1M/nukgPSLqe9krzOKHnE8OylUaKAiokAV3xRLdeExVHcRE7WG5uzCTKWTj1imKvPjDqXq/FWhlbbdWIn7xIwA=="], + "@opencode-ai/storybook/@storybook/addon-a11y": ["@storybook/addon-a11y@10.3.5", "", { "dependencies": { "@storybook/global": "^5.0.0", "axe-core": "^4.2.0" }, "peerDependencies": { "storybook": "^10.3.5" } }, "sha512-5k6lpgfIeLxvNhE8v3wEzdiu73ONKjF4gmH1AHvfqYd8kIVzQJai0KCDxgvqNncXHQhIWkaf1fg6+9hKaYJyaw=="], "@opencode-ai/storybook/@storybook/addon-docs": ["@storybook/addon-docs@10.3.5", "", { "dependencies": { "@mdx-js/react": "^3.0.0", "@storybook/csf-plugin": "10.3.5", "@storybook/icons": "^2.0.1", "@storybook/react-dom-shim": "10.3.5", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^10.3.5" } }, "sha512-WuHbxia/o5TX4Rg/IFD0641K5qId/Nk0dxhmAUNoFs5L0+yfZUwh65XOBbzXqrkYmYmcVID4v7cgDRmzstQNkA=="], @@ -4764,30 +4793,6 @@ "@textlint/linter-formatter/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "@types/cacache/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - - "@types/cross-spawn/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - - "@types/mssql/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - - "@types/node-fetch/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - - "@types/npm-registry-fetch/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - - "@types/npmcli__arborist/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - - "@types/npmlog/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - - "@types/pacote/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - - "@types/qrcode/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - - "@types/readable-stream/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - - "@types/ssri/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - - "@types/ws/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - "@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="], "@vitest/expect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], @@ -4826,8 +4831,6 @@ "ai-gateway-provider/@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@2.5.1", "", { "peerDependencies": { "ai": "^6.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-r1fJL1Cb3gQDa2MpWH/sfx1BsEW0uzlRriJM6eihaKqbtKDmZoBisF32VcVaQYassighX7NGCkF68EsrZA43uQ=="], - "apache-arrow/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - "archiver-utils/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], "archiver-utils/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], @@ -4862,8 +4865,6 @@ "buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], - "bun-types/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - "c12/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], "c12/dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="], @@ -4876,6 +4877,8 @@ "chalk-template/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "cheerio/htmlparser2": ["htmlparser2@10.1.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "entities": "^7.0.1" } }, "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ=="], + "cheerio/undici": ["undici@7.25.0", "", {}, "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ=="], "clean-stack/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], @@ -4956,6 +4959,8 @@ "gray-matter/js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], + "htmlparser2/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], "isomorphic-git/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], @@ -5044,10 +5049,6 @@ "openid-client/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], - "opentui-spinner/@opentui/core": ["@opentui/core@0.2.2", "", { "dependencies": { "bun-ffi-structs": "0.2.2", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.2.2", "@opentui/core-darwin-x64": "0.2.2", "@opentui/core-linux-arm64": "0.2.2", "@opentui/core-linux-x64": "0.2.2", "@opentui/core-win32-arm64": "0.2.2", "@opentui/core-win32-x64": "0.2.2" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-wxg1CD58SVrowu+WgbhZNi3UP/wWxPio2Kj2IeTjomoIE+6EXLxR8eCCxHYVuQUd9E4fknrKkY5HmiSsp6oPow=="], - - "opentui-spinner/@opentui/solid": ["@opentui/solid@0.2.2", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.2.2", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-ZBVfCoVAhcUGQWPAWOTdzuVldMaRkuPpCu4U1VZCqmIw9DtbCuiVr0WnDocDxKhJLbTu8bl3qEWtVCf6lTSi3w=="], - "ora/cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], "ora/log-symbols": ["log-symbols@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" } }, "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw=="], @@ -5078,8 +5079,6 @@ "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], - "protobufjs/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - "proxy-addr/ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], "qrcode/yargs": ["yargs@15.4.1", "", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="], @@ -5150,8 +5149,6 @@ "tar-fs/tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], - "tedious/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - "tedious/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], "tedious/sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="], @@ -5234,8 +5231,6 @@ "@morphllm/morphsdk/ai/@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.95", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZmUNNbZl3V42xwQzPaNUi+s8eqR2lnrxf0bvB6YbLXpLjHYv0k2Y78t12cNOfY0bxGeuVVTLyk856uLuQIuXEQ=="], - "@morphllm/morphsdk/openai/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - "@morphllm/morphsdk/openai/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="], @@ -5270,6 +5265,10 @@ "@octokit/rest/@octokit/core/before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="], + "@opencode-ai/core/@aws-sdk/credential-providers/@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.993.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.11", "@aws-sdk/credential-provider-node": "^3.972.10", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", "@aws-sdk/middleware-user-agent": "^3.972.11", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.993.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", "@aws-sdk/util-user-agent-node": "^3.972.9", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.23.2", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.16", "@smithy/middleware-retry": "^4.4.33", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.10", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.32", "@smithy/util-defaults-mode-node": "^4.2.35", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7Ne3Yk/bgQPVebAkv7W+RfhiwTRSbfER9BtbhOa2w/+dIr902LrJf6vrZlxiqaJbGj2ALx8M+ZK1YIHVxSwu9A=="], + + "@opencode-ai/core/@aws-sdk/credential-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.993.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.11", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", "@aws-sdk/middleware-user-agent": "^3.972.11", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.993.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", "@aws-sdk/util-user-agent-node": "^3.972.9", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.23.2", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.16", "@smithy/middleware-retry": "^4.4.33", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.10", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.32", "@smithy/util-defaults-mode-node": "^4.2.35", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-iOq86f2H67924kQUIPOAvlmMaOAvOLoDOIb66I2YqSUpMYB6ufiuJW3RlREgskxv86S5qKzMnfy/X6CqMjK6XQ=="], + "@opencode-ai/storybook/@storybook/addon-docs/@storybook/csf-plugin": ["@storybook/csf-plugin@10.3.5", "", { "dependencies": { "unplugin": "^2.3.5" }, "peerDependencies": { "esbuild": "*", "rollup": "*", "storybook": "^10.3.5", "vite": "*", "webpack": "*" }, "optionalPeers": ["esbuild", "rollup", "vite", "webpack"] }, "sha512-qlEzNKxOjq86pvrbuMwiGD/bylnsXk1dg7ve0j77YFjEEchqtl7qTlrXvFdNaLA89GhW6D/EV6eOCu/eobPDgw=="], "@opencode-ai/storybook/@storybook/addon-docs/@storybook/react-dom-shim": ["@storybook/react-dom-shim@10.3.5", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "storybook": "^10.3.5" } }, "sha512-Gw8R7XZm0zSUH0XAuxlQJhmizsLzyD6x00KOlP6l7oW9eQHXGfxg3seNDG3WrSAcW07iP1/P422kuiriQlOv7g=="], @@ -5528,8 +5527,6 @@ "jszip/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], - "kilo-code/openai/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - "lazystream/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], "lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], @@ -5560,20 +5557,6 @@ "opencontrol/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], - "opentui-spinner/@opentui/core/@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.2.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-tY5n3ZRQx+b0kyhQJJLsyJMeZ+0w4FV37YZc/Qqv3qvOqE9kZPw/7adR77FYwWDm/7fax94mLMrR8Y5bKUkDmw=="], - - "opentui-spinner/@opentui/core/@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.2.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-W/R7OnqY30FXcTG0tiP2JkQFmgtYbIte5afQ5PC12TliRoee1RqG3iCG6kY1jxW+3Vg6jge88uiSjUEDpeV2gA=="], - - "opentui-spinner/@opentui/core/@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-1pzTYFEZauYuw6AGycw2TYGtAlZVGjuUtSdxH1fP51kBPS3oVWduUY2j7GKREz3SU5NulvO2Wc6HWsm3feMqwQ=="], - - "opentui-spinner/@opentui/core/@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-ucVwUtUYeOYGVFPBLbPoxzbrPdhD0PDyKNQ2X4n1AJ9jlQX4gqBZRcXMEF8hiXDjFxsikZwef7De0ciCcWvAMg=="], - - "opentui-spinner/@opentui/core/@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.2.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-MPhYdJNdxmC5Bqsq6sis/+VkjRgkEjm+bQ1Tl++NSKLuiTU32Re0ImcZlgHbe+LZtZoGMZHVSgZlkGd3oYXO2g=="], - - "opentui-spinner/@opentui/core/@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.2.2", "", { "os": "win32", "cpu": "x64" }, "sha512-19BroLfn2h0RDYfJS5o96Fc8kYCDhRBcseIXtHIkoKIsKMxx62KiDLo/byVye6rp+yQRRB7Xkd2uWqsbdiWo9w=="], - - "opentui-spinner/@opentui/solid/@babel/core": ["@babel/core@7.28.0", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="], - "ora/log-symbols/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="], "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], @@ -5650,6 +5633,10 @@ "@octokit/rest/@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], + "@opencode-ai/core/@aws-sdk/credential-providers/@aws-sdk/client-cognito-identity/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.993.0", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-endpoints": "^3.2.8", "tslib": "^2.6.2" } }, "sha512-j6vioBeRZ4eHX4SWGvGPpwGg/xSOcK7f1GL0VM+rdf3ZFTIsUEhCFmD78B+5r2PgztcECSzEfvHQX01k8dPQPw=="], + + "@opencode-ai/core/@aws-sdk/credential-providers/@aws-sdk/nested-clients/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.993.0", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-endpoints": "^3.2.8", "tslib": "^2.6.2" } }, "sha512-j6vioBeRZ4eHX4SWGvGPpwGg/xSOcK7f1GL0VM+rdf3ZFTIsUEhCFmD78B+5r2PgztcECSzEfvHQX01k8dPQPw=="], + "@opencode-ai/storybook/storybook/@vitest/expect/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], "@opencode-ai/storybook/storybook/@vitest/expect/chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], @@ -5798,8 +5785,6 @@ "mocha/yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "opentui-spinner/@opentui/solid/@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "pkg-conf/find-up/locate-path/p-locate": ["p-locate@6.0.0", "", { "dependencies": { "p-limit": "^4.0.0" } }, "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw=="], "pkg-up/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="], diff --git a/bunfig.toml b/bunfig.toml index 25923129a8c..6b1e40c73f5 100644 --- a/bunfig.toml +++ b/bunfig.toml @@ -1,8 +1,8 @@ [install] exact = true +# Keep Kilo's longer supply-chain quarantine while allowing packages that must track coordinated releases. minimumReleaseAge = 410520 # seconds (~4.75 days / ~114 hours) -minimumReleaseAgeExcludes = ["mermaid", "@mermaid-js/parser"] +minimumReleaseAgeExcludes = ["mermaid", "@mermaid-js/parser", "@opentui/core", "@opentui/core-darwin-arm64", "@opentui/core-darwin-x64", "@opentui/core-linux-arm64", "@opentui/core-linux-x64", "@opentui/core-win32-arm64", "@opentui/core-win32-x64", "@opentui/keymap", "@opentui/solid"] [test] root = "./do-not-run-tests-from-root" - diff --git a/package.json b/package.json index 98da7e85c55..1488346535c 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "AI-powered development tool", "private": true, "type": "module", - "packageManager": "bun@1.3.14", + "packageManager": "bun@1.3.13", "scripts": { "dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts", "dev:storybook": "bun --cwd packages/storybook storybook", @@ -25,15 +25,15 @@ "packages/sdk/js" ], "catalog": { - "@effect/opentelemetry": "4.0.0-beta.57", - "@effect/platform-node": "4.0.0-beta.57", + "@effect/opentelemetry": "4.0.0-beta.65", + "@effect/platform-node": "4.0.0-beta.65", "@npmcli/arborist": "9.4.0", "@types/bun": "1.3.14", "@types/cross-spawn": "6.0.6", "@octokit/rest": "22.0.0", "@hono/zod-validator": "0.4.2", - "@opentui/core": "0.2.6", - "@opentui/solid": "0.2.6", + "@opentui/core": "0.2.10", + "@opentui/solid": "0.2.10", "ulid": "3.0.1", "@kobalte/core": "0.13.11", "@types/luxon": "3.7.1", @@ -51,7 +51,7 @@ "dompurify": "3.4.2", "drizzle-kit": "1.0.0-beta.19-d95b7a4", "drizzle-orm": "1.0.0-beta.19-d95b7a4", - "effect": "4.0.0-beta.59", + "effect": "4.0.0-beta.65", "ai": "6.0.168", "cross-spawn": "7.0.6", "hono": "4.12.12", @@ -78,7 +78,7 @@ "solid-js": "1.9.12", "vite-plugin-solid": "2.11.10", "@lydell/node-pty": "1.2.0-beta.10", - "@opentui/keymap": "0.2.6" + "@opentui/keymap": "0.2.10" } }, "devDependencies": { @@ -143,7 +143,8 @@ "happy-dom": ">=20.8.9", "@opentui/core": "catalog:", "@opentui/solid": "catalog:", - "solid-js": "catalog:" + "solid-js": "catalog:", + "@opentui/keymap": "catalog:" }, "patchedDependencies": { "@npmcli/agent@4.0.0": "patches/@npmcli%2Fagent@4.0.0.patch", diff --git a/packages/core/package.json b/packages/core/package.json index 2ba968c7496..e88fd53812e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -26,6 +26,7 @@ "@types/semver": "catalog:" }, "dependencies": { + "@kilocode/kilo-gateway": "workspace:*", "@effect/opentelemetry": "catalog:", "@effect/platform-node": "catalog:", "@npmcli/arborist": "9.4.0", @@ -43,7 +44,34 @@ "rotating-file-stream": "3.2.9", "semver": "^7.6.3", "xdg-basedir": "5.1.0", - "zod": "catalog:" + "zod": "catalog:", + "@ai-sdk/alibaba": "1.0.17", + "@ai-sdk/amazon-bedrock": "4.0.96", + "@ai-sdk/anthropic": "3.0.71", + "@ai-sdk/azure": "3.0.49", + "@ai-sdk/cerebras": "2.0.41", + "@ai-sdk/cohere": "3.0.27", + "@ai-sdk/deepinfra": "2.0.41", + "@ai-sdk/gateway": "3.0.104", + "@ai-sdk/google": "3.0.63", + "@ai-sdk/google-vertex": "4.0.112", + "@ai-sdk/groq": "3.0.31", + "@ai-sdk/mistral": "3.0.27", + "@ai-sdk/openai": "3.0.53", + "@ai-sdk/openai-compatible": "2.0.41", + "@ai-sdk/perplexity": "3.0.26", + "@ai-sdk/provider": "3.0.8", + "@ai-sdk/provider-utils": "4.0.23", + "@ai-sdk/togetherai": "2.0.41", + "@ai-sdk/vercel": "2.0.39", + "@ai-sdk/xai": "3.0.82", + "@aws-sdk/credential-providers": "3.993.0", + "@openrouter/ai-sdk-provider": "2.8.1", + "ai-gateway-provider": "3.1.2", + "gitlab-ai-provider": "6.6.0", + "google-auth-library": "10.5.0", + "immer": "11.1.4", + "venice-ai-sdk-provider": "2.0.1" }, "overrides": { "drizzle-orm": "catalog:" diff --git a/packages/core/src/aisdk.ts b/packages/core/src/aisdk.ts new file mode 100644 index 00000000000..5fa2294309c --- /dev/null +++ b/packages/core/src/aisdk.ts @@ -0,0 +1,172 @@ +export * as AISDK from "./aisdk" + +import type { LanguageModelV3 } from "@ai-sdk/provider" +import { Cause, Context, Effect, Layer, Schema } from "effect" +import { ModelV2 } from "./model" +import { PluginV2 } from "./plugin" +import { ProviderV2 } from "./provider" + +type SDK = any + +function wrapSSE(res: Response, ms: number, ctl: AbortController) { + if (typeof ms !== "number" || ms <= 0) return res + if (!res.body) return res + if (!res.headers.get("content-type")?.includes("text/event-stream")) return res + + const reader = res.body.getReader() + const body = new ReadableStream({ + async pull(ctrl) { + const part = await new Promise>>((resolve, reject) => { + const id = setTimeout(() => { + const err = new Error("SSE read timed out") + ctl.abort(err) + void reader.cancel(err) + reject(err) + }, ms) + + reader.read().then( + (part) => { + clearTimeout(id) + resolve(part) + }, + (err) => { + clearTimeout(id) + reject(err) + }, + ) + }) + + if (part.done) { + ctrl.close() + return + } + + ctrl.enqueue(part.value) + }, + async cancel(reason) { + ctl.abort(reason) + await reader.cancel(reason) + }, + }) + + return new Response(body, { + headers: new Headers(res.headers), + status: res.status, + statusText: res.statusText, + }) +} + +function prepareOptions(model: ModelV2.Info, pkg: string) { + const options: Record = { name: model.providerID, ...model.options.aisdk.provider } + if (model.endpoint.type === "aisdk" && model.endpoint.url) options.baseURL = model.endpoint.url + + const customFetch = options.fetch + const chunkTimeout = options.chunkTimeout + delete options.chunkTimeout + options.fetch = async (input: Parameters[0], init?: RequestInit) => { + const opts = { ...(init ?? {}) } + const signals = [ + opts.signal, + typeof chunkTimeout === "number" && chunkTimeout > 0 ? new AbortController() : undefined, + options.timeout !== undefined && options.timeout !== null && options.timeout !== false + ? AbortSignal.timeout(options.timeout) + : undefined, + ].filter((item): item is AbortSignal | AbortController => Boolean(item)) + const chunkAbortCtl = signals.find((item): item is AbortController => item instanceof AbortController) + const abortSignals = signals.map((item) => (item instanceof AbortController ? item.signal : item)) + if (abortSignals.length === 1) opts.signal = abortSignals[0] + if (abortSignals.length > 1) opts.signal = AbortSignal.any(abortSignals) + + if ((pkg === "@ai-sdk/openai" || pkg === "@ai-sdk/azure") && opts.body && opts.method === "POST") { + const body = JSON.parse(opts.body as string) + if (body.store !== true && Array.isArray(body.input)) { + for (const item of body.input) { + if ("id" in item) delete item.id + } + opts.body = JSON.stringify(body) + } + } + + const res = await (typeof customFetch === "function" ? customFetch : fetch)(input, { + ...opts, + timeout: false, + }) + if (!chunkAbortCtl || typeof chunkTimeout !== "number") return res + return wrapSSE(res, chunkTimeout, chunkAbortCtl) + } + + return options +} + +export class InitError extends Schema.TaggedErrorClass()("AISDK.InitError", { + providerID: ProviderV2.ID, + cause: Schema.Defect, +}) {} + +function initError(providerID: ProviderV2.ID) { + return Effect.catchCause((cause) => Effect.fail(new InitError({ providerID, cause: Cause.squash(cause) }))) +} + +export interface Interface { + readonly language: (model: ModelV2.Info) => Effect.Effect +} + +export class Service extends Context.Service()("@opencode/v2/AISDK") {} + +export const layer = Layer.effect( + Service, + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const languages = new Map() + const sdks = new Map() + + return Service.of({ + language: Effect.fn("AISDK.language")(function* (model) { + const key = `${model.providerID}/${model.id}/${model.options.variant ?? "default"}` + const existing = languages.get(key) + if (existing) return existing + if (model.endpoint.type !== "aisdk") + return yield* new InitError({ + providerID: model.providerID, + cause: new Error(`Unsupported endpoint ${model.endpoint.type}`), + }) + + const options = prepareOptions(model, model.endpoint.package) + const sdkKey = JSON.stringify({ + providerID: model.providerID, + endpoint: model.endpoint, + options, + }) + const sdk = + sdks.get(sdkKey) ?? + (yield* plugin + .trigger("aisdk.sdk", { model, package: model.endpoint.package, options }, {}) + .pipe(initError(model.providerID))).sdk + if (!sdk) + return yield* new InitError({ + providerID: model.providerID, + cause: new Error("No AISDK provider plugin returned an SDK"), + }) + sdks.set(sdkKey, sdk) + const result = yield* plugin + .trigger( + "aisdk.language", + { + model, + sdk, + options, + }, + {}, + ) + .pipe(initError(model.providerID)) + const language = yield* Effect.sync(() => result.language ?? sdk.languageModel(model.apiID)).pipe( + initError(model.providerID), + ) + languages.set(key, language) + return language + }), + }) + }), +) + +export const defaultLayer = layer.pipe(Layer.provide(PluginV2.defaultLayer)) diff --git a/packages/opencode/src/v2/auth.ts b/packages/core/src/auth.ts similarity index 85% rename from packages/opencode/src/v2/auth.ts rename to packages/core/src/auth.ts index 9ae5ee5a943..971b2320353 100644 --- a/packages/opencode/src/v2/auth.ts +++ b/packages/core/src/auth.ts @@ -1,9 +1,9 @@ import path from "path" import { Effect, Layer, Option, Schema, Context, SynchronizedRef } from "effect" -import { Identifier } from "@opencode-ai/core/util/identifier" -import { NonNegativeInt, withStatics } from "@opencode-ai/core/schema" -import { Global } from "@opencode-ai/core/global" -import { AppFileSystem } from "@opencode-ai/core/filesystem" +import { Identifier } from "./util/identifier" +import { NonNegativeInt, withStatics } from "./schema" +import { Global } from "./global" +import { AppFileSystem } from "./filesystem" export const OAUTH_DUMMY_KEY = "opencode-oauth-dummy-key" @@ -21,6 +21,7 @@ export class OAuthCredential extends Schema.Class("AuthV2.OAuth refresh: Schema.String, access: Schema.String, expires: NonNegativeInt, + accountId: Schema.optional(Schema.String), // kilocode_change - preserve Kilo organization during v1 migration }) {} export class ApiKeyCredential extends Schema.Class("AuthV2.ApiKeyCredential")({ @@ -106,25 +107,43 @@ export const layer = Layer.effect( const fsys = yield* AppFileSystem.Service const global = yield* Global.Service const file = path.join(global.data, "auth-v2.json") + const legacyFile = path.join(global.data, "auth.json") + + const writeMigrated = Effect.fnUntraced(function* (raw: Record) { + const migrated = migrate(raw) + yield* fsys + .writeJson(file, migrated, 0o600) + .pipe(Effect.mapError((cause) => new AuthFileWriteError({ operation: "migrate", cause }))) + return migrated + }) + + const parseAuthContent = () => { + try { + return JSON.parse(process.env.KILO_AUTH_CONTENT ?? "") + } catch {} + } const load: () => Effect.Effect = Effect.fnUntraced(function* () { if (process.env.KILO_AUTH_CONTENT) { - try { - return JSON.parse(process.env.KILO_AUTH_CONTENT) - } catch {} + const raw = parseAuthContent() + if (raw && typeof raw === "object") { + if ("version" in raw && raw.version === 2) return raw as Writable + return yield* writeMigrated(raw as Record) + } + return { version: 2, accounts: {}, active: {} } } - const raw = yield* fsys.readJson(file).pipe(Effect.orElseSucceed(() => null)) + const legacy = yield* fsys.readJson(legacyFile).pipe(Effect.orElseSucceed(() => null)) + if (legacy && typeof legacy === "object") return yield* writeMigrated(legacy as Record) - if (!raw || typeof raw !== "object") return { version: 2, accounts: {}, active: {} } + const raw = yield* fsys.readJson(file).pipe(Effect.orElseSucceed(() => null)) - if ("version" in raw && raw.version === 2) return raw as Writable + if (raw && typeof raw === "object") { + if ("version" in raw && raw.version === 2) return raw as Writable + return yield* writeMigrated(raw as Record) + } - const migrated = migrate(raw as Record) - yield* fsys - .writeJson(file, migrated, 0o600) - .pipe(Effect.mapError((cause) => new AuthFileWriteError({ operation: "migrate", cause }))) - return migrated + return { version: 2, accounts: {}, active: {} } }) const write = (data: Writable) => diff --git a/packages/core/src/catalog.ts b/packages/core/src/catalog.ts new file mode 100644 index 00000000000..70cdba78563 --- /dev/null +++ b/packages/core/src/catalog.ts @@ -0,0 +1,260 @@ +export * as Catalog from "./catalog" + +import { Context, Effect, HashMap, Layer, Option, Order, pipe, Schema, Array } from "effect" +import { produce, type Draft } from "immer" +import { ModelV2 } from "./model" +import { PluginV2 } from "./plugin" +import { ProviderV2 } from "./provider" +import { Instance } from "./instance" + +type ProviderRecord = { + provider: ProviderV2.Info + models: HashMap.HashMap +} + +export class ProviderNotFoundError extends Schema.TaggedErrorClass()( + "CatalogV2.ProviderNotFound", + { + providerID: ProviderV2.ID, + }, +) {} + +export class ModelNotFoundError extends Schema.TaggedErrorClass()("CatalogV2.ModelNotFound", { + providerID: ProviderV2.ID, + modelID: ModelV2.ID, +}) {} + +export interface Interface { + readonly provider: { + readonly get: (providerID: ProviderV2.ID) => Effect.Effect + readonly update: (providerID: ProviderV2.ID, fn: (provider: Draft) => void) => Effect.Effect + readonly all: () => Effect.Effect + readonly available: () => Effect.Effect + } + readonly model: { + readonly get: ( + providerID: ProviderV2.ID, + modelID: ModelV2.ID, + ) => Effect.Effect + readonly update: ( + providerID: ProviderV2.ID, + modelID: ModelV2.ID, + fn: (model: Draft) => void, + ) => Effect.Effect + readonly all: () => Effect.Effect + readonly available: () => Effect.Effect + readonly default: () => Effect.Effect> + readonly setDefault: ( + providerID: ProviderV2.ID, + modelID: ModelV2.ID, + ) => Effect.Effect + readonly small: (providerID: ProviderV2.ID) => Effect.Effect> + } +} + +export class Service extends Context.Service()("@opencode/v2/Catalog") {} + +export const layer = Layer.effect( + Service, + Effect.gen(function* () { + yield* Instance.Service + let records = HashMap.empty() + let defaultModel: { providerID: ProviderV2.ID; modelID: ModelV2.ID } | undefined + const plugin = yield* PluginV2.Service + + const resolve = (model: ModelV2.Info) => { + const provider = Option.getOrThrow(HashMap.get(records, model.providerID)).provider + const endpoint = + model.endpoint.type === "unknown" + ? provider.endpoint + : model.endpoint.type === "aisdk" && provider.endpoint.type === "aisdk" && !model.endpoint.url + ? { ...model.endpoint, url: provider.endpoint.url } + : model.endpoint + const options = { + headers: { + ...provider.options.headers, + ...model.options.headers, + }, + body: { + ...provider.options.body, + ...model.options.body, + }, + aisdk: { + provider: { + ...provider.options.aisdk.provider, + ...model.options.aisdk.provider, + }, + request: model.options.aisdk.request, + }, + variant: model.options.variant, + } + return new ModelV2.Info({ + ...model, + endpoint, + options, + }) + } + + function* getRecord(providerID: ProviderV2.ID) { + const match = HashMap.get(records, providerID) + if (!match.valueOrUndefined) return yield* new ProviderNotFoundError({ providerID }) + return match.value + } + + const result: Interface = { + provider: { + get: Effect.fn("CatalogV2.provider.get")(function* (providerID) { + const record = yield* getRecord(providerID) + return record.provider + }), + + update: Effect.fnUntraced(function* (providerID, fn) { + const current = Option.getOrUndefined(HashMap.get(records, providerID)) + const provider = produce(current?.provider ?? ProviderV2.Info.empty(providerID), (draft) => { + fn(draft) + if (draft.endpoint.type === "aisdk" && typeof draft.options.aisdk.provider.baseURL === "string") { + draft.endpoint.url = draft.options.aisdk.provider.baseURL + delete draft.options.aisdk.provider.baseURL + } + }) + const updated = yield* plugin.trigger("provider.update", {}, { provider, cancel: false }) + records = HashMap.set(records, providerID, { + provider: updated.provider, + models: current?.models ?? HashMap.empty(), + }) + }), + + all: Effect.fn("CatalogV2.provider.all")(function* () { + return globalThis.Array.from(HashMap.values(records)).map((record) => record.provider) + }), + + available: Effect.fn("CatalogV2.provider.available")(function* () { + return globalThis.Array.from(HashMap.values(records)) + .map((record) => record.provider) + .filter((provider) => provider.enabled) + }), + }, + + model: { + get: Effect.fn("CatalogV2.model.get")(function* (providerID, modelID) { + const record = yield* getRecord(providerID) + const model = Option.getOrUndefined(HashMap.get(record.models, modelID)) + if (!model) return yield* new ModelNotFoundError({ providerID, modelID }) + return resolve(model) + }), + + update: Effect.fnUntraced(function* (providerID, modelID, fn) { + const record = yield* getRecord(providerID) + const model = produce( + HashMap.get(record.models, modelID).pipe(Option.getOrElse(() => ModelV2.Info.empty(providerID, modelID))), + (draft) => { + fn(draft) + if (draft.endpoint.type === "aisdk" && typeof draft.options.aisdk.provider.baseURL === "string") { + draft.endpoint.url = draft.options.aisdk.provider.baseURL + delete draft.options.aisdk.provider.baseURL + } + }, + ) + const updated = yield* plugin.trigger("model.update", {}, { model, cancel: false }) + if (updated.cancel) return + records = HashMap.set(records, providerID, { + provider: record.provider, + models: HashMap.set( + record.models, + modelID, + new ModelV2.Info({ ...updated.model, id: modelID, providerID }), + ), + }) + return + }), + + all: Effect.fn("CatalogV2.model.all")(function* () { + return pipe( + records, + HashMap.toValues, + Array.flatMap((record) => HashMap.toValues(record.models)), + Array.map(resolve), + Array.sortWith((item) => item.time.released.epochMilliseconds, Order.flip(Order.Number)), + ) + }), + + available: Effect.fn("CatalogV2.model.available")(function* () { + return (yield* result.model.all()).filter((model) => { + const record = Option.getOrUndefined(HashMap.get(records, model.providerID)) + return record?.provider.enabled !== false && model.enabled + }) + }), + + default: Effect.fn("CatalogV2.model.default")(function* () { + if (defaultModel) { + const model = yield* result.model.get(defaultModel.providerID, defaultModel.modelID).pipe(Effect.option) + if (Option.isSome(model) && model.value.enabled) return model + } + + return pipe( + yield* result.model.available(), + Array.sortWith((item) => item.time.released.epochMilliseconds, Order.flip(Order.Number)), + Array.head, + ) + }), + + setDefault: Effect.fn("CatalogV2.model.setDefault")(function* (providerID, modelID) { + yield* result.model.get(providerID, modelID) + defaultModel = { providerID, modelID } + }), + + small: Effect.fn("CatalogV2.model.small")(function* (providerID) { + const record = Option.getOrUndefined(HashMap.get(records, providerID)) + if (!record) return Option.none() + + if (providerID === ProviderV2.ID.opencode) { + const gpt5Nano = Option.getOrUndefined(HashMap.get(record.models, ModelV2.ID.make("gpt-5-nano"))) + if (gpt5Nano?.enabled && gpt5Nano.status === "active") return Option.some(resolve(gpt5Nano)) + } + + const candidates = pipe( + HashMap.toValues(record.models), + Array.filter( + (model) => + model.providerID === providerID && + model.enabled && + model.status === "active" && + model.capabilities.input.some((item) => item.startsWith("text")) && + model.capabilities.output.some((item) => item.startsWith("text")), + ), + Array.map((model) => ({ + model, + cost: model.cost[0] ? model.cost[0].input + model.cost[0].output : 999, + age: (Date.now() - model.time.released.epochMilliseconds) / (1000 * 60 * 60 * 24 * 30), + small: SMALL_MODEL_RE.test(`${model.id} ${model.family ?? ""} ${model.name}`.toLowerCase()), + })), + Array.filter((item) => item.cost > 0 && item.age <= 18), + ) + + const pick = (items: typeof candidates) => { + const maxCost = Math.max(...items.map((item) => item.cost), 0.01) + const maxAge = Math.max(...items.map((item) => item.age), 0.01) + return pipe( + items, + Array.sortWith((item) => (item.cost / maxCost) * 0.8 + (item.age / maxAge) * 0.2, Order.Number), + Array.map((item) => resolve(item.model)), + Array.head, + ) + } + + return pipe( + candidates, + Array.filter((item) => item.small), + (items) => (items.length > 0 ? pick(items) : pick(candidates)), + ) + }), + }, + } + + return Service.of(result) + }), +) + +const SMALL_MODEL_RE = /\b(nano|flash|lite|mini|haiku|small|fast)\b/ + +export const defaultLayer = layer.pipe(Layer.provide(PluginV2.defaultLayer)) diff --git a/packages/core/src/effect-zod.ts b/packages/core/src/effect-zod.ts index 1c88712d7d1..68395379e3f 100644 --- a/packages/core/src/effect-zod.ts +++ b/packages/core/src/effect-zod.ts @@ -228,7 +228,7 @@ function issueMessage(issue: any): string | undefined { } function body(ast: SchemaAST.AST): z.ZodTypeAny { - if (SchemaAST.isOptional(ast)) return opt(ast) + if (SchemaAST.isOptional(ast) && ast._tag === "Union") return opt(ast) switch (ast._tag) { case "String": diff --git a/packages/core/src/flag/flag.ts b/packages/core/src/flag/flag.ts index 95c8e01634e..ab0647c8af3 100644 --- a/packages/core/src/flag/flag.ts +++ b/packages/core/src/flag/flag.ts @@ -11,8 +11,6 @@ function falsy(key: string) { return value === "false" || value === "0" } -// Channels where new experiments default to ON (unstable / internal users). -// Stable channels (`prod`, `latest`) stay opt-in. const UNSTABLE_CHANNELS = new Set(["dev", "beta", "local"]) function unstableDefault(key: string) { return truthy(key) || (!falsy(key) && UNSTABLE_CHANNELS.has(InstallationChannel)) @@ -54,16 +52,13 @@ export const Flag = { KILO_DISABLE_CLAUDE_CODE, KILO_DISABLE_CLAUDE_CODE_PROMPT: KILO_DISABLE_CLAUDE_CODE || truthy("KILO_DISABLE_CLAUDE_CODE_PROMPT"), KILO_DISABLE_CLAUDE_CODE_SKILLS, - KILO_DISABLE_EXTERNAL_SKILLS: truthy("KILO_DISABLE_EXTERNAL_SKILLS"), // kilocode_change - // Default-on for dev/beta/local; opt-in for stable. Set - // KILO_EXPERIMENTAL_CUSTOMIZE_SKILL=false to force off, =true to force on. + KILO_DISABLE_EXTERNAL_SKILLS: truthy("KILO_DISABLE_EXTERNAL_SKILLS"), KILO_EXPERIMENTAL_CUSTOMIZE_SKILL: unstableDefault("KILO_EXPERIMENTAL_CUSTOMIZE_SKILL"), KILO_FAKE_VCS: process.env["KILO_FAKE_VCS"], KILO_SERVER_PASSWORD: process.env["KILO_SERVER_PASSWORD"], KILO_SERVER_USERNAME: process.env["KILO_SERVER_USERNAME"], KILO_ENABLE_QUESTION_TOOL: truthy("KILO_ENABLE_QUESTION_TOOL"), - // Experimental KILO_EXPERIMENTAL, KILO_EXPERIMENTAL_FILEWATCHER: Config.boolean("KILO_EXPERIMENTAL_FILEWATCHER").pipe(Config.withDefault(false)), KILO_EXPERIMENTAL_DISABLE_FILEWATCHER: Config.boolean("KILO_EXPERIMENTAL_DISABLE_FILEWATCHER").pipe( @@ -93,9 +88,8 @@ export const Flag = { KILO_WORKSPACE_ID: process.env["KILO_WORKSPACE_ID"], KILO_EXPERIMENTAL_WORKSPACES: KILO_EXPERIMENTAL || truthy("KILO_EXPERIMENTAL_WORKSPACES"), KILO_EXPERIMENTAL_EVENT_SYSTEM: KILO_EXPERIMENTAL || truthy("KILO_EXPERIMENTAL_EVENT_SYSTEM"), + KILO_EXPERIMENTAL_SESSION_SWITCHING: KILO_EXPERIMENTAL || truthy("KILO_EXPERIMENTAL_SESSION_SWITCHING"), - // Evaluated at access time (not module load) because tests, the CLI, and - // external tooling set these env vars at runtime. get KILO_DISABLE_PROJECT_CONFIG() { return truthy("KILO_DISABLE_PROJECT_CONFIG") }, @@ -114,9 +108,7 @@ export const Flag = { get KILO_CLIENT() { return process.env["KILO_CLIENT"] ?? "cli" }, - // kilocode_change start get KILO_SESSION_RETRY_LIMIT() { return number("KILO_SESSION_RETRY_LIMIT") }, - // kilocode_change end } diff --git a/packages/opencode/src/provider/sdk/copilot/README.md b/packages/core/src/github-copilot/README.md similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/README.md rename to packages/core/src/github-copilot/README.md diff --git a/packages/opencode/src/provider/sdk/copilot/chat/convert-to-openai-compatible-chat-messages.ts b/packages/core/src/github-copilot/chat/convert-to-openai-compatible-chat-messages.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/chat/convert-to-openai-compatible-chat-messages.ts rename to packages/core/src/github-copilot/chat/convert-to-openai-compatible-chat-messages.ts diff --git a/packages/opencode/src/provider/sdk/copilot/chat/get-response-metadata.ts b/packages/core/src/github-copilot/chat/get-response-metadata.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/chat/get-response-metadata.ts rename to packages/core/src/github-copilot/chat/get-response-metadata.ts diff --git a/packages/opencode/src/provider/sdk/copilot/chat/map-openai-compatible-finish-reason.ts b/packages/core/src/github-copilot/chat/map-openai-compatible-finish-reason.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/chat/map-openai-compatible-finish-reason.ts rename to packages/core/src/github-copilot/chat/map-openai-compatible-finish-reason.ts diff --git a/packages/opencode/src/provider/sdk/copilot/chat/openai-compatible-api-types.ts b/packages/core/src/github-copilot/chat/openai-compatible-api-types.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/chat/openai-compatible-api-types.ts rename to packages/core/src/github-copilot/chat/openai-compatible-api-types.ts diff --git a/packages/opencode/src/provider/sdk/copilot/chat/openai-compatible-chat-language-model.ts b/packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/chat/openai-compatible-chat-language-model.ts rename to packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts diff --git a/packages/opencode/src/provider/sdk/copilot/chat/openai-compatible-chat-options.ts b/packages/core/src/github-copilot/chat/openai-compatible-chat-options.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/chat/openai-compatible-chat-options.ts rename to packages/core/src/github-copilot/chat/openai-compatible-chat-options.ts diff --git a/packages/opencode/src/provider/sdk/copilot/chat/openai-compatible-metadata-extractor.ts b/packages/core/src/github-copilot/chat/openai-compatible-metadata-extractor.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/chat/openai-compatible-metadata-extractor.ts rename to packages/core/src/github-copilot/chat/openai-compatible-metadata-extractor.ts diff --git a/packages/opencode/src/provider/sdk/copilot/chat/openai-compatible-prepare-tools.ts b/packages/core/src/github-copilot/chat/openai-compatible-prepare-tools.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/chat/openai-compatible-prepare-tools.ts rename to packages/core/src/github-copilot/chat/openai-compatible-prepare-tools.ts diff --git a/packages/opencode/src/provider/sdk/copilot/copilot-provider.ts b/packages/core/src/github-copilot/copilot-provider.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/copilot-provider.ts rename to packages/core/src/github-copilot/copilot-provider.ts diff --git a/packages/opencode/src/provider/sdk/copilot/openai-compatible-error.ts b/packages/core/src/github-copilot/openai-compatible-error.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/openai-compatible-error.ts rename to packages/core/src/github-copilot/openai-compatible-error.ts diff --git a/packages/opencode/src/provider/sdk/copilot/responses/convert-to-openai-responses-input.ts b/packages/core/src/github-copilot/responses/convert-to-openai-responses-input.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/responses/convert-to-openai-responses-input.ts rename to packages/core/src/github-copilot/responses/convert-to-openai-responses-input.ts diff --git a/packages/opencode/src/provider/sdk/copilot/responses/map-openai-responses-finish-reason.ts b/packages/core/src/github-copilot/responses/map-openai-responses-finish-reason.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/responses/map-openai-responses-finish-reason.ts rename to packages/core/src/github-copilot/responses/map-openai-responses-finish-reason.ts diff --git a/packages/opencode/src/provider/sdk/copilot/responses/openai-config.ts b/packages/core/src/github-copilot/responses/openai-config.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/responses/openai-config.ts rename to packages/core/src/github-copilot/responses/openai-config.ts diff --git a/packages/opencode/src/provider/sdk/copilot/responses/openai-error.ts b/packages/core/src/github-copilot/responses/openai-error.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/responses/openai-error.ts rename to packages/core/src/github-copilot/responses/openai-error.ts diff --git a/packages/opencode/src/provider/sdk/copilot/responses/openai-responses-api-types.ts b/packages/core/src/github-copilot/responses/openai-responses-api-types.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/responses/openai-responses-api-types.ts rename to packages/core/src/github-copilot/responses/openai-responses-api-types.ts diff --git a/packages/opencode/src/provider/sdk/copilot/responses/openai-responses-language-model.ts b/packages/core/src/github-copilot/responses/openai-responses-language-model.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/responses/openai-responses-language-model.ts rename to packages/core/src/github-copilot/responses/openai-responses-language-model.ts diff --git a/packages/opencode/src/provider/sdk/copilot/responses/openai-responses-prepare-tools.ts b/packages/core/src/github-copilot/responses/openai-responses-prepare-tools.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/responses/openai-responses-prepare-tools.ts rename to packages/core/src/github-copilot/responses/openai-responses-prepare-tools.ts diff --git a/packages/opencode/src/provider/sdk/copilot/responses/openai-responses-settings.ts b/packages/core/src/github-copilot/responses/openai-responses-settings.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/responses/openai-responses-settings.ts rename to packages/core/src/github-copilot/responses/openai-responses-settings.ts diff --git a/packages/opencode/src/provider/sdk/copilot/responses/tool/code-interpreter.ts b/packages/core/src/github-copilot/responses/tool/code-interpreter.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/responses/tool/code-interpreter.ts rename to packages/core/src/github-copilot/responses/tool/code-interpreter.ts diff --git a/packages/opencode/src/provider/sdk/copilot/responses/tool/file-search.ts b/packages/core/src/github-copilot/responses/tool/file-search.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/responses/tool/file-search.ts rename to packages/core/src/github-copilot/responses/tool/file-search.ts diff --git a/packages/opencode/src/provider/sdk/copilot/responses/tool/image-generation.ts b/packages/core/src/github-copilot/responses/tool/image-generation.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/responses/tool/image-generation.ts rename to packages/core/src/github-copilot/responses/tool/image-generation.ts diff --git a/packages/opencode/src/provider/sdk/copilot/responses/tool/local-shell.ts b/packages/core/src/github-copilot/responses/tool/local-shell.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/responses/tool/local-shell.ts rename to packages/core/src/github-copilot/responses/tool/local-shell.ts diff --git a/packages/opencode/src/provider/sdk/copilot/responses/tool/web-search-preview.ts b/packages/core/src/github-copilot/responses/tool/web-search-preview.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/responses/tool/web-search-preview.ts rename to packages/core/src/github-copilot/responses/tool/web-search-preview.ts diff --git a/packages/opencode/src/provider/sdk/copilot/responses/tool/web-search.ts b/packages/core/src/github-copilot/responses/tool/web-search.ts similarity index 100% rename from packages/opencode/src/provider/sdk/copilot/responses/tool/web-search.ts rename to packages/core/src/github-copilot/responses/tool/web-search.ts diff --git a/packages/core/src/instance-layer.ts b/packages/core/src/instance-layer.ts new file mode 100644 index 00000000000..20e6ac1aceb --- /dev/null +++ b/packages/core/src/instance-layer.ts @@ -0,0 +1,12 @@ +import { Layer, LayerMap } from "effect" +import { Instance } from "./instance" +import { Catalog } from "./catalog" +import { PluginBoot } from "./plugin/boot" + +export class InstanceServiceMap extends LayerMap.Service()("@opencode/example/InstanceServiceMap", { + lookup: (ref: Instance.Ref) => { + const instance = Layer.succeed(Instance.Service, Instance.Service.of(ref)) + return Layer.mergeAll(Catalog.defaultLayer, PluginBoot.defaultLayer).pipe(Layer.provide(instance)) + }, + idleTimeToLive: "5 minutes", +}) {} diff --git a/packages/core/src/instance.ts b/packages/core/src/instance.ts new file mode 100644 index 00000000000..af6de951be4 --- /dev/null +++ b/packages/core/src/instance.ts @@ -0,0 +1,10 @@ +import { Context } from "effect" + +export * as Instance from "./instance" + +export type Ref = { + readonly directory: string + readonly workspaceID?: string +} + +export class Service extends Context.Service()("@opencode/Instance") {} diff --git a/packages/core/src/model.ts b/packages/core/src/model.ts new file mode 100644 index 00000000000..77b8c60ebe7 --- /dev/null +++ b/packages/core/src/model.ts @@ -0,0 +1,116 @@ +import { DateTime, Schema } from "effect" +import { DateTimeUtcFromMillis } from "effect/Schema" +import { ProviderV2 } from "./provider" + +export const ID = Schema.String.pipe(Schema.brand("ModelV2.ID")) +export type ID = typeof ID.Type + +export const VariantID = Schema.String.pipe(Schema.brand("VariantID")) +export type VariantID = typeof VariantID.Type + +// Grouping of models, eg claude opus, claude sonnet +export const Family = Schema.String.pipe(Schema.brand("Family")) +export type Family = typeof Family.Type + +export const Capabilities = Schema.Struct({ + tools: Schema.Boolean, + // mime patterns, image, audio, video/*, text/* + input: Schema.String.pipe(Schema.Array), + output: Schema.String.pipe(Schema.Array), +}) +export type Capabilities = typeof Capabilities.Type + +export const Cost = Schema.Struct({ + tier: Schema.Struct({ + type: Schema.Literal("context"), + size: Schema.Int, + }).pipe(Schema.optional), + input: Schema.Finite, + output: Schema.Finite, + cache: Schema.Struct({ + read: Schema.Finite, + write: Schema.Finite, + }), +}) + +export const Ref = Schema.Struct({ + id: ID, + providerID: ProviderV2.ID, + variant: VariantID, +}) +export type Ref = typeof Ref.Type + +export class Info extends Schema.Class("ModelV2.Info")({ + id: ID, + apiID: ID, + providerID: ProviderV2.ID, + family: Family.pipe(Schema.optional), + name: Schema.String, + endpoint: ProviderV2.Endpoint, + capabilities: Capabilities, + options: Schema.Struct({ + ...ProviderV2.Options.fields, + variant: Schema.String.pipe(Schema.optional), + }), + variants: Schema.Struct({ + id: VariantID, + ...ProviderV2.Options.fields, + }).pipe(Schema.Array), + time: Schema.Struct({ + released: DateTimeUtcFromMillis, + }), + cost: Cost.pipe(Schema.Array), + status: Schema.Literals(["alpha", "beta", "deprecated", "active"]), + enabled: Schema.Boolean, + limit: Schema.Struct({ + context: Schema.Int, + input: Schema.Int.pipe(Schema.optional), + output: Schema.Int, + }), +}) { + static empty(providerID: ProviderV2.ID, modelID: ID) { + return new Info({ + id: modelID, + apiID: modelID, + providerID, + name: modelID, + endpoint: { + type: "unknown", + }, + capabilities: { + tools: false, + input: [], + output: [], + }, + options: { + headers: {}, + body: {}, + aisdk: { + provider: {}, + request: {}, + }, + }, + variants: [], + time: { + released: DateTime.makeUnsafe(0), + }, + cost: [], + status: "active", + enabled: true, + limit: { + context: 0, + output: 0, + }, + }) + } +} + +export function parse(input: string): { providerID: ProviderV2.ID; modelID: ID } { + const [providerID, ...modelID] = input.split("/") + return { + providerID: ProviderV2.ID.make(providerID), + modelID: ID.make(modelID.join("/")), + } +} + +export * as ModelV2 from "./model" diff --git a/packages/core/src/models-snapshot.d.ts b/packages/core/src/models-snapshot.d.ts new file mode 100644 index 00000000000..839eba6b7d5 --- /dev/null +++ b/packages/core/src/models-snapshot.d.ts @@ -0,0 +1,2 @@ +// Auto-generated by build.ts - do not edit +export declare const snapshot: Record diff --git a/packages/core/src/models-snapshot.js b/packages/core/src/models-snapshot.js new file mode 100644 index 00000000000..c582a75bdfd --- /dev/null +++ b/packages/core/src/models-snapshot.js @@ -0,0 +1,71726 @@ +// @ts-nocheck +// Auto-generated by build.ts - do not edit +export const snapshot = { + "302ai": { + id: "302ai", + env: ["302AI_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.302.ai/v1", + name: "302.AI", + doc: "https://doc.302.ai", + models: { + "qwen3-235b-a22b": { + id: "qwen3-235b-a22b", + name: "Qwen3-235B-A22B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04-29", + last_updated: "2025-04-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.29, output: 2.86 }, + limit: { context: 128000, output: 16384 }, + }, + "grok-4.1": { + id: "grok-4.1", + name: "grok-4.1", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-11-18", + last_updated: "2025-11-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 10 }, + limit: { context: 200000, output: 64000 }, + }, + "MiniMax-M2": { + id: "MiniMax-M2", + name: "MiniMax-M2", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-10-26", + last_updated: "2025-10-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.33, output: 1.32 }, + limit: { context: 1000000, output: 128000 }, + }, + "grok-4-1-fast-reasoning": { + id: "grok-4-1-fast-reasoning", + name: "grok-4-1-fast-reasoning", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-11-20", + last_updated: "2025-11-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5 }, + limit: { context: 2000000, output: 30000 }, + }, + "gemini-2.5-flash-nothink": { + id: "gemini-2.5-flash-nothink", + name: "gemini-2.5-flash-nothink", + family: "gemini-flash", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-06-24", + last_updated: "2025-06-24", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5 }, + limit: { context: 1000000, output: 65536 }, + }, + "grok-4.20-multi-agent-beta-0309": { + id: "grok-4.20-multi-agent-beta-0309", + name: "grok-4.20-multi-agent-beta-0309", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-16", + last_updated: "2026-03-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6 }, + limit: { context: 2000000, output: 30000 }, + }, + "kimi-k2-0905-preview": { + id: "kimi-k2-0905-preview", + name: "kimi-k2-0905-preview", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.632, output: 2.53 }, + limit: { context: 262144, output: 262144 }, + }, + "claude-haiku-4-5": { + id: "claude-haiku-4-5", + name: "claude-haiku-4-5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-16", + last_updated: "2025-10-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5 }, + limit: { context: 200000, output: 64000 }, + }, + "claude-opus-4-5-20251101": { + id: "claude-opus-4-5-20251101", + name: "claude-opus-4-5-20251101", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-25", + last_updated: "2025-11-25", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25 }, + limit: { context: 200000, output: 64000 }, + }, + "gemini-2.5-flash-lite-preview-09-2025": { + id: "gemini-2.5-flash-lite-preview-09-2025", + name: "gemini-2.5-flash-lite-preview-09-2025", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-09-26", + last_updated: "2025-09-26", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 1000000, output: 65536 }, + }, + "qwen3-235b-a22b-instruct-2507": { + id: "qwen3-235b-a22b-instruct-2507", + name: "qwen3-235b-a22b-instruct-2507", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-30", + last_updated: "2025-07-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.29, output: 1.143 }, + limit: { context: 128000, output: 65536 }, + }, + "glm-5v-turbo": { + id: "glm-5v-turbo", + name: "GLM-5V-Turbo", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.72, output: 3.2 }, + limit: { context: 200000, output: 131072 }, + }, + "mistral-large-2512": { + id: "mistral-large-2512", + name: "mistral-large-2512", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-12-16", + last_updated: "2025-12-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 3.3 }, + limit: { context: 128000, output: 262144 }, + }, + "glm-4.7": { + id: "glm-4.7", + name: "glm-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.286, output: 1.142 }, + limit: { context: 204800, output: 131072 }, + }, + "claude-3-5-haiku-20241022": { + id: "claude-3-5-haiku-20241022", + name: "claude-3-5-haiku-20241022", + family: "claude-haiku", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07-31", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 4 }, + limit: { context: 200000, output: 8192 }, + }, + "doubao-seed-1-8-251215": { + id: "doubao-seed-1-8-251215", + name: "doubao-seed-1-8-251215", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-12-18", + last_updated: "2025-12-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.114, output: 0.286 }, + limit: { context: 224000, output: 64000 }, + }, + "chatgpt-4o-latest": { + id: "chatgpt-4o-latest", + name: "chatgpt-4o-latest", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-09", + release_date: "2024-08-08", + last_updated: "2024-08-08", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 15 }, + limit: { context: 128000, output: 16384 }, + }, + "glm-5": { + id: "glm-5", + name: "glm-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.6 }, + limit: { context: 204800, output: 131072 }, + }, + "deepseek-chat": { + id: "deepseek-chat", + name: "Deepseek-Chat", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2024-11-29", + last_updated: "2024-11-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.29, output: 0.43 }, + limit: { context: 128000, output: 8192 }, + }, + "deepseek-v3.2-thinking": { + id: "deepseek-v3.2-thinking", + name: "DeepSeek-V3.2-Thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.29, output: 0.43 }, + limit: { context: 128000, output: 128000 }, + }, + "claude-sonnet-4-6": { + id: "claude-sonnet-4-6", + name: "claude-sonnet-4-6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-18", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 1000000, output: 64000 }, + }, + "gpt-5-thinking": { + id: "gpt-5-thinking", + name: "gpt-5-thinking", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-08-08", + last_updated: "2025-08-08", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 400000, output: 128000 }, + }, + "glm-4.7-flashx": { + id: "glm-4.7-flashx", + name: "glm-4.7-flashx", + family: "glm-flash", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-01-20", + last_updated: "2026-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.0715, output: 0.429 }, + limit: { context: 200000, output: 131072 }, + }, + "gemini-3-flash-preview": { + id: "gemini-3-flash-preview", + name: "gemini-3-flash-preview", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-12-18", + last_updated: "2025-12-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 3 }, + limit: { context: 1000000, output: 65536 }, + }, + "qwen-plus": { + id: "qwen-plus", + name: "Qwen-Plus", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.12, output: 1.2 }, + limit: { context: 1000000, output: 32768 }, + }, + "grok-4.20-beta-0309-non-reasoning": { + id: "grok-4.20-beta-0309-non-reasoning", + name: "grok-4.20-beta-0309-non-reasoning", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-03-16", + last_updated: "2026-03-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6 }, + limit: { context: 2000000, output: 30000 }, + }, + "claude-opus-4-7": { + id: "claude-opus-4-7", + name: "claude-opus-4-7", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2026-01-31", + release_date: "2026-04-17", + last_updated: "2026-04-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 5, + output: 25, + cache_read: 0.5, + cache_write: 6.25, + context_over_200k: { input: 10, output: 37.5, cache_read: 1, cache_write: 12.5 }, + }, + limit: { context: 1000000, output: 128000 }, + }, + "gpt-5-mini": { + id: "gpt-5-mini", + name: "gpt-5-mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-08", + last_updated: "2025-08-08", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gemini-3-pro-preview": { + id: "gemini-3-pro-preview", + name: "gemini-3-pro-preview", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-11-19", + last_updated: "2025-11-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12 }, + limit: { context: 1000000, output: 64000 }, + }, + "MiniMax-M2.7": { + id: "MiniMax-M2.7", + name: "MiniMax-M2.7", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-03-19", + last_updated: "2026-03-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 204800, output: 131072 }, + }, + "qwen3-max-2025-09-23": { + id: "qwen3-max-2025-09-23", + name: "qwen3-max-2025-09-23", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-24", + last_updated: "2025-09-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.86, output: 3.43 }, + limit: { context: 258048, output: 65536 }, + }, + "claude-sonnet-4-5-20250929": { + id: "claude-sonnet-4-5-20250929", + name: "claude-sonnet-4-5-20250929", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 200000, output: 64000 }, + }, + "qwen-flash": { + id: "qwen-flash", + name: "Qwen-Flash", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.022, output: 0.22 }, + limit: { context: 1000000, output: 32768 }, + }, + "gemini-2.5-pro": { + id: "gemini-2.5-pro", + name: "gemini-2.5-pro", + family: "gemini-pro", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 1000000, output: 65536 }, + }, + "grok-4-1-fast-non-reasoning": { + id: "grok-4-1-fast-non-reasoning", + name: "grok-4-1-fast-non-reasoning", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-11-20", + last_updated: "2025-11-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5 }, + limit: { context: 2000000, output: 30000 }, + }, + "claude-3-5-haiku-latest": { + id: "claude-3-5-haiku-latest", + name: "claude-3-5-haiku-latest", + family: "claude-haiku", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07-31", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 4 }, + limit: { context: 200000, output: 8192 }, + }, + "claude-opus-4-5-20251101-thinking": { + id: "claude-opus-4-5-20251101-thinking", + name: "claude-opus-4-5-20251101-thinking", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03", + release_date: "2025-11-25", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25 }, + limit: { context: 200000, output: 64000 }, + }, + "gpt-5.2": { + id: "gpt-5.2", + name: "gpt-5.2", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-12", + last_updated: "2025-12-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gpt-5.4-mini": { + id: "gpt-5.4-mini", + name: "gpt-5.4-mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-19", + last_updated: "2026-03-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.75, output: 4.5 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gemini-3-pro-image-preview": { + id: "gemini-3-pro-image-preview", + name: "gemini-3-pro-image-preview", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-06", + release_date: "2025-11-20", + last_updated: "2025-11-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 120 }, + limit: { context: 32768, output: 64000 }, + }, + "glm-5.1": { + id: "glm-5.1", + name: "glm-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-04-10", + last_updated: "2026-04-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.86, output: 3.5 }, + limit: { context: 200000, output: 131072 }, + }, + "qwen-max-latest": { + id: "qwen-max-latest", + name: "Qwen-Max-Latest", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2024-04-03", + last_updated: "2025-01-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.343, output: 1.372 }, + limit: { context: 131072, output: 8192 }, + }, + "gpt-5.4-nano": { + id: "gpt-5.4-nano", + name: "gpt-5.4-nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-19", + last_updated: "2026-03-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.25 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gemini-2.5-flash-image": { + id: "gemini-2.5-flash-image", + name: "gemini-2.5-flash-image", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-01", + release_date: "2025-10-08", + last_updated: "2025-10-08", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 30 }, + limit: { context: 32768, output: 32768 }, + }, + "glm-4.5": { + id: "glm-4.5", + name: "GLM-4.5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-29", + last_updated: "2025-07-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.286, output: 1.142 }, + limit: { context: 131072, output: 98304 }, + }, + "gpt-5.4-mini-2026-03-17": { + id: "gpt-5.4-mini-2026-03-17", + name: "gpt-5.4-mini-2026-03-17", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-19", + last_updated: "2026-03-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.75, output: 4.5 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gemini-2.5-flash": { + id: "gemini-2.5-flash", + name: "gemini-2.5-flash", + family: "gemini-flash", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5 }, + limit: { context: 1000000, output: 65536 }, + }, + "gpt-5.2-chat-latest": { + id: "gpt-5.2-chat-latest", + name: "gpt-5.2-chat-latest", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-12", + last_updated: "2025-12-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14 }, + limit: { context: 128000, output: 16384 }, + }, + "doubao-seed-1-6-vision-250815": { + id: "doubao-seed-1-6-vision-250815", + name: "doubao-seed-1-6-vision-250815", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.114, output: 1.143 }, + limit: { context: 256000, output: 32000 }, + }, + "gemini-3.1-flash-image-preview": { + id: "gemini-3.1-flash-image-preview", + name: "gemini-3.1-flash-image-preview", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-27", + last_updated: "2026-02-27", + modalities: { input: ["text", "image", "pdf"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 0.5, output: 60 }, + limit: { context: 131072, output: 32768 }, + }, + "MiniMax-M2.7-highspeed": { + id: "MiniMax-M2.7-highspeed", + name: "MiniMax-M2.7-highspeed", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-03-19", + last_updated: "2026-03-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 4.8 }, + limit: { context: 204800, output: 131072 }, + }, + "glm-4.5-x": { + id: "glm-4.5-x", + name: "glm-4.5-x", + family: "glm", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-29", + last_updated: "2025-07-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.143, output: 2.29 }, + limit: { context: 128000, output: 16384 }, + }, + "MiniMax-M2.1": { + id: "MiniMax-M2.1", + name: "MiniMax-M2.1", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-12-19", + last_updated: "2025-12-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 1000000, output: 131072 }, + }, + "gpt-5.1": { + id: "gpt-5.1", + name: "gpt-5.1", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-14", + last_updated: "2025-11-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "kimi-k2-thinking-turbo": { + id: "kimi-k2-thinking-turbo", + name: "kimi-k2-thinking-turbo", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.265, output: 9.119 }, + limit: { context: 262144, output: 262144 }, + }, + "deepseek-reasoner": { + id: "deepseek-reasoner", + name: "Deepseek-Reasoner", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.29, output: 0.43 }, + limit: { context: 128000, output: 128000 }, + }, + "grok-4-fast-reasoning": { + id: "grok-4-fast-reasoning", + name: "grok-4-fast-reasoning", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-09-23", + last_updated: "2025-09-23", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5 }, + limit: { context: 2000000, output: 30000 }, + }, + "claude-opus-4-1-20250805-thinking": { + id: "claude-opus-4-1-20250805-thinking", + name: "claude-opus-4-1-20250805-thinking", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03", + release_date: "2025-05-27", + last_updated: "2025-05-27", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75 }, + limit: { context: 200000, output: 32000 }, + }, + "glm-4.5-air": { + id: "glm-4.5-air", + name: "glm-4.5-air", + family: "glm-air", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-29", + last_updated: "2025-07-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1143, output: 0.286 }, + limit: { context: 131072, output: 98304 }, + }, + "gpt-5.4-pro": { + id: "gpt-5.4-pro", + name: "gpt-5.4-pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 180, cache_read: 0, cache_write: 0, context_over_200k: { input: 60, output: 270 } }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "glm-5-turbo": { + id: "glm-5-turbo", + name: "glm-5-turbo", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-03-16", + last_updated: "2026-03-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.72, output: 3.2 }, + limit: { context: 200000, output: 131072 }, + }, + "qwen3-30b-a3b": { + id: "qwen3-30b-a3b", + name: "Qwen3-30B-A3B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04-29", + last_updated: "2025-04-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.11, output: 1.08 }, + limit: { context: 128000, output: 8192 }, + }, + "claude-opus-4-5": { + id: "claude-opus-4-5", + name: "claude-opus-4-5", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-25", + last_updated: "2025-11-25", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25 }, + limit: { context: 200000, output: 64000 }, + }, + "glm-4.5v": { + id: "glm-4.5v", + name: "GLM-4.5V", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-08-12", + last_updated: "2025-08-12", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.29, output: 0.86 }, + limit: { context: 64000, output: 16384 }, + }, + "glm-4.6": { + id: "glm-4.6", + name: "glm-4.6", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.286, output: 1.142 }, + limit: { context: 204800, output: 131072 }, + }, + "claude-opus-4-6-thinking": { + id: "claude-opus-4-6-thinking", + name: "claude-opus-4-6-thinking", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-02-06", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25 }, + limit: { context: 1000000, output: 128000 }, + }, + "gemini-2.5-flash-preview-09-2025": { + id: "gemini-2.5-flash-preview-09-2025", + name: "gemini-2.5-flash-preview-09-2025", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-09-26", + last_updated: "2025-09-26", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5 }, + limit: { context: 1000000, output: 65536 }, + }, + "claude-sonnet-4-6-thinking": { + id: "claude-sonnet-4-6-thinking", + name: "claude-sonnet-4-6-thinking", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08", + release_date: "2026-02-18", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 1000000, output: 64000 }, + }, + "glm-4.6v": { + id: "glm-4.6v", + name: "GLM-4.6V", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-08", + last_updated: "2025-12-08", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.145, output: 0.43 }, + limit: { context: 128000, output: 32768 }, + }, + "claude-opus-4-1-20250805": { + id: "claude-opus-4-1-20250805", + name: "claude-opus-4-1-20250805", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75 }, + limit: { context: 200000, output: 32000 }, + }, + "gpt-5.4": { + id: "gpt-5.4", + name: "gpt-5.4", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 2.5, + output: 15, + cache_read: 0.25, + cache_write: 0, + context_over_200k: { input: 5, output: 22.5 }, + }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "gpt-5.1-chat-latest": { + id: "gpt-5.1-chat-latest", + name: "gpt-5.1-chat-latest", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-14", + last_updated: "2025-11-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 128000, output: 16384 }, + }, + "claude-haiku-4-5-20251001": { + id: "claude-haiku-4-5-20251001", + name: "claude-haiku-4-5-20251001", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-16", + last_updated: "2025-10-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5 }, + limit: { context: 200000, output: 64000 }, + }, + "MiniMax-M1": { + id: "MiniMax-M1", + name: "MiniMax-M1", + family: "minimax", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-06-16", + last_updated: "2025-06-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.132, output: 1.254 }, + limit: { context: 1000000, output: 128000 }, + }, + "gpt-5.4-nano-2026-03-17": { + id: "gpt-5.4-nano-2026-03-17", + name: "gpt-5.4-nano-2026-03-17", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-19", + last_updated: "2026-03-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.25 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "claude-sonnet-4-20250514": { + id: "claude-sonnet-4-20250514", + name: "claude-sonnet-4-20250514", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 200000, output: 64000 }, + }, + "qwen3-coder-480b-a35b-instruct": { + id: "qwen3-coder-480b-a35b-instruct", + name: "qwen3-coder-480b-a35b-instruct", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.86, output: 3.43 }, + limit: { context: 262144, output: 65536 }, + }, + "claude-opus-4-6": { + id: "claude-opus-4-6", + name: "claude-opus-4-6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-06", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25 }, + limit: { context: 1000000, output: 128000 }, + }, + "doubao-seed-code-preview-251028": { + id: "doubao-seed-code-preview-251028", + name: "doubao-seed-code-preview-251028", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-11-11", + last_updated: "2025-11-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.17, output: 1.14 }, + limit: { context: 256000, output: 32000 }, + }, + "gpt-4.1-nano": { + id: "gpt-4.1-nano", + name: "gpt-4.1-nano", + family: "gpt-nano", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 1047576, output: 32768 }, + }, + "deepseek-v3.2": { + id: "deepseek-v3.2", + name: "deepseek-v3.2", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.29, output: 0.43 }, + limit: { context: 128000, output: 8192 }, + }, + "gpt-5-pro": { + id: "gpt-5-pro", + name: "gpt-5-pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-10-08", + last_updated: "2025-10-08", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 120 }, + limit: { context: 400000, input: 272000, output: 272000 }, + }, + "gpt-4o": { + id: "gpt-4o", + name: "gpt-4o", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-09", + release_date: "2024-05-13", + last_updated: "2024-05-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10 }, + limit: { context: 128000, output: 16384 }, + }, + "claude-sonnet-4-5": { + id: "claude-sonnet-4-5", + name: "claude-sonnet-4-5", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 200000, output: 64000 }, + }, + "gpt-5": { + id: "gpt-5", + name: "gpt-5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-08-08", + last_updated: "2025-08-08", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "grok-4.20-beta-0309-reasoning": { + id: "grok-4.20-beta-0309-reasoning", + name: "grok-4.20-beta-0309-reasoning", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-16", + last_updated: "2026-03-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6 }, + limit: { context: 2000000, output: 30000 }, + }, + "claude-opus-4-20250514": { + id: "claude-opus-4-20250514", + name: "claude-opus-4-20250514", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75 }, + limit: { context: 200000, output: 32000 }, + }, + "glm-for-coding": { + id: "glm-for-coding", + name: "glm-for-coding", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.086, output: 0.343 }, + limit: { context: 200000, output: 131072 }, + }, + "claude-sonnet-4-5-20250929-thinking": { + id: "claude-sonnet-4-5-20250929-thinking", + name: "claude-sonnet-4-5-20250929-thinking", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03", + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 200000, output: 64000 }, + }, + "glm-4.5-airx": { + id: "glm-4.5-airx", + name: "glm-4.5-airx", + family: "glm", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-29", + last_updated: "2025-07-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.572, output: 1.714 }, + limit: { context: 128000, output: 16384 }, + }, + "gpt-4.1": { + id: "gpt-4.1", + name: "gpt-4.1", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8 }, + limit: { context: 1047576, output: 32768 }, + }, + "kimi-k2-thinking": { + id: "kimi-k2-thinking", + name: "kimi-k2-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.575, output: 2.3 }, + limit: { context: 262144, output: 262144 }, + }, + "gemini-2.0-flash-lite": { + id: "gemini-2.0-flash-lite", + name: "gemini-2.0-flash-lite", + family: "gemini-flash-lite", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-11", + release_date: "2025-06-16", + last_updated: "2025-06-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.075, output: 0.3 }, + limit: { context: 2000000, output: 8192 }, + }, + "gpt-4.1-mini": { + id: "gpt-4.1-mini", + name: "gpt-4.1-mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.6 }, + limit: { context: 1047576, output: 32768 }, + }, + "grok-4-fast-non-reasoning": { + id: "grok-4-fast-non-reasoning", + name: "grok-4-fast-non-reasoning", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-09-23", + last_updated: "2025-09-23", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5 }, + limit: { context: 2000000, output: 30000 }, + }, + "doubao-seed-1-6-thinking-250715": { + id: "doubao-seed-1-6-thinking-250715", + name: "doubao-seed-1-6-thinking-250715", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-07-15", + last_updated: "2025-07-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.121, output: 1.21 }, + limit: { context: 256000, output: 16000 }, + }, + "ministral-14b-2512": { + id: "ministral-14b-2512", + name: "ministral-14b-2512", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-12-16", + last_updated: "2025-12-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.33, output: 0.33 }, + limit: { context: 128000, output: 128000 }, + }, + }, + }, + alibaba: { + id: "alibaba", + env: ["DASHSCOPE_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1", + name: "Alibaba", + doc: "https://www.alibabacloud.com/help/en/model-studio/models", + models: { + "qwen3-235b-a22b": { + id: "qwen3-235b-a22b", + name: "Qwen3 235B-A22B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.7, output: 2.8, reasoning: 8.4 }, + limit: { context: 131072, output: 16384 }, + }, + "qwen3.5-122b-a10b": { + id: "qwen3.5-122b-a10b", + name: "Qwen3.5 122B-A10B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-23", + last_updated: "2026-02-23", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 3.2 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen3-coder-plus": { + id: "qwen3-coder-plus", + name: "Qwen3 Coder Plus", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 5 }, + limit: { context: 1048576, output: 65536 }, + }, + "qwen3.6-27b": { + id: "qwen3.6-27b", + name: "Qwen3.6 27B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3.6 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen3.5-27b": { + id: "qwen3.5-27b", + name: "Qwen3.5 27B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-23", + last_updated: "2026-02-23", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 2.4 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen-vl-ocr": { + id: "qwen-vl-ocr", + name: "Qwen-VL OCR", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-04", + release_date: "2024-10-28", + last_updated: "2025-04-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.72, output: 0.72 }, + limit: { context: 34096, output: 4096 }, + }, + "qwen-omni-turbo-realtime": { + id: "qwen-omni-turbo-realtime", + name: "Qwen-Omni Turbo Realtime", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-05-08", + last_updated: "2025-05-08", + modalities: { input: ["text", "image", "audio"], output: ["text", "audio"] }, + open_weights: false, + cost: { input: 0.27, output: 1.07, input_audio: 4.44, output_audio: 8.89 }, + limit: { context: 32768, output: 2048 }, + }, + "qwen3-8b": { + id: "qwen3-8b", + name: "Qwen3 8B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.18, output: 0.7, reasoning: 2.1 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen3.5-397b-a17b": { + id: "qwen3.5-397b-a17b", + name: "Qwen3.5 397B-A17B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-15", + last_updated: "2026-02-15", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3.6 }, + limit: { context: 262144, output: 65536 }, + }, + "qwq-plus": { + id: "qwq-plus", + name: "QwQ Plus", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-03-05", + last_updated: "2025-03-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 2.4 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen-vl-plus": { + id: "qwen-vl-plus", + name: "Qwen-VL Plus", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-01-25", + last_updated: "2025-08-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.21, output: 0.63 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen3-livetranslate-flash-realtime": { + id: "qwen3-livetranslate-flash-realtime", + name: "Qwen3-LiveTranslate Flash Realtime", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-04", + release_date: "2025-09-22", + last_updated: "2025-09-22", + modalities: { input: ["text", "image", "audio", "video"], output: ["text", "audio"] }, + open_weights: false, + cost: { input: 10, output: 10, input_audio: 10, output_audio: 38 }, + limit: { context: 53248, output: 4096 }, + }, + "qwen3-32b": { + id: "qwen3-32b", + name: "Qwen3 32B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.7, output: 2.8, reasoning: 8.4 }, + limit: { context: 131072, output: 16384 }, + }, + "qwen-max": { + id: "qwen-max", + name: "Qwen Max", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-04-03", + last_updated: "2025-01-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.6, output: 6.4 }, + limit: { context: 32768, output: 8192 }, + }, + "qwen-plus": { + id: "qwen-plus", + name: "Qwen Plus", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-01-25", + last_updated: "2025-09-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.2, reasoning: 4 }, + limit: { context: 1000000, output: 32768 }, + }, + "qwen3.6-35b-a3b": { + id: "qwen3.6-35b-a3b", + name: "Qwen3.6 35B-A3B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-17", + last_updated: "2026-04-17", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0.248, output: 1.485 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen-omni-turbo": { + id: "qwen-omni-turbo", + name: "Qwen-Omni Turbo", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-01-19", + last_updated: "2025-03-26", + modalities: { input: ["text", "image", "audio", "video"], output: ["text", "audio"] }, + open_weights: false, + cost: { input: 0.07, output: 0.27, input_audio: 4.44, output_audio: 8.89 }, + limit: { context: 32768, output: 2048 }, + }, + "qwen-flash": { + id: "qwen-flash", + name: "Qwen Flash", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.4 }, + limit: { context: 1000000, output: 32768 }, + }, + "qwen2-5-vl-7b-instruct": { + id: "qwen2-5-vl-7b-instruct", + name: "Qwen2.5-VL 7B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-09", + last_updated: "2024-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.35, output: 1.05 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen3.6-plus": { + id: "qwen3.6-plus", + name: "Qwen3.6 Plus", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.276, output: 1.651, cache_read: 0.028, cache_write: 0.344 }, + limit: { context: 1000000, output: 65536 }, + }, + "qwen3-max": { + id: "qwen3-max", + name: "Qwen3 Max", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-23", + last_updated: "2025-09-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.2, output: 6 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen3-omni-flash": { + id: "qwen3-omni-flash", + name: "Qwen3-Omni Flash", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-09-15", + last_updated: "2025-09-15", + modalities: { input: ["text", "image", "audio", "video"], output: ["text", "audio"] }, + open_weights: false, + cost: { input: 0.43, output: 1.66, input_audio: 3.81, output_audio: 15.11 }, + limit: { context: 65536, output: 16384 }, + }, + "qwen2-5-72b-instruct": { + id: "qwen2-5-72b-instruct", + name: "Qwen2.5 72B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-09", + last_updated: "2024-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.4, output: 5.6 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen3-vl-235b-a22b": { + id: "qwen3-vl-235b-a22b", + name: "Qwen3-VL 235B-A22B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.7, output: 2.8, reasoning: 8.4 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen3-asr-flash": { + id: "qwen3-asr-flash", + name: "Qwen3-ASR Flash", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + knowledge: "2024-04", + release_date: "2025-09-08", + last_updated: "2025-09-08", + modalities: { input: ["audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.035, output: 0.035 }, + limit: { context: 53248, output: 4096 }, + }, + "qwen3-next-80b-a3b-thinking": { + id: "qwen3-next-80b-a3b-thinking", + name: "Qwen3-Next 80B-A3B (Thinking)", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09", + last_updated: "2025-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 6 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen-mt-plus": { + id: "qwen-mt-plus", + name: "Qwen-MT Plus", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-04", + release_date: "2025-01", + last_updated: "2025-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.46, output: 7.37 }, + limit: { context: 16384, output: 8192 }, + }, + "qwen-vl-max": { + id: "qwen-vl-max", + name: "Qwen-VL Max", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-04-08", + last_updated: "2025-08-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 3.2 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen3-coder-flash": { + id: "qwen3-coder-flash", + name: "Qwen3 Coder Flash", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.5 }, + limit: { context: 1000000, output: 65536 }, + }, + "qwen2-5-7b-instruct": { + id: "qwen2-5-7b-instruct", + name: "Qwen2.5 7B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-09", + last_updated: "2024-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.175, output: 0.7 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen2-5-14b-instruct": { + id: "qwen2-5-14b-instruct", + name: "Qwen2.5 14B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-09", + last_updated: "2024-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.35, output: 1.4 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen2-5-32b-instruct": { + id: "qwen2-5-32b-instruct", + name: "Qwen2.5 32B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-09", + last_updated: "2024-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.7, output: 2.8 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen3-next-80b-a3b-instruct": { + id: "qwen3-next-80b-a3b-instruct", + name: "Qwen3-Next 80B-A3B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09", + last_updated: "2025-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 2 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen-plus-character-ja": { + id: "qwen-plus-character-ja", + name: "Qwen Plus Character (Japanese)", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-01", + last_updated: "2024-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 1.4 }, + limit: { context: 8192, output: 512 }, + }, + "qwen3-omni-flash-realtime": { + id: "qwen3-omni-flash-realtime", + name: "Qwen3-Omni Flash Realtime", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-09-15", + last_updated: "2025-09-15", + modalities: { input: ["text", "image", "audio", "video"], output: ["text", "audio"] }, + open_weights: false, + cost: { input: 0.52, output: 1.99, input_audio: 4.57, output_audio: 18.13 }, + limit: { context: 65536, output: 16384 }, + }, + "qwen3-vl-30b-a3b": { + id: "qwen3-vl-30b-a3b", + name: "Qwen3-VL 30B-A3B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.8, reasoning: 2.4 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen3-vl-plus": { + id: "qwen3-vl-plus", + name: "Qwen3-VL Plus", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-23", + last_updated: "2025-09-23", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.6, reasoning: 4.8 }, + limit: { context: 262144, output: 32768 }, + }, + "qwen3-coder-480b-a35b-instruct": { + id: "qwen3-coder-480b-a35b-instruct", + name: "Qwen3-Coder 480B-A35B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.5, output: 7.5 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen3-coder-30b-a3b-instruct": { + id: "qwen3-coder-30b-a3b-instruct", + name: "Qwen3-Coder 30B-A3B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.45, output: 2.25 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen-turbo": { + id: "qwen-turbo", + name: "Qwen Turbo", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-11-01", + last_updated: "2025-04-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.2, reasoning: 0.5 }, + limit: { context: 1000000, output: 16384 }, + }, + "qwen-mt-turbo": { + id: "qwen-mt-turbo", + name: "Qwen-MT Turbo", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-04", + release_date: "2025-01", + last_updated: "2025-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.16, output: 0.49 }, + limit: { context: 16384, output: 8192 }, + }, + "qwen3.6-max-preview": { + id: "qwen3.6-max-preview", + name: "Qwen3.6 Max Preview", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-04-20", + last_updated: "2026-04-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.3, output: 7.8, cache_read: 0.13, cache_write: 1.625 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen2-5-omni-7b": { + id: "qwen2-5-omni-7b", + name: "Qwen2.5-Omni 7B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-12", + last_updated: "2024-12", + modalities: { input: ["text", "image", "audio", "video"], output: ["text", "audio"] }, + open_weights: true, + cost: { input: 0.1, output: 0.4, input_audio: 6.76 }, + limit: { context: 32768, output: 2048 }, + }, + "qwen3.5-plus": { + id: "qwen3.5-plus", + name: "Qwen3.5 Plus", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-16", + last_updated: "2026-02-16", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2.4, reasoning: 2.4 }, + limit: { context: 1000000, output: 65536 }, + }, + "qwen2-5-vl-72b-instruct": { + id: "qwen2-5-vl-72b-instruct", + name: "Qwen2.5-VL 72B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-09", + last_updated: "2024-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 2.8, output: 8.4 }, + limit: { context: 131072, output: 8192 }, + }, + "qvq-max": { + id: "qvq-max", + name: "QVQ Max", + family: "qvq", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-03-25", + last_updated: "2025-03-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.2, output: 4.8 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen3-14b": { + id: "qwen3-14b", + name: "Qwen3 14B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.35, output: 1.4, reasoning: 4.2 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen3.5-35b-a3b": { + id: "qwen3.5-35b-a3b", + name: "Qwen3.5 35B-A3B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-23", + last_updated: "2026-02-23", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 2 }, + limit: { context: 262144, output: 65536 }, + }, + }, + }, + scaleway: { + id: "scaleway", + env: ["SCALEWAY_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.scaleway.ai/v1", + name: "Scaleway", + doc: "https://www.scaleway.com/en/docs/generative-apis/", + models: { + "qwen3-embedding-8b": { + id: "qwen3-embedding-8b", + name: "Qwen3 Embedding 8B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-25-11", + last_updated: "2026-03-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0 }, + limit: { context: 32768, output: 4096 }, + }, + "qwen3-235b-a22b-instruct-2507": { + id: "qwen3-235b-a22b-instruct-2507", + name: "Qwen3 235B A22B Instruct 2507", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-07-01", + last_updated: "2026-03-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.75, output: 2.25 }, + limit: { context: 260000, output: 16384 }, + }, + "llama-3.3-70b-instruct": { + id: "llama-3.3-70b-instruct", + name: "Llama-3.3-70B-Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-12-06", + last_updated: "2026-03-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.9, output: 0.9 }, + limit: { context: 100000, output: 16384 }, + }, + "qwen3.5-397b-a17b": { + id: "qwen3.5-397b-a17b", + name: "Qwen3.5 397B A17B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3.6 }, + limit: { context: 256000, output: 16384 }, + }, + "devstral-2-123b-instruct-2512": { + id: "devstral-2-123b-instruct-2512", + name: "Devstral 2 123B Instruct (2512)", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-01-07", + last_updated: "2026-03-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 2 }, + limit: { context: 256000, output: 16384 }, + }, + "deepseek-r1-distill-llama-70b": { + id: "deepseek-r1-distill-llama-70b", + name: "DeepSeek R1 Distill Llama 70B", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-01-20", + last_updated: "2026-03-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.9, output: 0.9 }, + limit: { context: 32000, output: 8196 }, + }, + "pixtral-12b-2409": { + id: "pixtral-12b-2409", + name: "Pixtral 12B 2409", + family: "pixtral", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-09-25", + last_updated: "2026-03-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 128000, output: 4096 }, + }, + "whisper-large-v3": { + id: "whisper-large-v3", + name: "Whisper Large v3", + family: "whisper", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + knowledge: "2023-09", + release_date: "2023-09-01", + last_updated: "2026-03-17", + modalities: { input: ["audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0.003, output: 0 }, + limit: { context: 0, output: 8192 }, + }, + "voxtral-small-24b-2507": { + id: "voxtral-small-24b-2507", + name: "Voxtral Small 24B 2507", + family: "voxtral", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-07-01", + last_updated: "2026-03-17", + modalities: { input: ["text", "audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.35 }, + limit: { context: 32000, output: 16384 }, + }, + "gemma-3-27b-it": { + id: "gemma-3-27b-it", + name: "Gemma-3-27B-IT", + family: "gemma", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2024-12-01", + last_updated: "2026-03-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 0.5 }, + limit: { context: 40000, output: 8192 }, + }, + "bge-multilingual-gemma2": { + id: "bge-multilingual-gemma2", + name: "BGE Multilingual Gemma2", + family: "gemma", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-07-26", + last_updated: "2025-06-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0 }, + limit: { context: 8191, output: 3072 }, + }, + "qwen3-coder-30b-a3b-instruct": { + id: "qwen3-coder-30b-a3b-instruct", + name: "Qwen3-Coder 30B-A3B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2026-03-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.8 }, + limit: { context: 128000, output: 32768 }, + }, + "mistral-small-3.2-24b-instruct-2506": { + id: "mistral-small-3.2-24b-instruct-2506", + name: "Mistral Small 3.2 24B Instruct (2506)", + family: "mistral-small", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-06-20", + last_updated: "2026-03-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.35 }, + limit: { context: 128000, output: 32768 }, + }, + "gpt-oss-120b": { + id: "gpt-oss-120b", + name: "GPT-OSS 120B", + family: "gpt-oss", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-01-01", + last_updated: "2026-03-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 128000, output: 32768 }, + }, + "mistral-nemo-instruct-2407": { + id: "mistral-nemo-instruct-2407", + name: "Mistral Nemo Instruct 2407", + family: "mistral-nemo", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-07-25", + last_updated: "2026-03-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 128000, output: 8192 }, + }, + "llama-3.1-8b-instruct": { + id: "llama-3.1-8b-instruct", + name: "Llama 3.1 8B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2025-01-01", + last_updated: "2026-03-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 128000, output: 16384 }, + }, + }, + }, + "nano-gpt": { + id: "nano-gpt", + env: ["NANO_GPT_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://nano-gpt.com/api/v1", + name: "NanoGPT", + doc: "https://docs.nano-gpt.com", + models: { + "glm-4-flash": { + id: "glm-4-flash", + name: "GLM-4 Flash", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-08-01", + last_updated: "2024-08-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1003, output: 0.1003 }, + limit: { context: 128000, input: 128000, output: 4096 }, + }, + "Meta-Llama-3-1-8B-Instruct-FP8": { + id: "Meta-Llama-3-1-8B-Instruct-FP8", + name: "Llama 3.1 8B (decentralized)", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.02, output: 0.03 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "claude-opus-4-thinking:32000": { + id: "claude-opus-4-thinking:32000", + name: "Claude 4 Opus Thinking (32K)", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 14.994, output: 75.004 }, + limit: { context: 200000, input: 200000, output: 32000 }, + }, + "gemini-2.5-pro-preview-05-06": { + id: "gemini-2.5-pro-preview-05-06", + name: "Gemini 2.5 Pro Preview 0506", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-05-06", + last_updated: "2025-05-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10 }, + limit: { context: 1048756, input: 1048756, output: 65536 }, + }, + "grok-3-mini-fast-beta": { + id: "grok-3-mini-fast-beta", + name: "Grok 3 Mini Fast Beta", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 4 }, + limit: { context: 131072, input: 131072, output: 131072 }, + }, + "MiniMax-M2": { + id: "MiniMax-M2", + name: "MiniMax M2", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-10-25", + last_updated: "2025-10-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.17, output: 1.53 }, + limit: { context: 200000, input: 200000, output: 131072 }, + }, + "command-a-reasoning-08-2025": { + id: "command-a-reasoning-08-2025", + name: "Cohere Command A (08/2025)", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-22", + last_updated: "2025-08-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10 }, + limit: { context: 256000, input: 256000, output: 8192 }, + }, + brave: { + id: "brave", + name: "Brave (Answers)", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2023-03-02", + last_updated: "2024-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 5 }, + limit: { context: 8192, input: 8192, output: 8192 }, + }, + "exa-research": { + id: "exa-research", + name: "Exa (Research)", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-06-04", + last_updated: "2025-06-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 2.5 }, + limit: { context: 8192, input: 8192, output: 8192 }, + }, + "Llama-3.3-70B-Nova": { + id: "Llama-3.3-70B-Nova", + name: "Llama 3.3 70B Nova", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "gemini-exp-1206": { + id: "gemini-exp-1206", + name: "Gemini 2.0 Pro 1206", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.258, output: 4.998 }, + limit: { context: 2097152, input: 2097152, output: 8192 }, + }, + "claude-opus-4-5-20251101": { + id: "claude-opus-4-5-20251101", + name: "Claude 4.5 Opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-11-01", + last_updated: "2025-11-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 4.998, output: 25.007 }, + limit: { context: 200000, input: 200000, output: 32000 }, + }, + "auto-model-basic": { + id: "auto-model-basic", + name: "Auto model (Basic)", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-06-01", + last_updated: "2024-06-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 9.996, output: 19.992 }, + limit: { context: 1000000, input: 1000000, output: 1000000 }, + }, + "jamba-mini": { + id: "jamba-mini", + name: "Jamba Mini", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1989, output: 0.408 }, + limit: { context: 256000, input: 256000, output: 4096 }, + }, + "gemini-2.5-flash-lite-preview-09-2025": { + id: "gemini-2.5-flash-lite-preview-09-2025", + name: "Gemini 2.5 Flash Lite Preview (09/2025)", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 1048756, input: 1048756, output: 65536 }, + }, + "yi-large": { + id: "yi-large", + name: "Yi Large", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-05-13", + last_updated: "2024-05-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3.196, output: 3.196 }, + limit: { context: 32000, input: 32000, output: 4096 }, + }, + "auto-model-premium": { + id: "auto-model-premium", + name: "Auto model (Premium)", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-06-01", + last_updated: "2024-06-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 9.996, output: 19.992 }, + limit: { context: 1000000, input: 1000000, output: 1000000 }, + }, + "azure-gpt-4o": { + id: "azure-gpt-4o", + name: "Azure gpt-4o", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2024-05-13", + last_updated: "2024-05-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.499, output: 9.996 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "deepseek-v3-0324": { + id: "deepseek-v3-0324", + name: "DeepSeek Chat 0324", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-03-24", + last_updated: "2025-03-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 0.7 }, + limit: { context: 128000, input: 128000, output: 8192 }, + }, + "claude-3-5-haiku-20241022": { + id: "claude-3-5-haiku-20241022", + name: "Claude 3.5 Haiku", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 4 }, + limit: { context: 200000, input: 200000, output: 8192 }, + }, + "doubao-seed-1-8-251215": { + id: "doubao-seed-1-8-251215", + name: "Doubao Seed 1.8", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-15", + last_updated: "2025-12-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.612, output: 6.12 }, + limit: { context: 128000, input: 128000, output: 8192 }, + }, + "doubao-seed-1-6-250615": { + id: "doubao-seed-1-6-250615", + name: "Doubao Seed 1.6", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-06-15", + last_updated: "2025-06-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.204, output: 0.51 }, + limit: { context: 256000, input: 256000, output: 16384 }, + }, + "ernie-x1.1-preview": { + id: "ernie-x1.1-preview", + name: "ERNIE X1.1", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-09-10", + last_updated: "2025-09-10", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 64000, input: 64000, output: 8192 }, + }, + "ernie-5.0-thinking-preview": { + id: "ernie-5.0-thinking-preview", + name: "Ernie 5.0 Thinking Preview", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-11-18", + last_updated: "2025-11-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 2 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "glm-4-air-0111": { + id: "glm-4-air-0111", + name: "GLM 4 Air 0111", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-01-11", + last_updated: "2025-01-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1394, output: 0.1394 }, + limit: { context: 128000, input: 128000, output: 4096 }, + }, + fastgpt: { + id: "fastgpt", + name: "Web Answer", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2023-08-01", + last_updated: "2024-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 7.5, output: 7.5 }, + limit: { context: 32768, input: 32768, output: 32768 }, + }, + "doubao-seed-1-6-thinking-250615": { + id: "doubao-seed-1-6-thinking-250615", + name: "Doubao Seed 1.6 Thinking", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-06-15", + last_updated: "2025-06-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.204, output: 2.04 }, + limit: { context: 256000, input: 256000, output: 16384 }, + }, + "gemini-2.0-flash-001": { + id: "gemini-2.0-flash-001", + name: "Gemini 2.0 Flash", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1003, output: 0.408 }, + limit: { context: 1000000, input: 1000000, output: 8192 }, + }, + "claude-opus-4-1-thinking:32000": { + id: "claude-opus-4-1-thinking:32000", + name: "Claude 4.1 Opus Thinking (32K)", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 14.994, output: 75.004 }, + limit: { context: 200000, input: 200000, output: 32000 }, + }, + "Llama-3.3-70B-RAWMAW": { + id: "Llama-3.3-70B-RAWMAW", + name: "Llama 3.3 70B RAWMAW", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "GLM-4.5-Air-Derestricted-Steam": { + id: "GLM-4.5-Air-Derestricted-Steam", + name: "GLM 4.5 Air Derestricted Steam", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 220600, input: 220600, output: 65536 }, + }, + "claude-3-5-sonnet-20241022": { + id: "claude-3-5-sonnet-20241022", + name: "Claude 3.5 Sonnet", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-08-26", + last_updated: "2025-08-26", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.992, output: 14.994 }, + limit: { context: 200000, input: 200000, output: 8192 }, + }, + "yi-medium-200k": { + id: "yi-medium-200k", + name: "Yi Medium 200k", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-03-01", + last_updated: "2024-03-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.499, output: 2.499 }, + limit: { context: 200000, input: 200000, output: 4096 }, + }, + "Gemma-3-27B-ArliAI-RPMax-v3": { + id: "Gemma-3-27B-ArliAI-RPMax-v3", + name: "Gemma 3 27B RPMax v3", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-03", + last_updated: "2025-07-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "phi-4-mini-instruct": { + id: "phi-4-mini-instruct", + name: "Phi 4 Mini", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-26", + last_updated: "2025-07-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.17, output: 0.68 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "Llama-3.3+(3v3.3)-70B-TenyxChat-DaybreakStorywriter": { + id: "Llama-3.3+(3v3.3)-70B-TenyxChat-DaybreakStorywriter", + name: "Llama 3.3+ 70B TenyxChat DaybreakStorywriter", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "ernie-x1-32k": { + id: "ernie-x1-32k", + name: "Ernie X1 32k", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-05-08", + last_updated: "2025-05-08", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.33, output: 1.32 }, + limit: { context: 32000, input: 32000, output: 16384 }, + }, + "deepseek-chat": { + id: "deepseek-chat", + name: "DeepSeek V3/Deepseek Chat", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-02-27", + last_updated: "2025-02-27", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 0.7 }, + limit: { context: 128000, input: 128000, output: 8192 }, + }, + "glm-z1-air": { + id: "glm-z1-air", + name: "GLM Z1 Air", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-04-15", + last_updated: "2025-04-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.07, output: 0.07 }, + limit: { context: 32000, input: 32000, output: 16384 }, + }, + "claude-3-7-sonnet-thinking:128000": { + id: "claude-3-7-sonnet-thinking:128000", + name: "Claude 3.7 Sonnet Thinking (128K)", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-02-24", + last_updated: "2025-02-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.992, output: 14.994 }, + limit: { context: 200000, input: 200000, output: 64000 }, + }, + "glm-4-air": { + id: "glm-4-air", + name: "GLM-4 Air", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-06-05", + last_updated: "2024-06-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2006, output: 0.2006 }, + limit: { context: 128000, input: 128000, output: 4096 }, + }, + "Llama-3.3-70B-MiraiFanfare": { + id: "Llama-3.3-70B-MiraiFanfare", + name: "Llama 3.3 70b Mirai Fanfare", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-26", + last_updated: "2025-07-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.493, output: 0.493 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "gemini-2.0-flash-thinking-exp-01-21": { + id: "gemini-2.0-flash-thinking-exp-01-21", + name: "Gemini 2.0 Flash Thinking 0121", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-01-21", + last_updated: "2025-01-21", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 1.003 }, + limit: { context: 1000000, input: 1000000, output: 8192 }, + }, + "Magistral-Small-2506": { + id: "Magistral-Small-2506", + name: "Magistral Small 2506", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.4 }, + limit: { context: 32768, input: 32768, output: 32768 }, + }, + "doubao-1.5-pro-32k": { + id: "doubao-1.5-pro-32k", + name: "Doubao 1.5 Pro 32k", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-01-22", + last_updated: "2025-01-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1343, output: 0.3349 }, + limit: { context: 32000, input: 32000, output: 8192 }, + }, + "venice-uncensored:web": { + id: "venice-uncensored:web", + name: "Venice Uncensored Web", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-05-01", + last_updated: "2024-05-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 0.4 }, + limit: { context: 80000, input: 80000, output: 16384 }, + }, + "glm-4": { + id: "glm-4", + name: "GLM-4", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-01-16", + last_updated: "2024-01-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 14.994, output: 14.994 }, + limit: { context: 128000, input: 128000, output: 4096 }, + }, + "qwen-max": { + id: "qwen-max", + name: "Qwen 2.5 Max", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-04-03", + last_updated: "2024-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.5997, output: 6.392 }, + limit: { context: 32000, input: 32000, output: 8192 }, + }, + "qwen3-vl-235b-a22b-instruct-original": { + id: "qwen3-vl-235b-a22b-instruct-original", + name: "Qwen3 VL 235B A22B Instruct Original", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 1.2 }, + limit: { context: 32768, input: 32768, output: 32768 }, + }, + "jamba-large-1.6": { + id: "jamba-large-1.6", + name: "Jamba Large 1.6", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-03-12", + last_updated: "2025-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.989, output: 7.99 }, + limit: { context: 256000, input: 256000, output: 4096 }, + }, + "qwen-plus": { + id: "qwen-plus", + name: "Qwen Plus", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2024-01-25", + last_updated: "2024-01-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3995, output: 1.2002 }, + limit: { context: 995904, input: 995904, output: 32768 }, + }, + "qwen25-vl-72b-instruct": { + id: "qwen25-vl-72b-instruct", + name: "Qwen25 VL 72b", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-05-10", + last_updated: "2025-05-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.69989, output: 0.69989 }, + limit: { context: 32000, input: 32000, output: 32768 }, + }, + "claude-sonnet-4-thinking:64000": { + id: "claude-sonnet-4-thinking:64000", + name: "Claude 4 Sonnet Thinking (64K)", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.992, output: 14.994 }, + limit: { context: 1000000, input: 1000000, output: 64000 }, + }, + "gemini-3-pro-preview": { + id: "gemini-3-pro-preview", + name: "Gemini 3 Pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-11-18", + last_updated: "2025-11-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12 }, + limit: { context: 1048756, input: 1048756, output: 65536 }, + }, + "Llama-3.3+(3.1v3.3)-70B-New-Dawn-v1.1": { + id: "Llama-3.3+(3.1v3.3)-70B-New-Dawn-v1.1", + name: "Llama 3.3+ 70B New Dawn v1.1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "GLM-4.5-Air-Derestricted-Iceblink-ReExtract": { + id: "GLM-4.5-Air-Derestricted-Iceblink-ReExtract", + name: "GLM 4.5 Air Derestricted Iceblink ReExtract", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-12", + last_updated: "2025-12-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 131072, input: 131072, output: 98304 }, + }, + "universal-summarizer": { + id: "universal-summarizer", + name: "Universal Summarizer", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2023-05-01", + last_updated: "2024-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 30 }, + limit: { context: 32768, input: 32768, output: 32768 }, + }, + "claude-sonnet-4-thinking:32768": { + id: "claude-sonnet-4-thinking:32768", + name: "Claude 4 Sonnet Thinking (32K)", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.992, output: 14.994 }, + limit: { context: 1000000, input: 1000000, output: 64000 }, + }, + "sarvan-medium": { + id: "sarvan-medium", + name: "Sarvam Medium", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 0.75 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "claude-3-7-sonnet-thinking:8192": { + id: "claude-3-7-sonnet-thinking:8192", + name: "Claude 3.7 Sonnet Thinking (8K)", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-02-24", + last_updated: "2025-02-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.992, output: 14.994 }, + limit: { context: 200000, input: 200000, output: 64000 }, + }, + "gemini-2.5-flash-preview-05-20": { + id: "gemini-2.5-flash-preview-05-20", + name: "Gemini 2.5 Flash 0520", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-05-20", + last_updated: "2025-05-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 1048000, input: 1048000, output: 65536 }, + }, + "GLM-4.5-Air-Derestricted-Iceblink-v2-ReExtract": { + id: "GLM-4.5-Air-Derestricted-Iceblink-v2-ReExtract", + name: "GLM 4.5 Air Derestricted Iceblink v2 ReExtract", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-12", + last_updated: "2025-12-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 131072, input: 131072, output: 65536 }, + }, + "Llama-3.3-70B-Fallen-v1": { + id: "Llama-3.3-70B-Fallen-v1", + name: "Llama 3.3 70B Fallen v1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "qwen3-vl-235b-a22b-thinking": { + id: "qwen3-vl-235b-a22b-thinking", + name: "Qwen3 VL 235B A22B Thinking", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-08-26", + last_updated: "2025-08-26", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 6 }, + limit: { context: 32768, input: 32768, output: 32768 }, + }, + "claude-3-7-sonnet-thinking:32768": { + id: "claude-3-7-sonnet-thinking:32768", + name: "Claude 3.7 Sonnet Thinking (32K)", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-07-15", + last_updated: "2025-07-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.992, output: 14.994 }, + limit: { context: 200000, input: 200000, output: 64000 }, + }, + "claude-3-7-sonnet-thinking:1024": { + id: "claude-3-7-sonnet-thinking:1024", + name: "Claude 3.7 Sonnet Thinking (1K)", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-02-24", + last_updated: "2025-02-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.992, output: 14.994 }, + limit: { context: 200000, input: 200000, output: 64000 }, + }, + "claude-sonnet-4-5-20250929": { + id: "claude-sonnet-4-5-20250929", + name: "Claude Sonnet 4.5", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.992, output: 14.994 }, + limit: { context: 1000000, input: 1000000, output: 64000 }, + }, + "Llama-3.3-70B-Vulpecula-R1": { + id: "Llama-3.3-70B-Vulpecula-R1", + name: "Llama 3.3 70B Vulpecula R1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "claude-sonnet-4-thinking:8192": { + id: "claude-sonnet-4-thinking:8192", + name: "Claude 4 Sonnet Thinking (8K)", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.992, output: 14.994 }, + limit: { context: 1000000, input: 1000000, output: 64000 }, + }, + "gemini-2.5-pro": { + id: "gemini-2.5-pro", + name: "Gemini 2.5 Pro", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-06-05", + last_updated: "2025-06-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10 }, + limit: { context: 1048756, input: 1048756, output: 65536 }, + }, + "Llama-3.3-70B-Ignition-v0.1": { + id: "Llama-3.3-70B-Ignition-v0.1", + name: "Llama 3.3 70B Ignition v0.1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "glm-4-plus-0111": { + id: "glm-4-plus-0111", + name: "GLM 4 Plus 0111", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-19", + last_updated: "2025-02-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 9.996, output: 9.996 }, + limit: { context: 128000, input: 128000, output: 4096 }, + }, + "KAT-Coder-Air-V1": { + id: "KAT-Coder-Air-V1", + name: "KAT Coder Air V1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-10-28", + last_updated: "2025-10-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.2 }, + limit: { context: 128000, input: 128000, output: 32768 }, + }, + "deepseek-r1-sambanova": { + id: "deepseek-r1-sambanova", + name: "DeepSeek R1 Fast", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-20", + last_updated: "2025-02-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 4.998, output: 6.987 }, + limit: { context: 128000, input: 128000, output: 4096 }, + }, + "deepseek-r1": { + id: "deepseek-r1", + name: "DeepSeek R1", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.7 }, + limit: { context: 128000, input: 128000, output: 8192 }, + }, + "doubao-1-5-thinking-pro-250415": { + id: "doubao-1-5-thinking-pro-250415", + name: "Doubao 1.5 Thinking Pro", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-17", + last_updated: "2025-04-17", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 2.4 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "sonar-pro": { + id: "sonar-pro", + name: "Perplexity Pro", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-19", + last_updated: "2025-02-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.992, output: 14.994 }, + limit: { context: 200000, input: 200000, output: 128000 }, + }, + "Gemma-3-27B-it-Abliterated": { + id: "Gemma-3-27B-it-Abliterated", + name: "Gemma 3 27B IT Abliterated", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-03", + last_updated: "2025-07-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.42, output: 0.42 }, + limit: { context: 32768, input: 32768, output: 96000 }, + }, + "deepseek-chat-cheaper": { + id: "deepseek-chat-cheaper", + name: "DeepSeek V3/Chat Cheaper", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-04-15", + last_updated: "2025-04-15", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 0.7 }, + limit: { context: 128000, input: 128000, output: 8192 }, + }, + "gemini-2.0-pro-exp-02-05": { + id: "gemini-2.0-pro-exp-02-05", + name: "Gemini 2.0 Pro 0205", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-05", + last_updated: "2025-02-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.989, output: 7.956 }, + limit: { context: 2097152, input: 2097152, output: 8192 }, + }, + "azure-gpt-4o-mini": { + id: "azure-gpt-4o-mini", + name: "Azure gpt-4o-mini", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1496, output: 0.595 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "Llama-3.3-70B-MS-Nevoria": { + id: "Llama-3.3-70B-MS-Nevoria", + name: "Llama 3.3 70B MS Nevoria", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "claude-opus-4-thinking": { + id: "claude-opus-4-thinking", + name: "Claude 4 Opus Thinking", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-07-15", + last_updated: "2025-07-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 14.994, output: 75.004 }, + limit: { context: 200000, input: 200000, output: 32000 }, + }, + "Llama-3.3-70B-Sapphira-0.1": { + id: "Llama-3.3-70B-Sapphira-0.1", + name: "Llama 3.3 70B Sapphira 0.1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "doubao-seed-code-preview-latest": { + id: "doubao-seed-code-preview-latest", + name: "Doubao Seed Code Preview", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 256000, input: 256000, output: 16384 }, + }, + "qwen-3.6-plus": { + id: "qwen-3.6-plus", + name: "Qwen 3.6 Plus", + family: "qwen3.6", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.45, output: 2.7 }, + limit: { context: 991800, output: 65536 }, + }, + "Llama-3.3-70B-ArliAI-RPMax-v1.4": { + id: "Llama-3.3-70B-ArliAI-RPMax-v1.4", + name: "Llama 3.3 70B RPMax v1.4", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "mistral-small-31-24b-instruct": { + id: "mistral-small-31-24b-instruct", + name: "Mistral Small 31 24b Instruct", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-15", + last_updated: "2025-04-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 128000, input: 128000, output: 131072 }, + }, + "glm-4.1v-thinking-flashx": { + id: "glm-4.1v-thinking-flashx", + name: "GLM 4.1V Thinking FlashX", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.3 }, + limit: { context: 64000, input: 64000, output: 8192 }, + }, + "hunyuan-t1-latest": { + id: "hunyuan-t1-latest", + name: "Hunyuan T1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-03-22", + last_updated: "2025-03-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.17, output: 0.66 }, + limit: { context: 256000, input: 256000, output: 16384 }, + }, + "doubao-1-5-thinking-vision-pro-250428": { + id: "doubao-1-5-thinking-vision-pro-250428", + name: "Doubao 1.5 Thinking Vision Pro", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-05-15", + last_updated: "2025-05-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.55, output: 1.43 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "asi1-mini": { + id: "asi1-mini", + name: "ASI1 Mini", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-03-25", + last_updated: "2025-03-25", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 1 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "ernie-5.0-thinking-latest": { + id: "ernie-5.0-thinking-latest", + name: "Ernie 5.0 Thinking", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-11-18", + last_updated: "2025-11-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 2 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "Llama-3.3-70B-Incandescent-Malevolence": { + id: "Llama-3.3-70B-Incandescent-Malevolence", + name: "Llama 3.3 70B Incandescent Malevolence", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "Llama-3.3-70B-Damascus-R1": { + id: "Llama-3.3-70B-Damascus-R1", + name: "Damascus R1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "Gemma-3-27B-Nidum-Uncensored": { + id: "Gemma-3-27B-Nidum-Uncensored", + name: "Gemma 3 27B Nidum Uncensored", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-08", + last_updated: "2025-08-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 96000 }, + }, + "gemini-2.5-flash-lite-preview-09-2025-thinking": { + id: "gemini-2.5-flash-lite-preview-09-2025-thinking", + name: "Gemini 2.5 Flash Lite Preview (09/2025) – Thinking", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 1048756, input: 1048756, output: 65536 }, + }, + "doubao-seed-2-0-pro-260215": { + id: "doubao-seed-2-0-pro-260215", + name: "Doubao Seed 2.0 Pro", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-02-14", + last_updated: "2026-02-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.782, output: 3.876 }, + limit: { context: 256000, input: 256000, output: 128000 }, + }, + "gemini-3-pro-image-preview": { + id: "gemini-3-pro-image-preview", + name: "Gemini 3 Pro Image", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-11-18", + last_updated: "2025-11-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12 }, + limit: { context: 1048756, input: 1048756, output: 65536 }, + }, + "Gemma-3-27B-CardProjector-v4": { + id: "Gemma-3-27B-CardProjector-v4", + name: "Gemma 3 27B CardProjector v4", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-03-10", + last_updated: "2025-03-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "jamba-mini-1.7": { + id: "jamba-mini-1.7", + name: "Jamba Mini 1.7", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1989, output: 0.408 }, + limit: { context: 256000, input: 256000, output: 4096 }, + }, + "Llama-3.3-70B-Forgotten-Safeword-3.6": { + id: "Llama-3.3-70B-Forgotten-Safeword-3.6", + name: "Llama 3.3 70B Forgotten Safeword 3.6", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "doubao-1-5-thinking-pro-vision-250415": { + id: "doubao-1-5-thinking-pro-vision-250415", + name: "Doubao 1.5 Thinking Pro Vision", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-15", + last_updated: "2025-04-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 2.4 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "gemini-2.5-pro-preview-06-05": { + id: "gemini-2.5-pro-preview-06-05", + name: "Gemini 2.5 Pro Preview 0605", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-06-05", + last_updated: "2025-06-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10 }, + limit: { context: 1048756, input: 1048756, output: 65536 }, + }, + "gemini-2.0-pro-reasoner": { + id: "gemini-2.0-pro-reasoner", + name: "Gemini 2.0 Pro Reasoner", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-05", + last_updated: "2025-02-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.292, output: 4.998 }, + limit: { context: 128000, input: 128000, output: 65536 }, + }, + "doubao-seed-2-0-lite-260215": { + id: "doubao-seed-2-0-lite-260215", + name: "Doubao Seed 2.0 Lite", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-02-14", + last_updated: "2026-02-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1462, output: 0.8738 }, + limit: { context: 256000, input: 256000, output: 32000 }, + }, + "gemini-2.5-flash-lite-preview-06-17": { + id: "gemini-2.5-flash-lite-preview-06-17", + name: "Gemini 2.5 Flash Lite Preview", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 1048756, input: 1048756, output: 65536 }, + }, + "sonar-deep-research": { + id: "sonar-deep-research", + name: "Perplexity Deep Research", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-25", + last_updated: "2025-02-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3.4, output: 13.6 }, + limit: { context: 60000, input: 60000, output: 128000 }, + }, + "Gemma-3-27B-it": { + id: "Gemma-3-27B-it", + name: "Gemma 3 27B IT", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-03-10", + last_updated: "2025-03-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "Llama-3.3-70B-GeneticLemonade-Unleashed-v3": { + id: "Llama-3.3-70B-GeneticLemonade-Unleashed-v3", + name: "Llama 3.3 70B GeneticLemonade Unleashed v3", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "Gemma-3-27B-Glitter": { + id: "Gemma-3-27B-Glitter", + name: "Gemma 3 27B Glitter", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-03-10", + last_updated: "2025-03-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "Llama-3.3-70B-The-Omega-Directive-Unslop-v2.1": { + id: "Llama-3.3-70B-The-Omega-Directive-Unslop-v2.1", + name: "Llama 3.3 70B Omega Directive Unslop v2.1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "qwen3-30b-a3b-instruct-2507": { + id: "qwen3-30b-a3b-instruct-2507", + name: "Qwen3 30B A3B Instruct 2507", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-20", + last_updated: "2025-02-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5 }, + limit: { context: 256000, input: 256000, output: 32768 }, + }, + "gemini-2.5-flash-preview-09-2025-thinking": { + id: "gemini-2.5-flash-preview-09-2025-thinking", + name: "Gemini 2.5 Flash Preview (09/2025) – Thinking", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5 }, + limit: { context: 1048756, input: 1048756, output: 65536 }, + }, + "gemini-2.5-flash": { + id: "gemini-2.5-flash", + name: "Gemini 2.5 Flash", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-06-05", + last_updated: "2025-06-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5 }, + limit: { context: 1048756, input: 1048756, output: 65536 }, + }, + deepclaude: { + id: "deepclaude", + name: "DeepClaude", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-01", + last_updated: "2025-02-01", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 128000, input: 128000, output: 8192 }, + }, + "ernie-4.5-8k-preview": { + id: "ernie-4.5-8k-preview", + name: "Ernie 4.5 8k Preview", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-03-25", + last_updated: "2025-03-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.66, output: 2.6 }, + limit: { context: 8000, input: 8000, output: 16384 }, + }, + "doubao-seed-2-0-mini-260215": { + id: "doubao-seed-2-0-mini-260215", + name: "Doubao Seed 2.0 Mini", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-02-14", + last_updated: "2026-02-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.0493, output: 0.4845 }, + limit: { context: 256000, input: 256000, output: 32000 }, + }, + "gemini-3-pro-preview-thinking": { + id: "gemini-3-pro-preview-thinking", + name: "Gemini 3 Pro Thinking", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-11-18", + last_updated: "2025-11-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12 }, + limit: { context: 1048756, input: 1048756, output: 65536 }, + }, + "Llama-3.3-70B-GeneticLemonade-Opus": { + id: "Llama-3.3-70B-GeneticLemonade-Opus", + name: "Llama 3.3 70B GeneticLemonade Opus", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "v0-1.5-lg": { + id: "v0-1.5-lg", + name: "v0 1.5 LG", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-04", + last_updated: "2025-07-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75 }, + limit: { context: 1000000, input: 1000000, output: 64000 }, + }, + "ernie-4.5-turbo-128k": { + id: "ernie-4.5-turbo-128k", + name: "Ernie 4.5 Turbo 128k", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-05-08", + last_updated: "2025-05-08", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.132, output: 0.55 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "KAT-Coder-Pro-V1": { + id: "KAT-Coder-Pro-V1", + name: "KAT Coder Pro V1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-10-28", + last_updated: "2025-10-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.5, output: 6 }, + limit: { context: 256000, input: 256000, output: 32768 }, + }, + "claude-3-5-sonnet-20240620": { + id: "claude-3-5-sonnet-20240620", + name: "Claude 3.5 Sonnet Old", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2024-06-20", + last_updated: "2024-06-20", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.992, output: 14.994 }, + limit: { context: 200000, input: 200000, output: 8192 }, + }, + "claude-opus-4-1-thinking:8192": { + id: "claude-opus-4-1-thinking:8192", + name: "Claude 4.1 Opus Thinking (8K)", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 14.994, output: 75.004 }, + limit: { context: 200000, input: 200000, output: 32000 }, + }, + "gemini-2.0-flash-exp-image-generation": { + id: "gemini-2.0-flash-exp-image-generation", + name: "Gemini Text + Image", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-19", + last_updated: "2025-02-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.8 }, + limit: { context: 32767, input: 32767, output: 8192 }, + }, + "Llama-3.3-70B-Magnum-v4-SE": { + id: "Llama-3.3-70B-Magnum-v4-SE", + name: "Llama 3.3 70B Magnum v4 SE", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "glm-zero-preview": { + id: "glm-zero-preview", + name: "GLM Zero Preview", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.802, output: 1.802 }, + limit: { context: 8000, input: 8000, output: 4096 }, + }, + "study_gpt-chatgpt-4o-latest": { + id: "study_gpt-chatgpt-4o-latest", + name: "Study Mode", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-05-13", + last_updated: "2024-05-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 4.998, output: 14.994 }, + limit: { context: 200000, input: 200000, output: 16384 }, + }, + "glm-4-airx": { + id: "glm-4-airx", + name: "GLM-4 AirX", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-06-05", + last_updated: "2024-06-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.006, output: 2.006 }, + limit: { context: 8000, input: 8000, output: 4096 }, + }, + "step-2-mini": { + id: "step-2-mini", + name: "Step-2 Mini", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-05", + last_updated: "2024-07-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2006, output: 0.408 }, + limit: { context: 8000, input: 8000, output: 4096 }, + }, + "gemini-2.5-flash-preview-04-17:thinking": { + id: "gemini-2.5-flash-preview-04-17:thinking", + name: "Gemini 2.5 Flash Preview Thinking", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-04-17", + last_updated: "2025-04-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 3.5 }, + limit: { context: 1048756, input: 1048756, output: 65536 }, + }, + "Llama-3.3-70B-Mokume-Gane-R1": { + id: "Llama-3.3-70B-Mokume-Gane-R1", + name: "Llama 3.3 70B Mokume Gane R1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "deepseek-reasoner": { + id: "deepseek-reasoner", + name: "DeepSeek Reasoner", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.7 }, + limit: { context: 64000, input: 64000, output: 65536 }, + }, + "glm-z1-airx": { + id: "glm-z1-airx", + name: "GLM Z1 AirX", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-04-15", + last_updated: "2025-04-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.7, output: 0.7 }, + limit: { context: 32000, input: 32000, output: 16384 }, + }, + "jamba-mini-1.6": { + id: "jamba-mini-1.6", + name: "Jamba Mini 1.6", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-03-01", + last_updated: "2025-03-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1989, output: 0.408 }, + limit: { context: 256000, input: 256000, output: 4096 }, + }, + "claude-opus-4-1-thinking": { + id: "claude-opus-4-1-thinking", + name: "Claude 4.1 Opus Thinking", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 14.994, output: 75.004 }, + limit: { context: 200000, input: 200000, output: 32000 }, + }, + "grok-3-beta": { + id: "grok-3-beta", + name: "Grok 3 Beta", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 131072, input: 131072, output: 131072 }, + }, + "Llama-3.3-70B-Legion-V2.1": { + id: "Llama-3.3-70B-Legion-V2.1", + name: "Llama 3.3 70B Legion V2.1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + sonar: { + id: "sonar", + name: "Perplexity Simple", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-19", + last_updated: "2025-02-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.003, output: 1.003 }, + limit: { context: 127000, input: 127000, output: 128000 }, + }, + "z-image-turbo": { + id: "z-image-turbo", + name: "Z Image Turbo", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-11-27", + last_updated: "2025-11-27", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 0, output: 0 }, + }, + "GLM-4.5-Air-Derestricted-Iceblink-v2": { + id: "GLM-4.5-Air-Derestricted-Iceblink-v2", + name: "GLM 4.5 Air Derestricted Iceblink v2", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 158600, input: 158600, output: 65536 }, + }, + "jamba-large": { + id: "jamba-large", + name: "Jamba Large", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.989, output: 7.99 }, + limit: { context: 256000, input: 256000, output: 4096 }, + }, + "claude-3-7-sonnet-reasoner": { + id: "claude-3-7-sonnet-reasoner", + name: "Claude 3.7 Sonnet Reasoner", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-03-29", + last_updated: "2025-03-29", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 128000, input: 128000, output: 8192 }, + }, + "ernie-4.5-turbo-vl-32k": { + id: "ernie-4.5-turbo-vl-32k", + name: "Ernie 4.5 Turbo VL 32k", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-05-08", + last_updated: "2025-05-08", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.495, output: 1.43 }, + limit: { context: 32000, input: 32000, output: 16384 }, + }, + "Mistral-Nemo-12B-Instruct-2407": { + id: "Mistral-Nemo-12B-Instruct-2407", + name: "Mistral Nemo 12B Instruct 2407", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.01, output: 0.01 }, + limit: { context: 16384, input: 16384, output: 16384 }, + }, + "doubao-seed-1-6-flash-250615": { + id: "doubao-seed-1-6-flash-250615", + name: "Doubao Seed 1.6 Flash", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-06-15", + last_updated: "2025-06-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.0374, output: 0.374 }, + limit: { context: 256000, input: 256000, output: 16384 }, + }, + "qwq-32b": { + id: "qwq-32b", + name: "Qwen: QwQ 32B", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-15", + last_updated: "2025-04-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25599999, output: 0.30499999 }, + limit: { context: 128000, input: 128000, output: 32768 }, + }, + "Llama-3.3-70B-Strawberrylemonade-v1.2": { + id: "Llama-3.3-70B-Strawberrylemonade-v1.2", + name: "Llama 3.3 70B StrawberryLemonade v1.2", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "gemini-2.5-flash-preview-04-17": { + id: "gemini-2.5-flash-preview-04-17", + name: "Gemini 2.5 Flash Preview", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-04-17", + last_updated: "2025-04-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 1048756, input: 1048756, output: 65536 }, + }, + "ernie-x1-turbo-32k": { + id: "ernie-x1-turbo-32k", + name: "Ernie X1 Turbo 32k", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-05-08", + last_updated: "2025-05-08", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.165, output: 0.66 }, + limit: { context: 32000, input: 32000, output: 16384 }, + }, + "deepseek-math-v2": { + id: "deepseek-math-v2", + name: "DeepSeek Math V2", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-03", + last_updated: "2025-12-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 2.2 }, + limit: { context: 128000, input: 128000, output: 65536 }, + }, + "Llama-3.3-70B-Electranova-v1.0": { + id: "Llama-3.3-70B-Electranova-v1.0", + name: "Llama 3.3 70B Electranova v1.0", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "Llama-3.3-70B-ArliAI-RPMax-v2": { + id: "Llama-3.3-70B-ArliAI-RPMax-v2", + name: "Llama 3.3 70B ArliAI RPMax v2", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-08", + last_updated: "2025-08-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "qwen-image": { + id: "qwen-image", + name: "Qwen Image", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["image"] }, + open_weights: false, + limit: { context: 0, output: 0 }, + }, + "Llama-3.3-70B-Cu-Mai-R1": { + id: "Llama-3.3-70B-Cu-Mai-R1", + name: "Llama 3.3 70B Cu Mai R1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "GLM-4.5-Air-Derestricted-Iceblink": { + id: "GLM-4.5-Air-Derestricted-Iceblink", + name: "GLM 4.5 Air Derestricted Iceblink", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 131072, input: 131072, output: 98304 }, + }, + "Llama-3.3-70B-Bigger-Body": { + id: "Llama-3.3-70B-Bigger-Body", + name: "Llama 3.3 70B Bigger Body", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "Llama-3.3+(3.1v3.3)-70B-Hanami-x1": { + id: "Llama-3.3+(3.1v3.3)-70B-Hanami-x1", + name: "Llama 3.3+ 70B Hanami x1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "hunyuan-turbos-20250226": { + id: "hunyuan-turbos-20250226", + name: "Hunyuan Turbo S", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-27", + last_updated: "2025-02-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.187, output: 0.374 }, + limit: { context: 24000, input: 24000, output: 8192 }, + }, + "gemini-2.5-flash-preview-09-2025": { + id: "gemini-2.5-flash-preview-09-2025", + name: "Gemini 2.5 Flash Preview (09/2025)", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5 }, + limit: { context: 1048756, input: 1048756, output: 65536 }, + }, + "GLM-4.6-Derestricted-v5": { + id: "GLM-4.6-Derestricted-v5", + name: "GLM 4.6 Derestricted v5", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.5 }, + limit: { context: 131072, input: 131072, output: 8192 }, + }, + "glm-4-plus": { + id: "glm-4-plus", + name: "GLM-4 Plus", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-08-01", + last_updated: "2024-08-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 7.497, output: 7.497 }, + limit: { context: 128000, input: 128000, output: 4096 }, + }, + "Gemma-3-27B-Big-Tiger-v3": { + id: "Gemma-3-27B-Big-Tiger-v3", + name: "Gemma 3 27B Big Tiger v3", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-08", + last_updated: "2025-08-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "brave-research": { + id: "brave-research", + name: "Brave (Research)", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2023-03-02", + last_updated: "2024-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 5 }, + limit: { context: 16384, input: 16384, output: 16384 }, + }, + hidream: { + id: "hidream", + name: "Hidream", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2024-01-01", + last_updated: "2024-01-01", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 0, output: 0 }, + }, + "qwen3-max-2026-01-23": { + id: "qwen3-max-2026-01-23", + name: "Qwen3 Max 2026-01-23", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-01-26", + last_updated: "2026-01-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.2002, output: 6.001 }, + limit: { context: 256000, input: 256000, output: 32768 }, + }, + "claude-opus-4-1-20250805": { + id: "claude-opus-4-1-20250805", + name: "Claude 4.1 Opus", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 14.994, output: 75.004 }, + limit: { context: 200000, input: 200000, output: 32000 }, + }, + "claude-haiku-4-5-20251001": { + id: "claude-haiku-4-5-20251001", + name: "Claude Haiku 4.5", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5 }, + limit: { context: 200000, input: 200000, output: 64000 }, + }, + "MiniMax-M1": { + id: "MiniMax-M1", + name: "MiniMax M1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-06-16", + last_updated: "2025-06-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1394, output: 1.3328 }, + limit: { context: 1000000, input: 1000000, output: 131072 }, + }, + "gemini-2.5-flash-nothinking": { + id: "gemini-2.5-flash-nothinking", + name: "Gemini 2.5 Flash (No Thinking)", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-06-05", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5 }, + limit: { context: 1048756, input: 1048756, output: 65536 }, + }, + "exa-research-pro": { + id: "exa-research-pro", + name: "Exa (Research Pro)", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-06-04", + last_updated: "2025-06-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 2.5 }, + limit: { context: 16384, input: 16384, output: 16384 }, + }, + "grok-3-fast-beta": { + id: "grok-3-fast-beta", + name: "Grok 3 Fast Beta", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25 }, + limit: { context: 131072, input: 131072, output: 131072 }, + }, + "claude-opus-4-5-20251101:thinking": { + id: "claude-opus-4-5-20251101:thinking", + name: "Claude 4.5 Opus Thinking", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-11-01", + last_updated: "2025-11-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 4.998, output: 25.007 }, + limit: { context: 200000, input: 200000, output: 32000 }, + }, + "gemini-2.5-pro-exp-03-25": { + id: "gemini-2.5-pro-exp-03-25", + name: "Gemini 2.5 Pro Experimental 0325", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-03-25", + last_updated: "2025-03-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10 }, + limit: { context: 1048756, input: 1048756, output: 65536 }, + }, + "claude-3-7-sonnet-thinking": { + id: "claude-3-7-sonnet-thinking", + name: "Claude 3.7 Sonnet Thinking", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-02-24", + last_updated: "2025-02-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.992, output: 14.994 }, + limit: { context: 200000, input: 200000, output: 16000 }, + }, + "claude-opus-4-thinking:8192": { + id: "claude-opus-4-thinking:8192", + name: "Claude 4 Opus Thinking (8K)", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 14.994, output: 75.004 }, + limit: { context: 200000, input: 200000, output: 32000 }, + }, + "claude-sonnet-4-thinking:1024": { + id: "claude-sonnet-4-thinking:1024", + name: "Claude 4 Sonnet Thinking (1K)", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.992, output: 14.994 }, + limit: { context: 1000000, input: 1000000, output: 64000 }, + }, + "Llama-3.3-70B-Magnum-v4-SE-Cirrus-x1-SLERP": { + id: "Llama-3.3-70B-Magnum-v4-SE-Cirrus-x1-SLERP", + name: "Llama 3.3 70B Magnum v4 SE Cirrus x1 SLERP", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-26", + last_updated: "2025-07-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "step-r1-v-mini": { + id: "step-r1-v-mini", + name: "Step R1 V Mini", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-08", + last_updated: "2025-04-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 11 }, + limit: { context: 128000, input: 128000, output: 65536 }, + }, + "ernie-x1-32k-preview": { + id: "ernie-x1-32k-preview", + name: "Ernie X1 32k", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.33, output: 1.32 }, + limit: { context: 32000, input: 32000, output: 16384 }, + }, + "Llama-3.3-70B-StrawberryLemonade-v1.0": { + id: "Llama-3.3-70B-StrawberryLemonade-v1.0", + name: "Llama 3.3 70B StrawberryLemonade v1.0", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "KAT-Coder-Exp-72B-1010": { + id: "KAT-Coder-Exp-72B-1010", + name: "KAT Coder Exp 72B 1010", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-10-28", + last_updated: "2025-10-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.2 }, + limit: { context: 128000, input: 128000, output: 32768 }, + }, + "gemini-2.5-pro-preview-03-25": { + id: "gemini-2.5-pro-preview-03-25", + name: "Gemini 2.5 Pro Preview 0325", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-03-25", + last_updated: "2025-03-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10 }, + limit: { context: 1048756, input: 1048756, output: 65536 }, + }, + "claude-opus-4-thinking:1024": { + id: "claude-opus-4-thinking:1024", + name: "Claude 4 Opus Thinking (1K)", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 14.994, output: 75.004 }, + limit: { context: 200000, input: 200000, output: 32000 }, + }, + "claude-sonnet-4-20250514": { + id: "claude-sonnet-4-20250514", + name: "Claude 4 Sonnet", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.992, output: 14.994 }, + limit: { context: 200000, input: 200000, output: 64000 }, + }, + "Llama-3.3-70B-Progenitor-V3.3": { + id: "Llama-3.3-70B-Progenitor-V3.3", + name: "Llama 3.3 70B Progenitor V3.3", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-26", + last_updated: "2025-07-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "Qwen2.5-32B-EVA-v0.2": { + id: "Qwen2.5-32B-EVA-v0.2", + name: "Qwen 2.5 32b EVA", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-09-01", + last_updated: "2024-09-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.493, output: 0.493 }, + limit: { context: 24576, input: 24576, output: 8192 }, + }, + "brave-pro": { + id: "brave-pro", + name: "Brave (Pro)", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2023-03-02", + last_updated: "2024-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 5 }, + limit: { context: 8192, input: 8192, output: 8192 }, + }, + "step-2-16k-exp": { + id: "step-2-16k-exp", + name: "Step-2 16k Exp", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-05", + last_updated: "2024-07-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 7.004, output: 19.992 }, + limit: { context: 16000, input: 16000, output: 8192 }, + }, + "Llama-3.3-70B-Fallen-R1-v1": { + id: "Llama-3.3-70B-Fallen-R1-v1", + name: "Llama 3.3 70B Fallen R1 v1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "claude-sonnet-4-thinking": { + id: "claude-sonnet-4-thinking", + name: "Claude 4 Sonnet Thinking", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-02-24", + last_updated: "2025-02-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.992, output: 14.994 }, + limit: { context: 1000000, input: 1000000, output: 64000 }, + }, + "doubao-1.5-pro-256k": { + id: "doubao-1.5-pro-256k", + name: "Doubao 1.5 Pro 256k", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-03-12", + last_updated: "2025-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.799, output: 1.445 }, + limit: { context: 256000, input: 256000, output: 16384 }, + }, + "claude-3-7-sonnet-20250219": { + id: "claude-3-7-sonnet-20250219", + name: "Claude 3.7 Sonnet", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-02-19", + last_updated: "2025-02-19", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.992, output: 14.994 }, + limit: { context: 200000, input: 200000, output: 16000 }, + }, + "learnlm-1.5-pro-experimental": { + id: "learnlm-1.5-pro-experimental", + name: "Gemini LearnLM Experimental", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-05-14", + last_updated: "2024-05-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3.502, output: 10.506 }, + limit: { context: 32767, input: 32767, output: 8192 }, + }, + "qwen3-coder-30b-a3b-instruct": { + id: "qwen3-coder-30b-a3b-instruct", + name: "Qwen3 Coder 30B A3B Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 128000, input: 128000, output: 65536 }, + }, + chroma: { + id: "chroma", + name: "Chroma", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-08-12", + last_updated: "2025-08-12", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 0, output: 0 }, + }, + "Llama-3.3-70B-Predatorial-Extasy": { + id: "Llama-3.3-70B-Predatorial-Extasy", + name: "Llama 3.3 70B Predatorial Extasy", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "Llama-3.3-70B-Aurora-Borealis": { + id: "Llama-3.3-70B-Aurora-Borealis", + name: "Llama 3.3 70B Aurora Borealis", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "Llama-3.3-70B-ArliAI-RPMax-v3": { + id: "Llama-3.3-70B-ArliAI-RPMax-v3", + name: "Llama 3.3 70B ArliAI RPMax v3", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "venice-uncensored": { + id: "venice-uncensored", + name: "Venice Uncensored", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-24", + last_updated: "2025-02-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 0.4 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "step-3": { + id: "step-3", + name: "Step-3", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-31", + last_updated: "2025-07-31", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2499, output: 0.6494 }, + limit: { context: 65536, input: 65536, output: 8192 }, + }, + "Llama-3.3-70B-The-Omega-Directive-Unslop-v2.0": { + id: "Llama-3.3-70B-The-Omega-Directive-Unslop-v2.0", + name: "Llama 3.3 70B Omega Directive Unslop v2.0", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "auto-model": { + id: "auto-model", + name: "Auto model", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-06-01", + last_updated: "2024-06-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 1000000, input: 1000000, output: 1000000 }, + }, + "claude-opus-4-1-thinking:32768": { + id: "claude-opus-4-1-thinking:32768", + name: "Claude 4.1 Opus Thinking (32K)", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 14.994, output: 75.004 }, + limit: { context: 200000, input: 200000, output: 32000 }, + }, + "Llama-3.3-70B-Shakudo": { + id: "Llama-3.3-70B-Shakudo", + name: "Llama 3.3 70B Shakudo", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "Baichuan4-Air": { + id: "Baichuan4-Air", + name: "Baichuan 4 Air", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-19", + last_updated: "2025-08-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.157, output: 0.157 }, + limit: { context: 32768, input: 32768, output: 32768 }, + }, + "kimi-thinking-preview": { + id: "kimi-thinking-preview", + name: "Kimi Thinking Preview", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-05-07", + last_updated: "2025-05-07", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 31.46, output: 31.46 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "qwen-turbo": { + id: "qwen-turbo", + name: "Qwen Turbo", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-11-01", + last_updated: "2024-11-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.04998, output: 0.2006 }, + limit: { context: 1000000, input: 1000000, output: 8192 }, + }, + "Llama-3.3-70B-Mhnnn-x1": { + id: "Llama-3.3-70B-Mhnnn-x1", + name: "Llama 3.3 70B Mhnnn x1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "claude-opus-4-thinking:32768": { + id: "claude-opus-4-thinking:32768", + name: "Claude 4 Opus Thinking (32K)", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 14.994, output: 75.004 }, + limit: { context: 200000, input: 200000, output: 32000 }, + }, + "Llama-3.3-70B-Argunaut-1-SFT": { + id: "Llama-3.3-70B-Argunaut-1-SFT", + name: "Llama 3.3 70B Argunaut 1 SFT", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "claude-opus-4-1-thinking:1024": { + id: "claude-opus-4-1-thinking:1024", + name: "Claude 4.1 Opus Thinking (1K)", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 14.994, output: 75.004 }, + limit: { context: 200000, input: 200000, output: 32000 }, + }, + "gemini-2.5-flash-lite": { + id: "gemini-2.5-flash-lite", + name: "Gemini 2.5 Flash Lite", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 1048756, input: 1048756, output: 65536 }, + }, + "phi-4-multimodal-instruct": { + id: "phi-4-multimodal-instruct", + name: "Phi 4 Multimodal", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-26", + last_updated: "2025-07-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.07, output: 0.11 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "doubao-seed-2-0-code-preview-260215": { + id: "doubao-seed-2-0-code-preview-260215", + name: "Doubao Seed 2.0 Code Preview", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-02-14", + last_updated: "2026-02-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.782, output: 3.893 }, + limit: { context: 256000, input: 256000, output: 128000 }, + }, + "deepseek-reasoner-cheaper": { + id: "deepseek-reasoner-cheaper", + name: "Deepseek R1 Cheaper", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.7 }, + limit: { context: 128000, input: 128000, output: 65536 }, + }, + "exa-answer": { + id: "exa-answer", + name: "Exa (Answer)", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-06-04", + last_updated: "2025-06-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 2.5 }, + limit: { context: 4096, input: 4096, output: 4096 }, + }, + "v0-1.0-md": { + id: "v0-1.0-md", + name: "v0 1.0 MD", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-04", + last_updated: "2025-07-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 200000, input: 200000, output: 64000 }, + }, + "glm-4.1v-thinking-flash": { + id: "glm-4.1v-thinking-flash", + name: "GLM 4.1V Thinking Flash", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.3 }, + limit: { context: 64000, input: 64000, output: 8192 }, + }, + "azure-o1": { + id: "azure-o1", + name: "Azure o1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-17", + last_updated: "2024-12-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 14.994, output: 59.993 }, + limit: { context: 200000, input: 200000, output: 100000 }, + }, + "GLM-4.5-Air-Derestricted": { + id: "GLM-4.5-Air-Derestricted", + name: "GLM 4.5 Air Derestricted", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 202600, input: 202600, output: 98304 }, + }, + "azure-o3-mini": { + id: "azure-o3-mini", + name: "Azure o3-mini", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-01-31", + last_updated: "2025-01-31", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.088, output: 4.3996 }, + limit: { context: 200000, input: 200000, output: 65536 }, + }, + "qwen3.6-max-preview": { + id: "qwen3.6-max-preview", + name: "Qwen3.6 Max Preview", + family: "qwen3.6", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-04-20", + last_updated: "2026-04-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.3, output: 7.8 }, + limit: { context: 245800, output: 65536 }, + }, + "Llama-3.3-70B-Sapphira-0.2": { + id: "Llama-3.3-70B-Sapphira-0.2", + name: "Llama 3.3 70B Sapphira 0.2", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "Llama-3.3-70B-Anthrobomination": { + id: "Llama-3.3-70B-Anthrobomination", + name: "Llama 3.3 70B Anthrobomination", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "QwQ-32B-ArliAI-RpR-v1": { + id: "QwQ-32B-ArliAI-RpR-v1", + name: "QwQ 32b Arli V1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 32768, input: 32768, output: 32768 }, + }, + "claude-opus-4-20250514": { + id: "claude-opus-4-20250514", + name: "Claude 4 Opus", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-05-14", + last_updated: "2025-05-14", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 14.994, output: 75.004 }, + limit: { context: 200000, input: 200000, output: 32000 }, + }, + "yi-lightning": { + id: "yi-lightning", + name: "Yi Lightning", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-10-16", + last_updated: "2024-10-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2006, output: 0.2006 }, + limit: { context: 12000, input: 12000, output: 4096 }, + }, + "Llama-3.3-70B-Electra-R1": { + id: "Llama-3.3-70B-Electra-R1", + name: "Llama 3.3 70B Electra R1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "Llama-3.3-70B-Forgotten-Abomination-v5.0": { + id: "Llama-3.3-70B-Forgotten-Abomination-v5.0", + name: "Llama 3.3 70B Forgotten Abomination v5.0", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "Llama-3.3-70B-Cirrus-x1": { + id: "Llama-3.3-70B-Cirrus-x1", + name: "Llama 3.3 70B Cirrus x1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "grok-3-mini-beta": { + id: "grok-3-mini-beta", + name: "Grok 3 Mini Beta", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.5 }, + limit: { context: 131072, input: 131072, output: 131072 }, + }, + "auto-model-standard": { + id: "auto-model-standard", + name: "Auto model (Standard)", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-06-01", + last_updated: "2024-06-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 9.996, output: 19.992 }, + limit: { context: 1000000, input: 1000000, output: 1000000 }, + }, + "claude-sonnet-4-5-20250929-thinking": { + id: "claude-sonnet-4-5-20250929-thinking", + name: "Claude Sonnet 4.5 Thinking", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.992, output: 14.994 }, + limit: { context: 1000000, input: 1000000, output: 64000 }, + }, + "v0-1.5-md": { + id: "v0-1.5-md", + name: "v0 1.5 MD", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-04", + last_updated: "2025-07-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 200000, input: 200000, output: 64000 }, + }, + "kimi-k2-instruct-fast": { + id: "kimi-k2-instruct-fast", + name: "Kimi K2 0711 Fast", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-15", + last_updated: "2025-07-15", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 2 }, + limit: { context: 131072, input: 131072, output: 16384 }, + }, + "glm-4-long": { + id: "glm-4-long", + name: "GLM-4 Long", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-08-01", + last_updated: "2024-08-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2006, output: 0.2006 }, + limit: { context: 1000000, input: 1000000, output: 4096 }, + }, + "jamba-large-1.7": { + id: "jamba-large-1.7", + name: "Jamba Large 1.7", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.989, output: 7.99 }, + limit: { context: 256000, input: 256000, output: 4096 }, + }, + "qvq-max": { + id: "qvq-max", + name: "Qwen: QvQ Max", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-03-28", + last_updated: "2025-03-28", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.4, output: 5.3 }, + limit: { context: 128000, input: 128000, output: 8192 }, + }, + "gemini-2.0-flash-thinking-exp-1219": { + id: "gemini-2.0-flash-thinking-exp-1219", + name: "Gemini 2.0 Flash Thinking 1219", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-19", + last_updated: "2024-12-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1003, output: 0.408 }, + limit: { context: 32767, input: 32767, output: 8192 }, + }, + "gemini-2.0-flash-lite": { + id: "gemini-2.0-flash-lite", + name: "Gemini 2.0 Flash Lite", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.0748, output: 0.306 }, + limit: { context: 1000000, input: 1000000, output: 8192 }, + }, + "azure-gpt-4-turbo": { + id: "azure-gpt-4-turbo", + name: "Azure gpt-4-turbo", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2023-11-06", + last_updated: "2024-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 9.996, output: 30.005 }, + limit: { context: 128000, input: 128000, output: 4096 }, + }, + "Baichuan-M2": { + id: "Baichuan-M2", + name: "Baichuan M2 32B Medical", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-19", + last_updated: "2025-08-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 15.73, output: 15.73 }, + limit: { context: 32768, input: 32768, output: 32768 }, + }, + "qwen-long": { + id: "qwen-long", + name: "Qwen Long 10M", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-01-25", + last_updated: "2025-01-25", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1003, output: 0.408 }, + limit: { context: 10000000, input: 10000000, output: 8192 }, + }, + "sonar-reasoning-pro": { + id: "sonar-reasoning-pro", + name: "Perplexity Reasoning Pro", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-02-19", + last_updated: "2025-02-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.006, output: 7.9985 }, + limit: { context: 127000, input: 127000, output: 128000 }, + }, + "gemini-2.5-flash-preview-05-20:thinking": { + id: "gemini-2.5-flash-preview-05-20:thinking", + name: "Gemini 2.5 Flash 0520 Thinking", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-05-20", + last_updated: "2025-05-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 3.5 }, + limit: { context: 1048000, input: 1048000, output: 65536 }, + }, + "GLM-4.5-Air-Derestricted-Steam-ReExtract": { + id: "GLM-4.5-Air-Derestricted-Steam-ReExtract", + name: "GLM 4.5 Air Derestricted Steam ReExtract", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-12", + last_updated: "2025-12-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 131072, input: 131072, output: 65536 }, + }, + "Llama-3.3-70B-Dark-Ages-v0.1": { + id: "Llama-3.3-70B-Dark-Ages-v0.1", + name: "Llama 3.3 70B Dark Ages v0.1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "Baichuan4-Turbo": { + id: "Baichuan4-Turbo", + name: "Baichuan 4 Turbo", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-19", + last_updated: "2025-08-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.42, output: 2.42 }, + limit: { context: 128000, input: 128000, output: 32768 }, + }, + "doubao-1.5-vision-pro-32k": { + id: "doubao-1.5-vision-pro-32k", + name: "Doubao 1.5 Vision Pro 32k", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-01-22", + last_updated: "2025-01-22", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.459, output: 1.377 }, + limit: { context: 32000, input: 32000, output: 8192 }, + }, + "alibaba/qwen3.6-flash": { + id: "alibaba/qwen3.6-flash", + name: "Qwen3.6 Flash", + family: "qwen3.6", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-04-17", + last_updated: "2026-04-17", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.19, output: 1.16 }, + limit: { context: 991800, output: 65536 }, + }, + "inflection/inflection-3-pi": { + id: "inflection/inflection-3-pi", + name: "Inflection 3 Pi", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-10-11", + last_updated: "2024-10-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.499, output: 9.996 }, + limit: { context: 8000, input: 8000, output: 4096 }, + }, + "inflection/inflection-3-productivity": { + id: "inflection/inflection-3-productivity", + name: "Inflection 3 Productivity", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-10-11", + last_updated: "2024-10-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.499, output: 9.996 }, + limit: { context: 8000, input: 8000, output: 4096 }, + }, + "essentialai/rnj-1-instruct": { + id: "essentialai/rnj-1-instruct", + name: "RNJ-1 Instruct 8B", + family: "rnj", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-13", + last_updated: "2025-12-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.15 }, + limit: { context: 128000, input: 128000, output: 8192 }, + }, + "LLM360/K2-Think": { + id: "LLM360/K2-Think", + name: "K2-Think", + family: "kimi-thinking", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-26", + last_updated: "2025-07-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.17, output: 0.68 }, + limit: { context: 128000, input: 128000, output: 32768 }, + }, + "TEE/kimi-k2.5": { + id: "TEE/kimi-k2.5", + name: "Kimi K2.5 TEE", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-01-29", + last_updated: "2026-01-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.9 }, + limit: { context: 128000, input: 128000, output: 65535 }, + }, + "TEE/glm-4.7": { + id: "TEE/glm-4.7", + name: "GLM 4.7 TEE", + family: "glm", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-01-29", + last_updated: "2026-01-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.85, output: 3.3 }, + limit: { context: 131000, input: 131000, output: 65535 }, + }, + "TEE/qwen3.5-397b-a17b": { + id: "TEE/qwen3.5-397b-a17b", + name: "Qwen3.5 397B A17B TEE", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-02-28", + last_updated: "2026-02-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 3.6 }, + limit: { context: 258048, input: 258048, output: 65536 }, + }, + "TEE/glm-5": { + id: "TEE/glm-5", + name: "GLM 5 TEE", + family: "glm", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.2, output: 3.5 }, + limit: { context: 203000, input: 203000, output: 65535 }, + }, + "TEE/qwen2.5-vl-72b-instruct": { + id: "TEE/qwen2.5-vl-72b-instruct", + name: "Qwen2.5 VL 72B TEE", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-01", + last_updated: "2025-02-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.7, output: 0.7 }, + limit: { context: 65536, input: 65536, output: 8192 }, + }, + "TEE/minimax-m2.1": { + id: "TEE/minimax-m2.1", + name: "MiniMax M2.1 TEE", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 200000, input: 200000, output: 131072 }, + }, + "TEE/qwen3-30b-a3b-instruct-2507": { + id: "TEE/qwen3-30b-a3b-instruct-2507", + name: "Qwen3 30B A3B Instruct 2507 TEE", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-29", + last_updated: "2025-07-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.44999999999999996 }, + limit: { context: 262000, input: 262000, output: 32768 }, + }, + "TEE/deepseek-v3.1": { + id: "TEE/deepseek-v3.1", + name: "DeepSeek V3.1 TEE", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-21", + last_updated: "2025-08-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 2.5 }, + limit: { context: 164000, input: 164000, output: 8192 }, + }, + "TEE/llama3-3-70b": { + id: "TEE/llama3-3-70b", + name: "Llama 3.3 70B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-03", + last_updated: "2025-07-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 2 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "TEE/glm-4.6": { + id: "TEE/glm-4.6", + name: "GLM 4.6 TEE", + family: "glm", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.75, output: 2 }, + limit: { context: 203000, input: 203000, output: 65535 }, + }, + "TEE/kimi-k2.5-thinking": { + id: "TEE/kimi-k2.5-thinking", + name: "Kimi K2.5 Thinking TEE", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2026-01-29", + last_updated: "2026-01-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.9 }, + limit: { context: 128000, input: 128000, output: 65535 }, + }, + "TEE/gemma-3-27b-it": { + id: "TEE/gemma-3-27b-it", + name: "Gemma 3 27B TEE", + family: "gemma", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-03-10", + last_updated: "2025-03-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.8 }, + limit: { context: 131072, input: 131072, output: 8192 }, + }, + "TEE/deepseek-v3.2": { + id: "TEE/deepseek-v3.2", + name: "DeepSeek V3.2 TEE", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 1 }, + limit: { context: 164000, input: 164000, output: 65536 }, + }, + "TEE/gpt-oss-20b": { + id: "TEE/gpt-oss-20b", + name: "GPT-OSS 20B TEE", + family: "gpt-oss", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.8 }, + limit: { context: 131072, input: 131072, output: 8192 }, + }, + "TEE/qwen3-coder": { + id: "TEE/qwen3-coder", + name: "Qwen3 Coder 480B TEE", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.5, output: 2 }, + limit: { context: 128000, input: 128000, output: 32768 }, + }, + "TEE/glm-4.7-flash": { + id: "TEE/glm-4.7-flash", + name: "GLM 4.7 Flash TEE", + family: "glm-flash", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-01-19", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.5 }, + limit: { context: 203000, input: 203000, output: 65535 }, + }, + "TEE/gpt-oss-120b": { + id: "TEE/gpt-oss-120b", + name: "GPT-OSS 120B TEE", + family: "gpt-oss", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 2 }, + limit: { context: 131072, input: 131072, output: 16384 }, + }, + "TEE/deepseek-r1-0528": { + id: "TEE/deepseek-r1-0528", + name: "DeepSeek R1 0528 TEE", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-05-28", + last_updated: "2025-05-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 2 }, + limit: { context: 128000, input: 128000, output: 65536 }, + }, + "TEE/kimi-k2-thinking": { + id: "TEE/kimi-k2-thinking", + name: "Kimi K2 Thinking TEE", + family: "kimi-thinking", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-11-06", + last_updated: "2025-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 2 }, + limit: { context: 128000, input: 128000, output: 65535 }, + }, + "CrucibleLab/L3.3-70B-Loki-V2.0": { + id: "CrucibleLab/L3.3-70B-Loki-V2.0", + name: "L3.3 70B Loki v2.0", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-01-22", + last_updated: "2026-01-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.49299999999999994, output: 0.49299999999999994 }, + limit: { context: 16384, input: 16384, output: 16384 }, + }, + "deepseek/deepseek-v3.2:thinking": { + id: "deepseek/deepseek-v3.2:thinking", + name: "DeepSeek V3.2 Thinking", + family: "deepseek", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27999999999999997, output: 0.42000000000000004 }, + limit: { context: 163000, input: 163000, output: 65536 }, + }, + "deepseek/deepseek-prover-v2-671b": { + id: "deepseek/deepseek-prover-v2-671b", + name: "DeepSeek Prover v2 671B", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-30", + last_updated: "2025-04-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 2.5 }, + limit: { context: 160000, input: 160000, output: 16384 }, + }, + "deepseek/deepseek-v3.2-speciale": { + id: "deepseek/deepseek-v3.2-speciale", + name: "DeepSeek V3.2 Speciale", + family: "deepseek", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-12-02", + last_updated: "2025-12-02", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27999999999999997, output: 0.42000000000000004 }, + limit: { context: 163000, input: 163000, output: 65536 }, + }, + "deepseek/deepseek-v3.2": { + id: "deepseek/deepseek-v3.2", + name: "DeepSeek V3.2", + family: "deepseek", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27999999999999997, output: 0.42000000000000004 }, + limit: { context: 163000, input: 163000, output: 65536 }, + }, + "Doctor-Shotgun/MS3.2-24B-Magnum-Diamond": { + id: "Doctor-Shotgun/MS3.2-24B-Magnum-Diamond", + name: "MS3.2 24B Magnum Diamond", + family: "mistral", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-11-24", + last_updated: "2025-11-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.49299999999999994, output: 0.49299999999999994 }, + limit: { context: 16384, input: 16384, output: 32768 }, + }, + "NeverSleep/Llama-3-Lumimaid-70B-v0.1": { + id: "NeverSleep/Llama-3-Lumimaid-70B-v0.1", + name: "Lumimaid 70b", + family: "llama", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-01", + last_updated: "2024-07-01", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.006, output: 2.006 }, + limit: { context: 16384, input: 16384, output: 8192 }, + }, + "NeverSleep/Lumimaid-v0.2-70B": { + id: "NeverSleep/Lumimaid-v0.2-70B", + name: "Lumimaid v0.2", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-01", + last_updated: "2024-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 1.5 }, + limit: { context: 16384, input: 16384, output: 8192 }, + }, + "Steelskull/L3.3-Cu-Mai-R1-70b": { + id: "Steelskull/L3.3-Cu-Mai-R1-70b", + name: "Llama 3.3 70B Cu Mai", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.49299999999999994, output: 0.49299999999999994 }, + limit: { context: 16384, input: 16384, output: 16384 }, + }, + "Steelskull/L3.3-Nevoria-R1-70b": { + id: "Steelskull/L3.3-Nevoria-R1-70b", + name: "Steelskull Nevoria R1 70b", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.49299999999999994, output: 0.49299999999999994 }, + limit: { context: 16384, input: 16384, output: 16384 }, + }, + "Steelskull/L3.3-MS-Evayale-70B": { + id: "Steelskull/L3.3-MS-Evayale-70B", + name: "Evayale 70b ", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.49299999999999994, output: 0.49299999999999994 }, + limit: { context: 16384, input: 16384, output: 16384 }, + }, + "Steelskull/L3.3-Electra-R1-70b": { + id: "Steelskull/L3.3-Electra-R1-70b", + name: "Steelskull Electra R1 70b", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.69989, output: 0.69989 }, + limit: { context: 16384, input: 16384, output: 16384 }, + }, + "Steelskull/L3.3-MS-Nevoria-70b": { + id: "Steelskull/L3.3-MS-Nevoria-70b", + name: "Steelskull Nevoria 70b", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.49299999999999994, output: 0.49299999999999994 }, + limit: { context: 16384, input: 16384, output: 16384 }, + }, + "Steelskull/L3.3-MS-Evalebis-70b": { + id: "Steelskull/L3.3-MS-Evalebis-70b", + name: "MS Evalebis 70b", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.49299999999999994, output: 0.49299999999999994 }, + limit: { context: 16384, input: 16384, output: 16384 }, + }, + "miromind-ai/mirothinker-v1.5-235b": { + id: "miromind-ai/mirothinker-v1.5-235b", + name: "MiroThinker v1.5 235B", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-01-07", + last_updated: "2026-01-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 32768, input: 32768, output: 4000 }, + }, + "pamanseau/OpenReasoning-Nemotron-32B": { + id: "pamanseau/OpenReasoning-Nemotron-32B", + name: "OpenReasoning Nemotron 32B", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-08-21", + last_updated: "2025-08-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 32768, input: 32768, output: 65536 }, + }, + "arcee-ai/trinity-mini": { + id: "arcee-ai/trinity-mini", + name: "Trinity Mini", + family: "trinity-mini", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.045000000000000005, output: 0.15 }, + limit: { context: 131072, input: 131072, output: 8192 }, + }, + "arcee-ai/trinity-large": { + id: "arcee-ai/trinity-large", + name: "Trinity Large", + family: "trinity", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1 }, + limit: { context: 131072, input: 131072, output: 8192 }, + }, + "cognitivecomputations/dolphin-2.9.2-qwen2-72b": { + id: "cognitivecomputations/dolphin-2.9.2-qwen2-72b", + name: "Dolphin 72b", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-27", + last_updated: "2025-02-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.306 }, + limit: { context: 8192, input: 8192, output: 4096 }, + }, + "deepcogito/cogito-v1-preview-qwen-32B": { + id: "deepcogito/cogito-v1-preview-qwen-32B", + name: "Cogito v1 Preview Qwen 32B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-05-10", + last_updated: "2025-05-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.7999999999999998, output: 1.7999999999999998 }, + limit: { context: 128000, input: 128000, output: 32768 }, + }, + "deepcogito/cogito-v2.1-671b": { + id: "deepcogito/cogito-v2.1-671b", + name: "Cogito v2.1 671B MoE", + family: "cogito", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-11-19", + last_updated: "2025-11-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 1.25 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "Salesforce/Llama-xLAM-2-70b-fc-r": { + id: "Salesforce/Llama-xLAM-2-70b-fc-r", + name: "Llama-xLAM-2 70B fc-r", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-13", + last_updated: "2025-04-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 2.5 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "NousResearch 2/hermes-4-405b:thinking": { + id: "NousResearch 2/hermes-4-405b:thinking", + name: "Hermes 4 Large (Thinking)", + family: "nousresearch", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 128000, input: 128000, output: 8192 }, + }, + "NousResearch 2/DeepHermes-3-Mistral-24B-Preview": { + id: "NousResearch 2/DeepHermes-3-Mistral-24B-Preview", + name: "DeepHermes-3 Mistral 24B (Preview)", + family: "nousresearch", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-05-10", + last_updated: "2025-05-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.3 }, + limit: { context: 128000, input: 128000, output: 32768 }, + }, + "NousResearch 2/Hermes-4-70B:thinking": { + id: "NousResearch 2/Hermes-4-70B:thinking", + name: "Hermes 4 (Thinking)", + family: "nousresearch", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-09-17", + last_updated: "2025-09-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2006, output: 0.39949999999999997 }, + limit: { context: 128000, input: 128000, output: 8192 }, + }, + "NousResearch 2/hermes-4-405b": { + id: "NousResearch 2/hermes-4-405b", + name: "Hermes 4 Large", + family: "nousresearch", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-08-26", + last_updated: "2025-08-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 128000, input: 128000, output: 8192 }, + }, + "NousResearch 2/hermes-3-llama-3.1-70b": { + id: "NousResearch 2/hermes-3-llama-3.1-70b", + name: "Hermes 3 70B", + family: "nousresearch", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-01-07", + last_updated: "2026-01-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.408, output: 0.408 }, + limit: { context: 65536, input: 65536, output: 8192 }, + }, + "NousResearch 2/hermes-4-70b": { + id: "NousResearch 2/hermes-4-70b", + name: "Hermes 4 Medium", + family: "nousresearch", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-03", + last_updated: "2025-07-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2006, output: 0.39949999999999997 }, + limit: { context: 128000, input: 128000, output: 8192 }, + }, + "soob3123/Veiled-Calla-12B": { + id: "soob3123/Veiled-Calla-12B", + name: "Veiled Calla 12B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-13", + last_updated: "2025-04-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.3 }, + limit: { context: 32768, input: 32768, output: 8192 }, + }, + "soob3123/GrayLine-Qwen3-8B": { + id: "soob3123/GrayLine-Qwen3-8B", + name: "Grayline Qwen3 8B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.3 }, + limit: { context: 16384, input: 16384, output: 32768 }, + }, + "soob3123/amoral-gemma3-27B-v2": { + id: "soob3123/amoral-gemma3-27B-v2", + name: "Amoral Gemma3 27B v2", + family: "gemma", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-05-23", + last_updated: "2025-05-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.3 }, + limit: { context: 32768, input: 32768, output: 8192 }, + }, + "nex-agi/deepseek-v3.1-nex-n1": { + id: "nex-agi/deepseek-v3.1-nex-n1", + name: "DeepSeek V3.1 Nex N1", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-10", + last_updated: "2025-12-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27999999999999997, output: 0.42000000000000004 }, + limit: { context: 128000, input: 128000, output: 8192 }, + }, + "Envoid/Llama-3.05-NT-Storybreaker-Ministral-70B": { + id: "Envoid/Llama-3.05-NT-Storybreaker-Ministral-70B", + name: "Llama 3.05 Storybreaker Ministral 70b", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.49299999999999994, output: 0.49299999999999994 }, + limit: { context: 16384, input: 16384, output: 8192 }, + }, + "Envoid/Llama-3.05-Nemotron-Tenyxchat-Storybreaker-70B": { + id: "Envoid/Llama-3.05-Nemotron-Tenyxchat-Storybreaker-70B", + name: "Nemotron Tenyxchat Storybreaker 70b", + family: "nemotron", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.49299999999999994, output: 0.49299999999999994 }, + limit: { context: 16384, input: 16384, output: 8192 }, + }, + "anthracite-org/magnum-v4-72b": { + id: "anthracite-org/magnum-v4-72b", + name: "Magnum v4 72B", + family: "llama", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.006, output: 2.992 }, + limit: { context: 16384, input: 16384, output: 8192 }, + }, + "anthracite-org/magnum-v2-72b": { + id: "anthracite-org/magnum-v2-72b", + name: "Magnum V2 72B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-01", + last_updated: "2024-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.006, output: 2.992 }, + limit: { context: 16384, input: 16384, output: 8192 }, + }, + "ReadyArt/MS3.2-The-Omega-Directive-24B-Unslop-v2.0": { + id: "ReadyArt/MS3.2-The-Omega-Directive-24B-Unslop-v2.0", + name: "Omega Directive 24B Unslop v2.0", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-08", + last_updated: "2025-12-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 0.5 }, + limit: { context: 16384, input: 16384, output: 32768 }, + }, + "ReadyArt/The-Omega-Abomination-L-70B-v1.0": { + id: "ReadyArt/The-Omega-Abomination-L-70B-v1.0", + name: "The Omega Abomination V1", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.7, output: 0.95 }, + limit: { context: 16384, input: 16384, output: 16384 }, + }, + "undi95/remm-slerp-l2-13b": { + id: "undi95/remm-slerp-l2-13b", + name: "ReMM SLERP 13B", + family: "llama", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.7989999999999999, output: 1.2069999999999999 }, + limit: { context: 6144, input: 6144, output: 4096 }, + }, + "MarinaraSpaghetti/NemoMix-Unleashed-12B": { + id: "MarinaraSpaghetti/NemoMix-Unleashed-12B", + name: "NemoMix 12B Unleashed", + family: "mistral-nemo", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-01", + last_updated: "2024-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.49299999999999994, output: 0.49299999999999994 }, + limit: { context: 32768, input: 32768, output: 8192 }, + }, + "allenai/molmo-2-8b": { + id: "allenai/molmo-2-8b", + name: "Molmo 2 8B", + family: "allenai", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-02-14", + last_updated: "2026-02-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 36864, input: 36864, output: 36864 }, + }, + "allenai/olmo-3.1-32b-instruct": { + id: "allenai/olmo-3.1-32b-instruct", + name: "Olmo 3.1 32B Instruct", + family: "allenai", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-01-25", + last_updated: "2026-01-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.6 }, + limit: { context: 65536, input: 65536, output: 8192 }, + }, + "allenai/olmo-3.1-32b-think": { + id: "allenai/olmo-3.1-32b-think", + name: "Olmo 3.1 32B Think", + family: "allenai", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2026-01-25", + last_updated: "2026-01-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.5 }, + limit: { context: 65536, input: 65536, output: 8192 }, + }, + "allenai/olmo-3-32b-think": { + id: "allenai/olmo-3-32b-think", + name: "Olmo 3 32B Think", + family: "allenai", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-11-01", + last_updated: "2025-11-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.44999999999999996 }, + limit: { context: 128000, input: 128000, output: 8192 }, + }, + "stepfun-ai/step-3.5-flash:thinking": { + id: "stepfun-ai/step-3.5-flash:thinking", + name: "Step 3.5 Flash Thinking", + family: "step", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2026-02-02", + last_updated: "2026-02-02", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5 }, + limit: { context: 256000, input: 256000, output: 256000 }, + }, + "stepfun-ai/step-3.5-flash": { + id: "stepfun-ai/step-3.5-flash", + name: "Step 3.5 Flash", + family: "step", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2026-02-02", + last_updated: "2026-02-02", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5 }, + limit: { context: 256000, input: 256000, output: 256000 }, + }, + "zai-org/glm-4.7": { + id: "zai-org/glm-4.7", + name: "GLM 4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2026-01-29", + last_updated: "2026-01-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.8 }, + limit: { context: 200000, input: 200000, output: 128000 }, + }, + "zai-org/glm-5": { + id: "zai-org/glm-5", + name: "GLM 5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 2.55 }, + limit: { context: 200000, input: 200000, output: 128000 }, + }, + "zai-org/glm-5.1": { + id: "zai-org/glm-5.1", + name: "GLM 5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2026-03-27", + last_updated: "2026-03-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 2.55 }, + limit: { context: 200000, input: 200000, output: 131072 }, + }, + "zai-org/glm-5.1:thinking": { + id: "zai-org/glm-5.1:thinking", + name: "GLM 5.1 Thinking", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2026-03-27", + last_updated: "2026-03-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 2.55 }, + limit: { context: 200000, input: 200000, output: 131072 }, + }, + "zai-org/glm-5:thinking": { + id: "zai-org/glm-5:thinking", + name: "GLM 5 Thinking", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 2.55 }, + limit: { context: 200000, input: 200000, output: 128000 }, + }, + "zai-org/glm-4.7-flash": { + id: "zai-org/glm-4.7-flash", + name: "GLM 4.7 Flash", + family: "glm-flash", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2026-01-19", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.4 }, + limit: { context: 200000, input: 200000, output: 128000 }, + }, + "featherless-ai/Qwerky-72B": { + id: "featherless-ai/Qwerky-72B", + name: "Qwerky 72B", + family: "qwerky", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-03-20", + last_updated: "2025-03-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 0.5 }, + limit: { context: 32000, input: 32000, output: 8192 }, + }, + "mlabonne/NeuralDaredevil-8B-abliterated": { + id: "mlabonne/NeuralDaredevil-8B-abliterated", + name: "Neural Daredevil 8B abliterated", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.44, output: 0.44 }, + limit: { context: 8192, input: 8192, output: 8192 }, + }, + "raifle/sorcererlm-8x22b": { + id: "raifle/sorcererlm-8x22b", + name: "SorcererLM 8x22B", + family: "mixtral", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 4.505, output: 4.505 }, + limit: { context: 16000, input: 16000, output: 8192 }, + }, + "mistralai/mixtral-8x7b-instruct-v0.1": { + id: "mistralai/mixtral-8x7b-instruct-v0.1", + name: "Mixtral 8x7B", + family: "mixtral", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 0.27 }, + limit: { context: 32768, input: 32768, output: 32768 }, + }, + "mistralai/mistral-saba": { + id: "mistralai/mistral-saba", + name: "Mistral Saba", + family: "mistral", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1989, output: 0.595 }, + limit: { context: 32000, input: 32000, output: 32768 }, + }, + "mistralai/mistral-large-3-675b-instruct-2512": { + id: "mistralai/mistral-large-3-675b-instruct-2512", + name: "Mistral Large 3 675B", + family: "mistral-large", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-02", + last_updated: "2025-12-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 3 }, + limit: { context: 262144, input: 262144, output: 256000 }, + }, + "mistralai/devstral-2-123b-instruct-2512": { + id: "mistralai/devstral-2-123b-instruct-2512", + name: "Devstral 2 123B", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-09", + last_updated: "2025-12-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.4 }, + limit: { context: 262144, input: 262144, output: 65536 }, + }, + "mistralai/codestral-2508": { + id: "mistralai/codestral-2508", + name: "Codestral 2508", + family: "codestral", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-01", + last_updated: "2025-08-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.8999999999999999 }, + limit: { context: 256000, input: 256000, output: 32768 }, + }, + "mistralai/ministral-14b-instruct-2512": { + id: "mistralai/ministral-14b-instruct-2512", + name: "Ministral 3 14B", + family: "ministral", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-02", + last_updated: "2025-12-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 262144, input: 262144, output: 32768 }, + }, + "mistralai/mistral-tiny": { + id: "mistralai/mistral-tiny", + name: "Mistral Tiny", + family: "mistral", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2023-12-11", + last_updated: "2024-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25499999999999995, output: 0.25499999999999995 }, + limit: { context: 32000, input: 32000, output: 8192 }, + }, + "mistralai/ministral-8b-2512": { + id: "mistralai/ministral-8b-2512", + name: "Ministral 8B", + family: "ministral", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-04", + last_updated: "2025-12-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.15 }, + limit: { context: 262144, input: 262144, output: 32768 }, + }, + "mistralai/mixtral-8x22b-instruct-v0.1": { + id: "mistralai/mixtral-8x22b-instruct-v0.1", + name: "Mixtral 8x22B", + family: "mixtral", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8999999999999999, output: 0.8999999999999999 }, + limit: { context: 65536, input: 65536, output: 32768 }, + }, + "mistralai/mistral-medium-3.1": { + id: "mistralai/mistral-medium-3.1", + name: "Mistral Medium 3.1", + family: "mistral-medium", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2 }, + limit: { context: 131072, input: 131072, output: 32768 }, + }, + "mistralai/ministral-3b-2512": { + id: "mistralai/ministral-3b-2512", + name: "Ministral 3B", + family: "ministral", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-04", + last_updated: "2025-12-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 131072, input: 131072, output: 32768 }, + }, + "mistralai/Mistral-Nemo-Instruct-2407": { + id: "mistralai/Mistral-Nemo-Instruct-2407", + name: "Mistral Nemo", + family: "mistral-nemo", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1003, output: 0.1207 }, + limit: { context: 16384, input: 16384, output: 8192 }, + }, + "mistralai/mistral-medium-3": { + id: "mistralai/mistral-medium-3", + name: "Mistral Medium 3", + family: "mistral-medium", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2 }, + limit: { context: 131072, input: 131072, output: 32768 }, + }, + "mistralai/mistral-7b-instruct": { + id: "mistralai/mistral-7b-instruct", + name: "Mistral 7B Instruct", + family: "mistral", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-05-27", + last_updated: "2024-05-27", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.0544, output: 0.0544 }, + limit: { context: 32768, input: 32768, output: 8192 }, + }, + "mistralai/Devstral-Small-2505": { + id: "mistralai/Devstral-Small-2505", + name: "Mistral Devstral Small 2505", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-02", + last_updated: "2025-08-02", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.060000000000000005, output: 0.060000000000000005 }, + limit: { context: 32768, input: 32768, output: 8192 }, + }, + "mistralai/mistral-small-creative": { + id: "mistralai/mistral-small-creative", + name: "Mistral Small Creative", + family: "mistral-small", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-12-16", + last_updated: "2025-12-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 32768, input: 32768, output: 32768 }, + }, + "mistralai/mistral-large": { + id: "mistralai/mistral-large", + name: "Mistral Large 2411", + family: "mistral-large", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-02-26", + last_updated: "2024-02-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.006, output: 6.001 }, + limit: { context: 128000, input: 128000, output: 256000 }, + }, + "mistralai/ministral-14b-2512": { + id: "mistralai/ministral-14b-2512", + name: "Ministral 14B", + family: "ministral", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-04", + last_updated: "2025-12-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 262144, input: 262144, output: 32768 }, + }, + "shisa-ai/shisa-v2.1-llama3.3-70b": { + id: "shisa-ai/shisa-v2.1-llama3.3-70b", + name: "Shisa V2.1 Llama 3.3 70B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 0.5 }, + limit: { context: 32768, input: 32768, output: 4096 }, + }, + "shisa-ai/shisa-v2-llama3.3-70b": { + id: "shisa-ai/shisa-v2-llama3.3-70b", + name: "Shisa V2 Llama 3.3 70B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-26", + last_updated: "2025-07-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 0.5 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "meta-llama/llama-3.3-70b-instruct": { + id: "meta-llama/llama-3.3-70b-instruct", + name: "Llama 3.3 70b Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-02-27", + last_updated: "2025-02-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.23 }, + limit: { context: 131072, input: 131072, output: 16384 }, + }, + "meta-llama/llama-4-scout": { + id: "meta-llama/llama-4-scout", + name: "Llama 4 Scout", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.085, output: 0.46 }, + limit: { context: 328000, input: 328000, output: 65536 }, + }, + "meta-llama/llama-4-maverick": { + id: "meta-llama/llama-4-maverick", + name: "Llama 4 Maverick", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.18000000000000002, output: 0.8 }, + limit: { context: 1048576, input: 1048576, output: 65536 }, + }, + "meta-llama/llama-3.2-90b-vision-instruct": { + id: "meta-llama/llama-3.2-90b-vision-instruct", + name: "Llama 3.2 Medium", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.9009999999999999, output: 0.9009999999999999 }, + limit: { context: 131072, input: 131072, output: 16384 }, + }, + "meta-llama/llama-3.2-3b-instruct": { + id: "meta-llama/llama-3.2-3b-instruct", + name: "Llama 3.2 3b Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-09-25", + last_updated: "2024-09-25", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.0306, output: 0.0493 }, + limit: { context: 131072, input: 131072, output: 8192 }, + }, + "meta-llama/llama-3.1-8b-instruct": { + id: "meta-llama/llama-3.1-8b-instruct", + name: "Llama 3.1 8b Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.0544, output: 0.0544 }, + limit: { context: 131072, input: 131072, output: 16384 }, + }, + "GalrionSoftworks/MN-LooseCannon-12B-v1": { + id: "GalrionSoftworks/MN-LooseCannon-12B-v1", + name: "MN-LooseCannon-12B-v1", + family: "mistral-nemo", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-01", + last_updated: "2024-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.49299999999999994, output: 0.49299999999999994 }, + limit: { context: 16384, input: 16384, output: 8192 }, + }, + "baseten/Kimi-K2-Instruct-FP4": { + id: "baseten/Kimi-K2-Instruct-FP4", + name: "Kimi K2 0711 Instruct FP4", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-11", + last_updated: "2025-07-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 2 }, + limit: { context: 128000, input: 128000, output: 131072 }, + }, + "Gryphe/MythoMax-L2-13b": { + id: "Gryphe/MythoMax-L2-13b", + name: "MythoMax 13B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-08", + last_updated: "2025-08-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1003, output: 0.1003 }, + limit: { context: 4000, input: 4000, output: 4096 }, + }, + "x-ai/grok-4-fast:thinking": { + id: "x-ai/grok-4-fast:thinking", + name: "Grok 4 Fast Thinking", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5 }, + limit: { context: 2000000, input: 2000000, output: 131072 }, + }, + "x-ai/grok-4-07-09": { + id: "x-ai/grok-4-07-09", + name: "Grok 4", + family: "grok", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 256000, input: 256000, output: 131072 }, + }, + "x-ai/grok-4-fast": { + id: "x-ai/grok-4-fast", + name: "Grok 4 Fast", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-09-20", + last_updated: "2025-09-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5 }, + limit: { context: 2000000, input: 2000000, output: 131072 }, + }, + "x-ai/grok-code-fast-1": { + id: "x-ai/grok-code-fast-1", + name: "Grok Code Fast 1", + family: "grok", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-08-28", + last_updated: "2025-08-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.5 }, + limit: { context: 256000, input: 256000, output: 131072 }, + }, + "x-ai/grok-4.1-fast": { + id: "x-ai/grok-4.1-fast", + name: "Grok 4.1 Fast", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-11-20", + last_updated: "2025-11-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5 }, + limit: { context: 2000000, input: 2000000, output: 131072 }, + }, + "x-ai/grok-4.1-fast-reasoning": { + id: "x-ai/grok-4.1-fast-reasoning", + name: "Grok 4.1 Fast Reasoning", + family: "grok", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-11-20", + last_updated: "2025-11-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5 }, + limit: { context: 2000000, input: 2000000, output: 131072 }, + }, + "tencent/Hunyuan-MT-7B": { + id: "tencent/Hunyuan-MT-7B", + name: "Hunyuan MT 7B", + family: "hunyuan", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-09-18", + last_updated: "2025-09-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 10, output: 20 }, + limit: { context: 8192, input: 8192, output: 8192 }, + }, + "microsoft/wizardlm-2-8x22b": { + id: "microsoft/wizardlm-2-8x22b", + name: "WizardLM-2 8x22B", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-15", + last_updated: "2025-04-15", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.49299999999999994, output: 0.49299999999999994 }, + limit: { context: 65536, input: 65536, output: 8192 }, + }, + "microsoft/MAI-DS-R1-FP8": { + id: "microsoft/MAI-DS-R1-FP8", + name: "Microsoft DeepSeek R1", + family: "deepseek", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.3 }, + limit: { context: 128000, input: 128000, output: 8192 }, + }, + "cohere/command-r": { + id: "cohere/command-r", + name: "Cohere: Command R", + family: "command-r", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-03-11", + last_updated: "2024-03-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.476, output: 1.428 }, + limit: { context: 128000, input: 128000, output: 4096 }, + }, + "cohere/command-r-plus-08-2024": { + id: "cohere/command-r-plus-08-2024", + name: "Cohere: Command R+", + family: "command-r", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + release_date: "2024-08-30", + last_updated: "2024-08-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.856, output: 14.246 }, + limit: { context: 128000, input: 128000, output: 4096 }, + }, + "chutesai/Mistral-Small-3.2-24B-Instruct-2506": { + id: "chutesai/Mistral-Small-3.2-24B-Instruct-2506", + name: "Mistral Small 3.2 24b Instruct", + family: "chutesai", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-15", + last_updated: "2025-04-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.4 }, + limit: { context: 128000, input: 128000, output: 131072 }, + }, + "nvidia/Llama-3.1-Nemotron-Ultra-253B-v1": { + id: "nvidia/Llama-3.1-Nemotron-Ultra-253B-v1", + name: "Nvidia Nemotron Ultra 253B", + family: "nemotron", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-03", + last_updated: "2025-07-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 0.8 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "nvidia/nemotron-3-nano-30b-a3b": { + id: "nvidia/nemotron-3-nano-30b-a3b", + name: "Nvidia Nemotron 3 Nano 30B", + family: "nemotron", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-15", + last_updated: "2025-12-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.17, output: 0.68 }, + limit: { context: 256000, input: 256000, output: 262144 }, + }, + "nvidia/nvidia-nemotron-nano-9b-v2": { + id: "nvidia/nvidia-nemotron-nano-9b-v2", + name: "Nvidia Nemotron Nano 9B v2", + family: "nemotron", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-18", + last_updated: "2025-08-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.17, output: 0.68 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "nvidia/Llama-3.1-Nemotron-70B-Instruct-HF": { + id: "nvidia/Llama-3.1-Nemotron-70B-Instruct-HF", + name: "Nvidia Nemotron 70b", + family: "nemotron", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-15", + last_updated: "2025-04-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.357, output: 0.408 }, + limit: { context: 16384, input: 16384, output: 8192 }, + }, + "nvidia/Llama-3.3-Nemotron-Super-49B-v1": { + id: "nvidia/Llama-3.3-Nemotron-Super-49B-v1", + name: "Nvidia Nemotron Super 49B", + family: "nemotron", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-08", + last_updated: "2025-08-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.15 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "nvidia/Llama-3_3-Nemotron-Super-49B-v1_5": { + id: "nvidia/Llama-3_3-Nemotron-Super-49B-v1_5", + name: "Nvidia Nemotron Super 49B v1.5", + family: "nemotron", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-08", + last_updated: "2025-08-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.25 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "TheDrummer 2/Anubis-70B-v1": { + id: "TheDrummer 2/Anubis-70B-v1", + name: "Anubis 70B v1", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-01", + last_updated: "2024-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.31, output: 0.31 }, + limit: { context: 65536, input: 65536, output: 16384 }, + }, + "TheDrummer 2/Cydonia-24B-v4.3": { + id: "TheDrummer 2/Cydonia-24B-v4.3", + name: "The Drummer Cydonia 24B v4.3", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-25", + last_updated: "2025-12-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1003, output: 0.1207 }, + limit: { context: 32768, input: 32768, output: 32768 }, + }, + "TheDrummer 2/Magidonia-24B-v4.3": { + id: "TheDrummer 2/Magidonia-24B-v4.3", + name: "The Drummer Magidonia 24B v4.3", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-25", + last_updated: "2025-12-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1003, output: 0.1207 }, + limit: { context: 32768, input: 32768, output: 32768 }, + }, + "TheDrummer 2/Cydonia-24B-v4": { + id: "TheDrummer 2/Cydonia-24B-v4", + name: "The Drummer Cydonia 24B v4", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-22", + last_updated: "2025-07-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2006, output: 0.2414 }, + limit: { context: 16384, input: 16384, output: 32768 }, + }, + "TheDrummer 2/Anubis-70B-v1.1": { + id: "TheDrummer 2/Anubis-70B-v1.1", + name: "Anubis 70B v1.1", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-01", + last_updated: "2024-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.31, output: 0.31 }, + limit: { context: 131072, input: 131072, output: 16384 }, + }, + "TheDrummer 2/Rocinante-12B-v1.1": { + id: "TheDrummer 2/Rocinante-12B-v1.1", + name: "Rocinante 12b", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-01", + last_updated: "2024-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.408, output: 0.595 }, + limit: { context: 16384, input: 16384, output: 8192 }, + }, + "TheDrummer 2/Cydonia-24B-v4.1": { + id: "TheDrummer 2/Cydonia-24B-v4.1", + name: "The Drummer Cydonia 24B v4.1", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-19", + last_updated: "2025-08-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1003, output: 0.1207 }, + limit: { context: 16384, input: 16384, output: 32768 }, + }, + "TheDrummer 2/UnslopNemo-12B-v4.1": { + id: "TheDrummer 2/UnslopNemo-12B-v4.1", + name: "UnslopNemo 12b v4", + family: "llama", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-01", + last_updated: "2024-07-01", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.49299999999999994, output: 0.49299999999999994 }, + limit: { context: 32768, input: 32768, output: 8192 }, + }, + "TheDrummer 2/Cydonia-24B-v2": { + id: "TheDrummer 2/Cydonia-24B-v2", + name: "The Drummer Cydonia 24B v2", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1003, output: 0.1207 }, + limit: { context: 16384, input: 16384, output: 32768 }, + }, + "TheDrummer 2/skyfall-36b-v2": { + id: "TheDrummer 2/skyfall-36b-v2", + name: "TheDrummer Skyfall 36B V2", + family: "llama", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-03-10", + last_updated: "2025-03-10", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.49299999999999994, output: 0.49299999999999994 }, + limit: { context: 64000, input: 64000, output: 32768 }, + }, + "deepseek-ai/DeepSeek-V3.1:thinking": { + id: "deepseek-ai/DeepSeek-V3.1:thinking", + name: "DeepSeek V3.1 Thinking", + family: "deepseek-thinking", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-21", + last_updated: "2025-08-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.7 }, + limit: { context: 128000, input: 128000, output: 65536 }, + }, + "deepseek-ai/DeepSeek-V3.1": { + id: "deepseek-ai/DeepSeek-V3.1", + name: "DeepSeek V3.1", + family: "deepseek", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-26", + last_updated: "2025-07-26", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.7 }, + limit: { context: 128000, input: 128000, output: 65536 }, + }, + "deepseek-ai/DeepSeek-V3.1-Terminus:thinking": { + id: "deepseek-ai/DeepSeek-V3.1-Terminus:thinking", + name: "DeepSeek V3.1 Terminus (Thinking)", + family: "deepseek-thinking", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-09-22", + last_updated: "2025-09-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 0.7 }, + limit: { context: 128000, input: 128000, output: 65536 }, + }, + "deepseek-ai/deepseek-v3.2-exp-thinking": { + id: "deepseek-ai/deepseek-v3.2-exp-thinking", + name: "DeepSeek V3.2 Exp Thinking", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27999999999999997, output: 0.42000000000000004 }, + limit: { context: 163840, input: 163840, output: 65536 }, + }, + "deepseek-ai/deepseek-v3.2-exp": { + id: "deepseek-ai/deepseek-v3.2-exp", + name: "DeepSeek V3.2 Exp", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27999999999999997, output: 0.42000000000000004 }, + limit: { context: 163840, input: 163840, output: 65536 }, + }, + "deepseek-ai/DeepSeek-R1-0528": { + id: "deepseek-ai/DeepSeek-R1-0528", + name: "DeepSeek R1 0528", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-05-28", + last_updated: "2025-05-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.7 }, + limit: { context: 128000, input: 128000, output: 163840 }, + }, + "deepseek-ai/DeepSeek-V3.1-Terminus": { + id: "deepseek-ai/DeepSeek-V3.1-Terminus", + name: "DeepSeek V3.1 Terminus", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-08-02", + last_updated: "2025-08-02", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 0.7 }, + limit: { context: 128000, input: 128000, output: 65536 }, + }, + "openai/gpt-5.1-codex-max": { + id: "openai/gpt-5.1-codex-max", + name: "GPT 5.1 Codex Max", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 20 }, + limit: { context: 400000, input: 400000, output: 128000 }, + }, + "openai/gpt-5.2-chat": { + id: "openai/gpt-5.2-chat", + name: "GPT 5.2 Chat", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2026-01-01", + last_updated: "2026-01-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14 }, + limit: { context: 400000, input: 400000, output: 16384 }, + }, + "openai/gpt-4o-mini-search-preview": { + id: "openai/gpt-4o-mini-search-preview", + name: "GPT-4o mini Search Preview", + family: "gpt-mini", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.088, output: 0.35 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "openai/chatgpt-4o-latest": { + id: "openai/chatgpt-4o-latest", + name: "ChatGPT 4o", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2024-05-13", + last_updated: "2024-05-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 4.998, output: 14.993999999999998 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "openai/gpt-5.2-pro": { + id: "openai/gpt-5.2-pro", + name: "GPT 5.2 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2026-01-01", + last_updated: "2026-01-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 21, output: 168 }, + limit: { context: 400000, input: 400000, output: 128000 }, + }, + "openai/gpt-5-mini": { + id: "openai/gpt-5-mini", + name: "GPT 5 Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2 }, + limit: { context: 400000, input: 400000, output: 128000 }, + }, + "openai/gpt-5-nano": { + id: "openai/gpt-5-nano", + name: "GPT 5 Nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.4 }, + limit: { context: 400000, input: 400000, output: 128000 }, + }, + "openai/gpt-4-turbo": { + id: "openai/gpt-4-turbo", + name: "GPT-4 Turbo", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2023-11-06", + last_updated: "2024-01-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 10, output: 30 }, + limit: { context: 128000, input: 128000, output: 4096 }, + }, + "openai/gpt-5.2": { + id: "openai/gpt-5.2", + name: "GPT 5.2", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2026-01-01", + last_updated: "2026-01-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14 }, + limit: { context: 400000, input: 400000, output: 128000 }, + }, + "openai/o3-mini-high": { + id: "openai/o3-mini-high", + name: "OpenAI o3-mini (High)", + family: "o-mini", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-01-31", + last_updated: "2025-01-31", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.64, output: 2.588 }, + limit: { context: 200000, input: 200000, output: 100000 }, + }, + "openai/gpt-4o-mini": { + id: "openai/gpt-4o-mini", + name: "GPT-4o mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1496, output: 0.595 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "openai/o4-mini-deep-research": { + id: "openai/o4-mini-deep-research", + name: "OpenAI o4-mini Deep Research", + family: "o-mini", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 9.996, output: 19.992 }, + limit: { context: 200000, input: 200000, output: 100000 }, + }, + "openai/gpt-5.1-chat": { + id: "openai/gpt-5.1-chat", + name: "GPT 5.1 Chat", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 400000, input: 400000, output: 128000 }, + }, + "openai/o4-mini": { + id: "openai/o4-mini", + name: "OpenAI o4-mini", + family: "o-mini", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4 }, + limit: { context: 200000, input: 200000, output: 100000 }, + }, + "openai/gpt-5.2-codex": { + id: "openai/gpt-5.2-codex", + name: "GPT 5.2 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2026-01-14", + last_updated: "2026-01-14", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14 }, + limit: { context: 400000, input: 400000, output: 128000 }, + }, + "openai/gpt-5.1-codex-mini": { + id: "openai/gpt-5.1-codex-mini", + name: "GPT 5.1 Codex Mini", + family: "gpt-codex-mini", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2 }, + limit: { context: 400000, input: 400000, output: 128000 }, + }, + "openai/o1-preview": { + id: "openai/o1-preview", + name: "OpenAI o1-preview", + family: "o", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2024-09-12", + last_updated: "2024-09-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 14.993999999999998, output: 59.993 }, + limit: { context: 128000, input: 128000, output: 32768 }, + }, + "openai/gpt-4o-2024-08-06": { + id: "openai/gpt-4o-2024-08-06", + name: "GPT-4o (2024-08-06)", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-08-06", + last_updated: "2024-08-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.499, output: 9.996 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "openai/gpt-5.1": { + id: "openai/gpt-5.1", + name: "GPT 5.1", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 400000, input: 400000, output: 128000 }, + }, + "openai/o1": { + id: "openai/o1", + name: "OpenAI o1", + family: "o", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2024-12-17", + last_updated: "2024-12-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 14.993999999999998, output: 59.993 }, + limit: { context: 200000, input: 200000, output: 100000 }, + }, + "openai/gpt-3.5-turbo": { + id: "openai/gpt-3.5-turbo", + name: "GPT-3.5 Turbo", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2022-11-30", + last_updated: "2024-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 1.5 }, + limit: { context: 16385, input: 16385, output: 4096 }, + }, + "openai/o3-deep-research": { + id: "openai/o3-deep-research", + name: "OpenAI o3 Deep Research", + family: "o", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 9.996, output: 19.992 }, + limit: { context: 200000, input: 200000, output: 100000 }, + }, + "openai/o3-mini": { + id: "openai/o3-mini", + name: "OpenAI o3-mini", + family: "o-mini", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-01-31", + last_updated: "2025-01-31", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4 }, + limit: { context: 200000, input: 200000, output: 100000 }, + }, + "openai/gpt-4-turbo-preview": { + id: "openai/gpt-4-turbo-preview", + name: "GPT-4 Turbo Preview", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2023-11-06", + last_updated: "2024-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 9.996, output: 30.004999999999995 }, + limit: { context: 128000, input: 128000, output: 4096 }, + }, + "openai/o1-pro": { + id: "openai/o1-pro", + name: "OpenAI o1 Pro", + family: "o-pro", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-01-25", + last_updated: "2025-01-25", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 150, output: 600 }, + limit: { context: 200000, input: 200000, output: 100000 }, + }, + "openai/gpt-5-codex": { + id: "openai/gpt-5-codex", + name: "GPT-5 Codex", + family: "gpt-codex", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-09-15", + last_updated: "2025-09-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 9.996, output: 19.992 }, + limit: { context: 256000, input: 256000, output: 32768 }, + }, + "openai/gpt-5.1-chat-latest": { + id: "openai/gpt-5.1-chat-latest", + name: "GPT 5.1 Chat (Latest)", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 400000, input: 400000, output: 16384 }, + }, + "openai/gpt-4o-search-preview": { + id: "openai/gpt-4o-search-preview", + name: "GPT-4o Search Preview", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-05-13", + last_updated: "2024-05-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.47, output: 5.88 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "openai/gpt-4.1-nano": { + id: "openai/gpt-4.1-nano", + name: "GPT 4.1 Nano", + family: "gpt-nano", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 1047576, input: 1047576, output: 32768 }, + }, + "openai/o4-mini-high": { + id: "openai/o4-mini-high", + name: "OpenAI o4-mini high", + family: "o-mini", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4 }, + limit: { context: 200000, input: 200000, output: 100000 }, + }, + "openai/o3": { + id: "openai/o3", + name: "OpenAI o3", + family: "o", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8 }, + limit: { context: 200000, input: 200000, output: 100000 }, + }, + "openai/gpt-oss-20b": { + id: "openai/gpt-oss-20b", + name: "GPT OSS 20B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.04, output: 0.15 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "openai/gpt-5-pro": { + id: "openai/gpt-5-pro", + name: "GPT 5 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 120 }, + limit: { context: 400000, input: 400000, output: 128000 }, + }, + "openai/gpt-5.1-2025-11-13": { + id: "openai/gpt-5.1-2025-11-13", + name: "GPT-5.1 (2025-11-13)", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 1000000, input: 1000000, output: 32768 }, + }, + "openai/gpt-4o": { + id: "openai/gpt-4o", + name: "GPT-4o", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-05-13", + last_updated: "2024-05-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.499, output: 9.996 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "openai/o3-mini-low": { + id: "openai/o3-mini-low", + name: "OpenAI o3-mini (Low)", + family: "o-mini", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-01-31", + last_updated: "2025-01-31", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 9.996, output: 19.992 }, + limit: { context: 200000, input: 200000, output: 100000 }, + }, + "openai/gpt-5": { + id: "openai/gpt-5", + name: "GPT 5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 400000, input: 400000, output: 128000 }, + }, + "openai/gpt-oss-safeguard-20b": { + id: "openai/gpt-oss-safeguard-20b", + name: "GPT OSS Safeguard 20B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-10-29", + last_updated: "2025-10-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.075, output: 0.3 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "openai/o3-pro-2025-06-10": { + id: "openai/o3-pro-2025-06-10", + name: "OpenAI o3-pro (2025-06-10)", + family: "o-pro", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-06-10", + last_updated: "2025-06-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 9.996, output: 19.992 }, + limit: { context: 200000, input: 200000, output: 100000 }, + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "GPT OSS 120B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.25 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "openai/gpt-5-chat-latest": { + id: "openai/gpt-5-chat-latest", + name: "GPT 5 Chat", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 400000, input: 400000, output: 128000 }, + }, + "openai/gpt-4.1": { + id: "openai/gpt-4.1", + name: "GPT 4.1", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-09-10", + last_updated: "2025-09-10", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8 }, + limit: { context: 1047576, input: 1047576, output: 32768 }, + }, + "openai/gpt-4.1-mini": { + id: "openai/gpt-4.1-mini", + name: "GPT 4.1 Mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.6 }, + limit: { context: 1047576, input: 1047576, output: 32768 }, + }, + "openai/gpt-5.1-codex": { + id: "openai/gpt-5.1-codex", + name: "GPT 5.1 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 400000, input: 400000, output: 128000 }, + }, + "openai/gpt-4o-2024-11-20": { + id: "openai/gpt-4o-2024-11-20", + name: "GPT-4o (2024-11-20)", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-11-20", + last_updated: "2024-11-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10 }, + limit: { context: 128000, input: 128000, output: 16384 }, + }, + "VongolaChouko/Starcannon-Unleashed-12B-v1.0": { + id: "VongolaChouko/Starcannon-Unleashed-12B-v1.0", + name: "Mistral Nemo Starcannon 12b v1", + family: "mistral-nemo", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-01", + last_updated: "2024-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.49299999999999994, output: 0.49299999999999994 }, + limit: { context: 16384, input: 16384, output: 8192 }, + }, + "amazon/nova-lite-v1": { + id: "amazon/nova-lite-v1", + name: "Amazon Nova Lite 1.0", + family: "nova-lite", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-03", + last_updated: "2024-12-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.0595, output: 0.238 }, + limit: { context: 300000, input: 300000, output: 5120 }, + }, + "amazon/nova-pro-v1": { + id: "amazon/nova-pro-v1", + name: "Amazon Nova Pro 1.0", + family: "nova-pro", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-03", + last_updated: "2024-12-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.7989999999999999, output: 3.1959999999999997 }, + limit: { context: 300000, input: 300000, output: 32000 }, + }, + "amazon/nova-2-lite-v1": { + id: "amazon/nova-2-lite-v1", + name: "Amazon Nova 2 Lite", + family: "nova", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-03", + last_updated: "2024-12-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5099999999999999, output: 4.25 }, + limit: { context: 1000000, input: 1000000, output: 65535 }, + }, + "amazon/nova-micro-v1": { + id: "amazon/nova-micro-v1", + name: "Amazon Nova Micro 1.0", + family: "nova-micro", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-03", + last_updated: "2024-12-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.0357, output: 0.1394 }, + limit: { context: 128000, input: 128000, output: 5120 }, + }, + "Sao10K/L3.3-70B-Euryale-v2.3": { + id: "Sao10K/L3.3-70B-Euryale-v2.3", + name: "Llama 3.3 70B Euryale", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.49299999999999994, output: 0.49299999999999994 }, + limit: { context: 20480, input: 20480, output: 16384 }, + }, + "Sao10K/L3.1-70B-Euryale-v2.2": { + id: "Sao10K/L3.1-70B-Euryale-v2.2", + name: "Llama 3.1 70B Euryale", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.306, output: 0.357 }, + limit: { context: 20480, input: 20480, output: 16384 }, + }, + "Sao10K/L3.1-70B-Hanami-x1": { + id: "Sao10K/L3.1-70B-Hanami-x1", + name: "Llama 3.1 70B Hanami", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.49299999999999994, output: 0.49299999999999994 }, + limit: { context: 16384, input: 16384, output: 16384 }, + }, + "Sao10K/L3-8B-Stheno-v3.2": { + id: "Sao10K/L3-8B-Stheno-v3.2", + name: "Sao10K Stheno 8b", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-11-29", + last_updated: "2024-11-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2006, output: 0.2006 }, + limit: { context: 16384, input: 16384, output: 8192 }, + }, + "LatitudeGames/Wayfarer-Large-70B-Llama-3.3": { + id: "LatitudeGames/Wayfarer-Large-70B-Llama-3.3", + name: "Llama 3.3 70B Wayfarer", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-20", + last_updated: "2025-02-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.700000007, output: 0.700000007 }, + limit: { context: 16384, input: 16384, output: 16384 }, + }, + "z-ai/glm-4.6:thinking": { + id: "z-ai/glm-4.6:thinking", + name: "GLM 4.6 Thinking", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.5 }, + limit: { context: 200000, input: 200000, output: 65535 }, + }, + "z-ai/glm-4.5v": { + id: "z-ai/glm-4.5v", + name: "GLM 4.5V", + family: "glmv", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-11-22", + last_updated: "2025-11-22", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 1.7999999999999998 }, + limit: { context: 64000, input: 64000, output: 96000 }, + }, + "z-ai/glm-4.6": { + id: "z-ai/glm-4.6", + name: "GLM 4.6", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.5 }, + limit: { context: 200000, input: 200000, output: 65535 }, + }, + "z-ai/glm-4.5v:thinking": { + id: "z-ai/glm-4.5v:thinking", + name: "GLM 4.5V Thinking", + family: "glmv", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-11-22", + last_updated: "2025-11-22", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 1.7999999999999998 }, + limit: { context: 64000, input: 64000, output: 96000 }, + }, + "baidu/ernie-4.5-vl-28b-a3b": { + id: "baidu/ernie-4.5-vl-28b-a3b", + name: "ERNIE 4.5 VL 28B", + family: "ernie", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-06-30", + last_updated: "2025-06-30", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.13999999999999999, output: 0.5599999999999999 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "baidu/ernie-4.5-300b-a47b": { + id: "baidu/ernie-4.5-300b-a47b", + name: "ERNIE 4.5 300B", + family: "ernie", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-06-30", + last_updated: "2025-06-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.35, output: 1.15 }, + limit: { context: 131072, input: 131072, output: 16384 }, + }, + "dmind/dmind-1": { + id: "dmind/dmind-1", + name: "DMind-1", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-06-01", + last_updated: "2025-06-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.6 }, + limit: { context: 32768, input: 32768, output: 8192 }, + }, + "dmind/dmind-1-mini": { + id: "dmind/dmind-1-mini", + name: "DMind-1-Mini", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-06-01", + last_updated: "2025-06-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.4 }, + limit: { context: 32768, input: 32768, output: 8192 }, + }, + "Infermatic/MN-12B-Inferor-v0.0": { + id: "Infermatic/MN-12B-Inferor-v0.0", + name: "Mistral Nemo Inferor 12B", + family: "mistral-nemo", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-01", + last_updated: "2024-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25499999999999995, output: 0.49299999999999994 }, + limit: { context: 16384, input: 16384, output: 8192 }, + }, + "meituan-longcat/LongCat-Flash-Chat-FP8": { + id: "meituan-longcat/LongCat-Flash-Chat-FP8", + name: "LongCat Flash", + family: "longcat", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-08-31", + last_updated: "2025-08-31", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.7 }, + limit: { context: 128000, input: 128000, output: 32768 }, + }, + "meganova-ai/manta-mini-1.0": { + id: "meganova-ai/manta-mini-1.0", + name: "Manta Mini 1.0", + family: "nova", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-20", + last_updated: "2025-12-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.02, output: 0.16 }, + limit: { context: 8192, input: 8192, output: 8192 }, + }, + "meganova-ai/manta-pro-1.0": { + id: "meganova-ai/manta-pro-1.0", + name: "Manta Pro 1.0", + family: "nova", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-20", + last_updated: "2025-12-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.060000000000000005, output: 0.5 }, + limit: { context: 32768, input: 32768, output: 32768 }, + }, + "meganova-ai/manta-flash-1.0": { + id: "meganova-ai/manta-flash-1.0", + name: "Manta Flash 1.0", + family: "nova", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-20", + last_updated: "2025-12-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.02, output: 0.16 }, + limit: { context: 16384, input: 16384, output: 16384 }, + }, + "minimax/minimax-m2.7": { + id: "minimax/minimax-m2.7", + name: "MiniMax M2.7", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 204800, input: 204800, output: 131072 }, + }, + "minimax/minimax-01": { + id: "minimax/minimax-01", + name: "MiniMax 01", + family: "minimax", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-01-15", + last_updated: "2025-01-15", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1394, output: 1.1219999999999999 }, + limit: { context: 1000192, input: 1000192, output: 16384 }, + }, + "minimax/minimax-m2.1": { + id: "minimax/minimax-m2.1", + name: "MiniMax M2.1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-12-19", + last_updated: "2025-12-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.33, output: 1.32 }, + limit: { context: 200000, input: 200000, output: 131072 }, + }, + "minimax/minimax-m2-her": { + id: "minimax/minimax-m2-her", + name: "MiniMax M2-her", + family: "minimax", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-01-24", + last_updated: "2026-01-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.30200000000000005, output: 1.2069999999999999 }, + limit: { context: 65532, input: 65532, output: 2048 }, + }, + "minimax/minimax-m2.5": { + id: "minimax/minimax-m2.5", + name: "MiniMax M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 204800, input: 204800, output: 131072 }, + }, + "qwen/Qwen3.6-35B-A3B:thinking": { + id: "qwen/Qwen3.6-35B-A3B:thinking", + name: "Qwen3.6 35B A3B Thinking", + family: "qwen3.6", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2026-04-19", + last_updated: "2026-04-21", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.29, output: 1.74 }, + limit: { context: 262144, output: 16384 }, + }, + "qwen/qwen3.5-397b-a17b": { + id: "qwen/qwen3.5-397b-a17b", + name: "Qwen3.5 397B A17B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-02-16", + last_updated: "2026-02-16", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3.6 }, + limit: { context: 258048, input: 258048, output: 65536 }, + }, + "qwen/Qwen3.6-35B-A3B": { + id: "qwen/Qwen3.6-35B-A3B", + name: "Qwen3.6 35B A3B", + family: "qwen3.6", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2026-04-17", + last_updated: "2026-04-21", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.29, output: 1.74 }, + limit: { context: 262144, output: 16384 }, + }, + "unsloth/gemma-3-1b-it": { + id: "unsloth/gemma-3-1b-it", + name: "Gemma 3 1B IT", + family: "unsloth", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-03-10", + last_updated: "2025-03-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1003, output: 0.1003 }, + limit: { context: 128000, input: 128000, output: 8192 }, + }, + "unsloth/gemma-3-12b-it": { + id: "unsloth/gemma-3-12b-it", + name: "Gemma 3 12B IT", + family: "unsloth", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-03-10", + last_updated: "2025-03-10", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.272, output: 0.272 }, + limit: { context: 128000, input: 128000, output: 131072 }, + }, + "unsloth/gemma-3-4b-it": { + id: "unsloth/gemma-3-4b-it", + name: "Gemma 3 4B IT", + family: "unsloth", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-03-10", + last_updated: "2025-03-10", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2006, output: 0.2006 }, + limit: { context: 128000, input: 128000, output: 8192 }, + }, + "unsloth/gemma-3-27b-it": { + id: "unsloth/gemma-3-27b-it", + name: "Gemma 3 27B IT", + family: "unsloth", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-03-10", + last_updated: "2025-03-10", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2992, output: 0.2992 }, + limit: { context: 128000, input: 128000, output: 96000 }, + }, + "THUDM/GLM-Z1-9B-0414": { + id: "THUDM/GLM-Z1-9B-0414", + name: "GLM Z1 9B 0414", + family: "glm-z", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 32000, input: 32000, output: 8000 }, + }, + "THUDM/GLM-4-9B-0414": { + id: "THUDM/GLM-4-9B-0414", + name: "GLM 4 9B 0414", + family: "glm", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 32000, input: 32000, output: 8000 }, + }, + "THUDM/GLM-Z1-Rumination-32B-0414": { + id: "THUDM/GLM-Z1-Rumination-32B-0414", + name: "GLM Z1 Rumination 32B 0414", + family: "glm-z", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-15", + last_updated: "2025-04-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 32000, input: 32000, output: 65536 }, + }, + "THUDM/GLM-4-32B-0414": { + id: "THUDM/GLM-4-32B-0414", + name: "GLM 4 32B 0414", + family: "glm", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 128000, input: 128000, output: 65536 }, + }, + "THUDM/GLM-Z1-32B-0414": { + id: "THUDM/GLM-Z1-32B-0414", + name: "GLM Z1 32B 0414", + family: "glm-z", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-15", + last_updated: "2025-04-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 128000, input: 128000, output: 65536 }, + }, + "google/gemini-3-flash-preview": { + id: "google/gemini-3-flash-preview", + name: "Gemini 3 Flash (Preview)", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 3 }, + limit: { context: 1048756, input: 1048756, output: 65536 }, + }, + "google/gemini-flash-1.5": { + id: "google/gemini-flash-1.5", + name: "Gemini 1.5 Flash", + family: "gemini-flash", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-05-14", + last_updated: "2024-05-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.0748, output: 0.306 }, + limit: { context: 2000000, input: 2000000, output: 8192 }, + }, + "google/gemini-3-flash-preview-thinking": { + id: "google/gemini-3-flash-preview-thinking", + name: "Gemini 3 Flash Thinking", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 3 }, + limit: { context: 1048756, input: 1048756, output: 65536 }, + }, + "moonshotai/kimi-k2.5": { + id: "moonshotai/kimi-k2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + release_date: "2026-01-26", + last_updated: "2026-01-26", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.9 }, + limit: { context: 256000, input: 256000, output: 65536 }, + }, + "moonshotai/kimi-k2-instruct": { + id: "moonshotai/kimi-k2-instruct", + name: "Kimi K2 Instruct", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-07-01", + last_updated: "2025-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 2 }, + limit: { context: 256000, input: 256000, output: 8192 }, + }, + "moonshotai/kimi-k2-thinking-original": { + id: "moonshotai/kimi-k2-thinking-original", + name: "Kimi K2 Thinking Original", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-11-06", + last_updated: "2025-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 2.5 }, + limit: { context: 256000, input: 256000, output: 16384 }, + }, + "moonshotai/kimi-k2-instruct-0711": { + id: "moonshotai/kimi-k2-instruct-0711", + name: "Kimi K2 0711", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-07-11", + last_updated: "2025-07-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 2 }, + limit: { context: 128000, input: 128000, output: 8192 }, + }, + "moonshotai/Kimi-Dev-72B": { + id: "moonshotai/Kimi-Dev-72B", + name: "Kimi Dev 72B", + family: "kimi", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-04-15", + last_updated: "2025-04-15", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 0.4 }, + limit: { context: 128000, input: 128000, output: 131072 }, + }, + "moonshotai/kimi-k2-thinking-turbo-original": { + id: "moonshotai/kimi-k2-thinking-turbo-original", + name: "Kimi K2 Thinking Turbo Original", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-11-06", + last_updated: "2025-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.15, output: 8 }, + limit: { context: 256000, input: 256000, output: 16384 }, + }, + "moonshotai/kimi-k2.6": { + id: "moonshotai/kimi-k2.6", + name: "Kimi K2.6", + family: "kimi-k2.6", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + release_date: "2026-04-16", + last_updated: "2026-04-21", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.53, output: 2.73 }, + limit: { context: 256000, output: 65536 }, + }, + "moonshotai/kimi-k2.6:thinking": { + id: "moonshotai/kimi-k2.6:thinking", + name: "Kimi K2.6 Thinking", + family: "kimi-thinking", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + release_date: "2026-04-16", + last_updated: "2026-04-21", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.53, output: 2.73 }, + limit: { context: 256000, output: 65536 }, + }, + "moonshotai/Kimi-K2-Instruct-0905": { + id: "moonshotai/Kimi-K2-Instruct-0905", + name: "Kimi K2 0905", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2 }, + limit: { context: 256000, input: 256000, output: 262144 }, + }, + "moonshotai/kimi-k2-thinking": { + id: "moonshotai/kimi-k2-thinking", + name: "Kimi K2 Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + release_date: "2025-11-06", + last_updated: "2025-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 256000, input: 256000, output: 262144 }, + }, + "moonshotai/kimi-k2.5:thinking": { + id: "moonshotai/kimi-k2.5:thinking", + name: "Kimi K2.5 Thinking", + family: "kimi-thinking", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + release_date: "2026-01-26", + last_updated: "2026-01-26", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.9 }, + limit: { context: 256000, input: 256000, output: 65536 }, + }, + "Tongyi-Zhiwen/QwenLong-L1-32B": { + id: "Tongyi-Zhiwen/QwenLong-L1-32B", + name: "QwenLong L1 32B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-01-25", + last_updated: "2025-01-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.13999999999999999, output: 0.6 }, + limit: { context: 128000, input: 128000, output: 40960 }, + }, + "nothingiisreal/L3.1-70B-Celeste-V0.1-BF16": { + id: "nothingiisreal/L3.1-70B-Celeste-V0.1-BF16", + name: "Llama 3.1 70B Celeste v0.1", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.49299999999999994, output: 0.49299999999999994 }, + limit: { context: 16384, input: 16384, output: 16384 }, + }, + "aion-labs/aion-1.0": { + id: "aion-labs/aion-1.0", + name: "Aion 1.0", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-01", + last_updated: "2025-02-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3.995, output: 7.99 }, + limit: { context: 65536, input: 65536, output: 8192 }, + }, + "aion-labs/aion-rp-llama-3.1-8b": { + id: "aion-labs/aion-rp-llama-3.1-8b", + name: "Llama 3.1 8b (uncensored)", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2006, output: 0.2006 }, + limit: { context: 32768, input: 32768, output: 16384 }, + }, + "aion-labs/aion-1.0-mini": { + id: "aion-labs/aion-1.0-mini", + name: "Aion 1.0 mini (DeepSeek)", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-02-20", + last_updated: "2025-02-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.7989999999999999, output: 1.394 }, + limit: { context: 131072, input: 131072, output: 8192 }, + }, + "Alibaba-NLP/Tongyi-DeepResearch-30B-A3B": { + id: "Alibaba-NLP/Tongyi-DeepResearch-30B-A3B", + name: "Tongyi DeepResearch 30B A3B", + family: "yi", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-26", + last_updated: "2025-08-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.08, output: 0.24000000000000002 }, + limit: { context: 128000, input: 128000, output: 65536 }, + }, + "MiniMaxAI/MiniMax-M1-80k": { + id: "MiniMaxAI/MiniMax-M1-80k", + name: "MiniMax M1 80K", + family: "minimax", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-06-16", + last_updated: "2025-06-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6052, output: 2.4225000000000003 }, + limit: { context: 1000000, input: 1000000, output: 131072 }, + }, + "anthropic/claude-opus-4.6:thinking:low": { + id: "anthropic/claude-opus-4.6:thinking:low", + name: "Claude 4.6 Opus Thinking Low", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 4.998, output: 25.007 }, + limit: { context: 1000000, input: 1000000, output: 128000 }, + }, + "anthropic/claude-opus-4.6": { + id: "anthropic/claude-opus-4.6", + name: "Claude 4.6 Opus", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 4.998, output: 25.007 }, + limit: { context: 1000000, input: 1000000, output: 128000 }, + }, + "anthropic/claude-sonnet-4.6:thinking": { + id: "anthropic/claude-sonnet-4.6:thinking", + name: "Claude Sonnet 4.6 Thinking", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-02-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.992, output: 14.993999999999998 }, + limit: { context: 1000000, input: 1000000, output: 128000 }, + }, + "anthropic/claude-opus-4.6:thinking:max": { + id: "anthropic/claude-opus-4.6:thinking:max", + name: "Claude 4.6 Opus Thinking Max", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 4.998, output: 25.007 }, + limit: { context: 1000000, input: 1000000, output: 128000 }, + }, + "anthropic/claude-opus-4.6:thinking:medium": { + id: "anthropic/claude-opus-4.6:thinking:medium", + name: "Claude 4.6 Opus Thinking Medium", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 4.998, output: 25.007 }, + limit: { context: 1000000, input: 1000000, output: 128000 }, + }, + "anthropic/claude-sonnet-4.6": { + id: "anthropic/claude-sonnet-4.6", + name: "Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-02-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.992, output: 14.993999999999998 }, + limit: { context: 1000000, input: 1000000, output: 128000 }, + }, + "anthropic/claude-opus-4.6:thinking": { + id: "anthropic/claude-opus-4.6:thinking", + name: "Claude 4.6 Opus Thinking", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 4.998, output: 25.007 }, + limit: { context: 1000000, input: 1000000, output: 128000 }, + }, + "abacusai/Dracarys-72B-Instruct": { + id: "abacusai/Dracarys-72B-Instruct", + name: "Llama 3.1 70B Dracarys 2", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-02", + last_updated: "2025-08-02", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.49299999999999994, output: 0.49299999999999994 }, + limit: { context: 16384, input: 16384, output: 8192 }, + }, + "EVA-UNIT-01/EVA-LLaMA-3.33-70B-v0.0": { + id: "EVA-UNIT-01/EVA-LLaMA-3.33-70B-v0.0", + name: "EVA Llama 3.33 70B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-26", + last_updated: "2025-07-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.006, output: 2.006 }, + limit: { context: 16384, input: 16384, output: 16384 }, + }, + "EVA-UNIT-01/EVA-Qwen2.5-72B-v0.2": { + id: "EVA-UNIT-01/EVA-Qwen2.5-72B-v0.2", + name: "EVA-Qwen2.5-72B-v0.2", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.7989999999999999, output: 0.7989999999999999 }, + limit: { context: 16384, input: 16384, output: 8192 }, + }, + "EVA-UNIT-01/EVA-LLaMA-3.33-70B-v0.1": { + id: "EVA-UNIT-01/EVA-LLaMA-3.33-70B-v0.1", + name: "EVA-LLaMA-3.33-70B-v0.1", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.006, output: 2.006 }, + limit: { context: 16384, input: 16384, output: 16384 }, + }, + "EVA-UNIT-01/EVA-Qwen2.5-32B-v0.2": { + id: "EVA-UNIT-01/EVA-Qwen2.5-32B-v0.2", + name: "EVA-Qwen2.5-32B-v0.2", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-26", + last_updated: "2025-07-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.7989999999999999, output: 0.7989999999999999 }, + limit: { context: 16384, input: 16384, output: 8192 }, + }, + "huihui-ai/DeepSeek-R1-Distill-Qwen-32B-abliterated": { + id: "huihui-ai/DeepSeek-R1-Distill-Qwen-32B-abliterated", + name: "DeepSeek R1 Qwen Abliterated", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.4, output: 1.4 }, + limit: { context: 16384, input: 16384, output: 8192 }, + }, + "huihui-ai/DeepSeek-R1-Distill-Llama-70B-abliterated": { + id: "huihui-ai/DeepSeek-R1-Distill-Llama-70B-abliterated", + name: "DeepSeek R1 Llama 70B Abliterated", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.7, output: 0.7 }, + limit: { context: 16384, input: 16384, output: 8192 }, + }, + "huihui-ai/Llama-3.3-70B-Instruct-abliterated": { + id: "huihui-ai/Llama-3.3-70B-Instruct-abliterated", + name: "Llama 3.3 70B Instruct abliterated", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-08-08", + last_updated: "2025-08-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.7, output: 0.7 }, + limit: { context: 16384, input: 16384, output: 16384 }, + }, + "huihui-ai/Qwen2.5-32B-Instruct-abliterated": { + id: "huihui-ai/Qwen2.5-32B-Instruct-abliterated", + name: "Qwen 2.5 32B Abliterated", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-01-06", + last_updated: "2025-01-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.7, output: 0.7 }, + limit: { context: 32768, input: 32768, output: 8192 }, + }, + "huihui-ai/Llama-3.1-Nemotron-70B-Instruct-HF-abliterated": { + id: "huihui-ai/Llama-3.1-Nemotron-70B-Instruct-HF-abliterated", + name: "Nemotron 3.1 70B abliterated", + family: "nemotron", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.7, output: 0.7 }, + limit: { context: 16384, input: 16384, output: 16384 }, + }, + "xiaomi/mimo-v2-flash-thinking-original": { + id: "xiaomi/mimo-v2-flash-thinking-original", + name: "MiMo V2 Flash (Thinking) Original", + family: "mimo", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.102, output: 0.306 }, + limit: { context: 256000, input: 256000, output: 32768 }, + }, + "xiaomi/mimo-v2-flash-thinking": { + id: "xiaomi/mimo-v2-flash-thinking", + name: "MiMo V2 Flash (Thinking)", + family: "mimo", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.102, output: 0.306 }, + limit: { context: 256000, input: 256000, output: 32768 }, + }, + "xiaomi/mimo-v2-flash": { + id: "xiaomi/mimo-v2-flash", + name: "MiMo V2 Flash", + family: "mimo", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.102, output: 0.306 }, + limit: { context: 256000, input: 256000, output: 32768 }, + }, + "xiaomi/mimo-v2-flash-original": { + id: "xiaomi/mimo-v2-flash-original", + name: "MiMo V2 Flash Original", + family: "mimo", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.102, output: 0.306 }, + limit: { context: 256000, input: 256000, output: 32768 }, + }, + "tngtech/DeepSeek-TNG-R1T2-Chimera": { + id: "tngtech/DeepSeek-TNG-R1T2-Chimera", + name: "DeepSeek TNG R1T2 Chimera", + family: "tngtech", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.31, output: 0.31 }, + limit: { context: 128000, input: 128000, output: 8192 }, + }, + "tngtech/tng-r1t-chimera": { + id: "tngtech/tng-r1t-chimera", + name: "TNG R1T Chimera", + family: "tngtech", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-11-26", + last_updated: "2025-11-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 128000, input: 128000, output: 65536 }, + }, + "inflatebot/MN-12B-Mag-Mell-R1": { + id: "inflatebot/MN-12B-Mag-Mell-R1", + name: "Mag Mell R1", + family: "mistral-nemo", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2024-07-01", + last_updated: "2024-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.49299999999999994, output: 0.49299999999999994 }, + limit: { context: 16384, input: 16384, output: 8192 }, + }, + "failspy/Meta-Llama-3-70B-Instruct-abliterated-v3.5": { + id: "failspy/Meta-Llama-3-70B-Instruct-abliterated-v3.5", + name: "Llama 3 70B abliterated", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + release_date: "2025-07-26", + last_updated: "2025-07-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.7, output: 0.7 }, + limit: { context: 8192, input: 8192, output: 8192 }, + }, + }, + }, + abacus: { + id: "abacus", + env: ["ABACUS_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://routellm.abacus.ai/v1", + name: "Abacus", + doc: "https://abacus.ai/help/api", + models: { + "gpt-5.1-codex-max": { + id: "gpt-5.1-codex-max", + name: "GPT-5.1 Codex Max", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "claude-opus-4-5-20251101": { + id: "claude-opus-4-5-20251101", + name: "Claude Opus 4.5", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-01", + last_updated: "2025-11-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25 }, + limit: { context: 200000, output: 64000 }, + }, + "kimi-k2.5": { + id: "kimi-k2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3 }, + limit: { context: 262144, output: 32768 }, + }, + "gemini-3.1-flash-lite-preview": { + id: "gemini-3.1-flash-lite-preview", + name: "Gemini 3.1 Flash Lite Preview", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-03-01", + last_updated: "2026-03-01", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.5, cache_read: 0.025, cache_write: 1 }, + limit: { context: 1048576, output: 65536 }, + }, + "claude-sonnet-4-6": { + id: "claude-sonnet-4-6", + name: "Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-02-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 200000, output: 64000 }, + }, + "gemini-3.1-pro-preview": { + id: "gemini-3.1-pro-preview", + name: "Gemini 3.1 Pro Preview", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-19", + last_updated: "2026-02-19", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12 }, + limit: { context: 1048576, output: 65536 }, + }, + "gpt-5.3-chat-latest": { + id: "gpt-5.3-chat-latest", + name: "GPT-5.3 Chat Latest", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-01", + last_updated: "2026-03-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14 }, + limit: { context: 400000, output: 128000 }, + }, + "gemini-3-flash-preview": { + id: "gemini-3-flash-preview", + name: "Gemini 3 Flash Preview", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 3 }, + limit: { context: 1048576, output: 65536 }, + }, + "llama-3.3-70b-versatile": { + id: "llama-3.3-70b-versatile", + name: "Llama 3.3 70B Versatile", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.59, output: 0.79 }, + limit: { context: 128000, output: 32768 }, + }, + "gpt-5-mini": { + id: "gpt-5-mini", + name: "GPT-5 Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2 }, + limit: { context: 400000, output: 128000 }, + }, + "gpt-5-nano": { + id: "gpt-5-nano", + name: "GPT-5 Nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.4 }, + limit: { context: 400000, output: 128000 }, + }, + "gpt-5.3-codex": { + id: "gpt-5.3-codex", + name: "GPT-5.3 Codex", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "claude-sonnet-4-5-20250929": { + id: "claude-sonnet-4-5-20250929", + name: "Claude Sonnet 4.5", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 200000, output: 64000 }, + }, + "gemini-2.5-pro": { + id: "gemini-2.5-pro", + name: "Gemini 2.5 Pro", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-25", + last_updated: "2025-03-25", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 1048576, output: 65536 }, + }, + "grok-4-1-fast-non-reasoning": { + id: "grok-4-1-fast-non-reasoning", + name: "Grok 4.1 Fast (Non-Reasoning)", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-11-17", + last_updated: "2025-11-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5 }, + limit: { context: 2000000, output: 16384 }, + }, + "gpt-5.2": { + id: "gpt-5.2", + name: "GPT-5.2", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14 }, + limit: { context: 400000, output: 128000 }, + }, + "o3-pro": { + id: "o3-pro", + name: "o3-pro", + family: "o-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05", + release_date: "2025-06-10", + last_updated: "2025-06-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 20, output: 40 }, + limit: { context: 200000, output: 100000 }, + }, + "gpt-4o-mini": { + id: "gpt-4o-mini", + name: "GPT-4o Mini", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 128000, output: 16384 }, + }, + "qwen3-max": { + id: "qwen3-max", + name: "Qwen3 Max", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-05-28", + last_updated: "2025-05-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.2, output: 6 }, + limit: { context: 131072, output: 16384 }, + }, + "o4-mini": { + id: "o4-mini", + name: "o4-mini", + family: "o-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05", + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4 }, + limit: { context: 200000, output: 100000 }, + }, + "gpt-5.2-codex": { + id: "gpt-5.2-codex", + name: "GPT-5.2 Codex", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gemini-2.5-flash": { + id: "gemini-2.5-flash", + name: "Gemini 2.5 Flash", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-20", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5 }, + limit: { context: 1048576, output: 65536 }, + }, + "gpt-5.2-chat-latest": { + id: "gpt-5.2-chat-latest", + name: "GPT-5.2 Chat Latest", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-09-30", + release_date: "2026-01-01", + last_updated: "2026-01-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14 }, + limit: { context: 400000, output: 128000 }, + }, + "gpt-5.3-codex-xhigh": { + id: "gpt-5.3-codex-xhigh", + name: "GPT-5.3 Codex XHigh", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "grok-code-fast-1": { + id: "grok-code-fast-1", + name: "Grok Code Fast 1", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-09-01", + last_updated: "2025-09-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.5 }, + limit: { context: 256000, output: 16384 }, + }, + "gpt-5.1": { + id: "gpt-5.1", + name: "GPT-5.1", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 400000, output: 128000 }, + }, + "o3-mini": { + id: "o3-mini", + name: "o3-mini", + family: "o-mini", + attachment: false, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05", + release_date: "2024-12-20", + last_updated: "2025-01-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4 }, + limit: { context: 200000, output: 100000 }, + }, + "grok-4-0709": { + id: "grok-4-0709", + name: "Grok 4", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 256000, output: 16384 }, + }, + "route-llm": { + id: "route-llm", + name: "Route LLM", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-01-01", + last_updated: "2024-01-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 128000, output: 16384 }, + }, + "qwen-2.5-coder-32b": { + id: "qwen-2.5-coder-32b", + name: "Qwen 2.5 Coder 32B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-11-11", + last_updated: "2024-11-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.79, output: 0.79 }, + limit: { context: 128000, output: 8192 }, + }, + "gpt-5-codex": { + id: "gpt-5-codex", + name: "GPT-5 Codex", + family: "gpt", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-09-15", + last_updated: "2025-09-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "claude-opus-4-1-20250805": { + id: "claude-opus-4-1-20250805", + name: "Claude Opus 4.1", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75 }, + limit: { context: 200000, output: 32000 }, + }, + "gpt-5.4": { + id: "gpt-5.4", + name: "GPT-5.4", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 15 }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "gpt-5.1-chat-latest": { + id: "gpt-5.1-chat-latest", + name: "GPT-5.1 Chat Latest", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 400000, output: 128000 }, + }, + "claude-haiku-4-5-20251001": { + id: "claude-haiku-4-5-20251001", + name: "Claude Haiku 4.5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5 }, + limit: { context: 200000, output: 64000 }, + }, + "claude-sonnet-4-20250514": { + id: "claude-sonnet-4-20250514", + name: "Claude Sonnet 4", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-05-14", + last_updated: "2025-05-14", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 200000, output: 64000 }, + }, + "kimi-k2-turbo-preview": { + id: "kimi-k2-turbo-preview", + name: "Kimi K2 Turbo Preview", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-07-08", + last_updated: "2025-07-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 8 }, + limit: { context: 256000, output: 8192 }, + }, + "claude-opus-4-6": { + id: "claude-opus-4-6", + name: "Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25 }, + limit: { context: 200000, output: 128000 }, + }, + "gpt-4.1-nano": { + id: "gpt-4.1-nano", + name: "GPT-4.1 Nano", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 1047576, output: 32768 }, + }, + "claude-3-7-sonnet-20250219": { + id: "claude-3-7-sonnet-20250219", + name: "Claude Sonnet 3.7", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10-31", + release_date: "2025-02-19", + last_updated: "2025-02-19", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 200000, output: 64000 }, + }, + o3: { + id: "o3", + name: "o3", + family: "o", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05", + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8 }, + limit: { context: 200000, output: 100000 }, + }, + "gpt-5": { + id: "gpt-5", + name: "GPT-5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 400000, output: 128000 }, + }, + "claude-opus-4-20250514": { + id: "claude-opus-4-20250514", + name: "Claude Opus 4", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-05-14", + last_updated: "2025-05-14", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75 }, + limit: { context: 200000, output: 32000 }, + }, + "gpt-4.1": { + id: "gpt-4.1", + name: "GPT-4.1", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8 }, + limit: { context: 1047576, output: 32768 }, + }, + "gpt-4.1-mini": { + id: "gpt-4.1-mini", + name: "GPT-4.1 Mini", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.6 }, + limit: { context: 1047576, output: 32768 }, + }, + "gpt-5.1-codex": { + id: "gpt-5.1-codex", + name: "GPT-5.1 Codex", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gpt-4o-2024-11-20": { + id: "gpt-4o-2024-11-20", + name: "GPT-4o (2024-11-20)", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-11-20", + last_updated: "2024-11-20", + modalities: { input: ["text", "image", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10 }, + limit: { context: 128000, output: 16384 }, + }, + "grok-4-fast-non-reasoning": { + id: "grok-4-fast-non-reasoning", + name: "Grok 4 Fast (Non-Reasoning)", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5 }, + limit: { context: 2000000, output: 16384 }, + }, + "deepseek/deepseek-v3.1": { + id: "deepseek/deepseek-v3.1", + name: "DeepSeek V3.1", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 1.66 }, + limit: { context: 128000, output: 8192 }, + }, + "Qwen/QwQ-32B": { + id: "Qwen/QwQ-32B", + name: "QwQ 32B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2024-11-28", + last_updated: "2024-11-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 0.4 }, + limit: { context: 32768, output: 32768 }, + }, + "Qwen/Qwen3-235B-A22B-Instruct-2507": { + id: "Qwen/Qwen3-235B-A22B-Instruct-2507", + name: "Qwen3 235B A22B Instruct", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-07-01", + last_updated: "2025-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.13, output: 0.6 }, + limit: { context: 262144, output: 8192 }, + }, + "Qwen/Qwen3-32B": { + id: "Qwen/Qwen3-32B", + name: "Qwen3 32B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-04-29", + last_updated: "2025-04-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.09, output: 0.29 }, + limit: { context: 128000, output: 8192 }, + }, + "Qwen/qwen3-coder-480b-a35b-instruct": { + id: "Qwen/qwen3-coder-480b-a35b-instruct", + name: "Qwen3 Coder 480B A35B Instruct", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-07-22", + last_updated: "2025-07-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.29, output: 1.2 }, + limit: { context: 262144, output: 65536 }, + }, + "Qwen/Qwen2.5-72B-Instruct": { + id: "Qwen/Qwen2.5-72B-Instruct", + name: "Qwen 2.5 72B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-09-19", + last_updated: "2024-09-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.11, output: 0.38 }, + limit: { context: 128000, output: 8192 }, + }, + "zai-org/glm-4.7": { + id: "zai-org/glm-4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-06-01", + last_updated: "2025-06-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2 }, + limit: { context: 128000, output: 8192 }, + }, + "zai-org/glm-5": { + id: "zai-org/glm-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3.2 }, + limit: { context: 204800, output: 131072 }, + }, + "zai-org/glm-4.5": { + id: "zai-org/glm-4.5", + name: "GLM-4.5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2 }, + limit: { context: 128000, output: 8192 }, + }, + "zai-org/glm-4.6": { + id: "zai-org/glm-4.6", + name: "GLM-4.6", + family: "glm", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-03-01", + last_updated: "2025-03-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2 }, + limit: { context: 128000, output: 8192 }, + }, + "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo": { + id: "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo", + name: "Llama 3.1 405B Instruct Turbo", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 3.5, output: 3.5 }, + limit: { context: 128000, output: 4096 }, + }, + "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8": { + id: "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8", + name: "Llama 4 Maverick 17B 128E Instruct FP8", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.59 }, + limit: { context: 1000000, output: 32768 }, + }, + "meta-llama/Meta-Llama-3.1-8B-Instruct": { + id: "meta-llama/Meta-Llama-3.1-8B-Instruct", + name: "Llama 3.1 8B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0.05 }, + limit: { context: 128000, output: 4096 }, + }, + "deepseek-ai/DeepSeek-R1": { + id: "deepseek-ai/DeepSeek-R1", + name: "DeepSeek R1", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 3, output: 7 }, + limit: { context: 128000, output: 8192 }, + }, + "deepseek-ai/DeepSeek-V3.2": { + id: "deepseek-ai/DeepSeek-V3.2", + name: "DeepSeek V3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-06-15", + last_updated: "2025-06-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 0.4 }, + limit: { context: 128000, output: 8192 }, + }, + "deepseek-ai/DeepSeek-V3.1-Terminus": { + id: "deepseek-ai/DeepSeek-V3.1-Terminus", + name: "DeepSeek V3.1 Terminus", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-06-01", + last_updated: "2025-06-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 1 }, + limit: { context: 128000, output: 8192 }, + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "GPT-OSS 120B", + family: "gpt-oss", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.08, output: 0.44 }, + limit: { context: 128000, output: 32768 }, + }, + }, + }, + "perplexity-agent": { + id: "perplexity-agent", + env: ["PERPLEXITY_API_KEY"], + npm: "@ai-sdk/openai", + api: "https://api.perplexity.ai/v1", + name: "Perplexity Agent", + doc: "https://docs.perplexity.ai/docs/agent-api/models", + models: { + "perplexity/sonar": { + id: "perplexity/sonar", + name: "Sonar", + family: "sonar", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-09-01", + release_date: "2024-01-01", + last_updated: "2025-09-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2.5, cache_read: 0.0625 }, + limit: { context: 128000, output: 8192 }, + }, + "xai/grok-4-1-fast-non-reasoning": { + id: "xai/grok-4-1-fast-non-reasoning", + name: "Grok 4.1 Fast (Non-Reasoning)", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-11-19", + last_updated: "2025-11-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + "nvidia/nemotron-3-super-120b-a12b": { + id: "nvidia/nemotron-3-super-120b-a12b", + name: "Nemotron 3 Super 120B", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2026-02", + release_date: "2026-03-11", + last_updated: "2026-03-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 2.5 }, + limit: { context: 1000000, output: 32000 }, + }, + "openai/gpt-5.5": { + id: "openai/gpt-5.5", + name: "GPT-5.5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-12-01", + release_date: "2026-04-23", + last_updated: "2026-04-23", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 30, cache_read: 0.5 }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "openai/gpt-5-mini": { + id: "openai/gpt-5-mini", + name: "GPT-5 Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.025 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "openai/gpt-5.2": { + id: "openai/gpt-5.2", + name: "GPT-5.2", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "openai/gpt-5.1": { + id: "openai/gpt-5.1", + name: "GPT-5.1", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "openai/gpt-5.4": { + id: "openai/gpt-5.4", + name: "GPT-5.4", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 15, cache_read: 0.25 }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "google/gemini-3.1-pro-preview": { + id: "google/gemini-3.1-pro-preview", + name: "Gemini 3.1 Pro Preview", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-19", + last_updated: "2026-02-19", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2, context_over_200k: { input: 4, output: 18, cache_read: 0.4 } }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-3-flash-preview": { + id: "google/gemini-3-flash-preview", + name: "Gemini 3 Flash Preview", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 0.5, + output: 3, + cache_read: 0.05, + context_over_200k: { input: 0.5, output: 3, cache_read: 0.05 }, + }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-2.5-pro": { + id: "google/gemini-2.5-pro", + name: "Gemini 2.5 Pro", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-20", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 1.25, + output: 10, + cache_read: 0.125, + context_over_200k: { input: 2.5, output: 15, cache_read: 0.25 }, + }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-2.5-flash": { + id: "google/gemini-2.5-flash", + name: "Gemini 2.5 Flash", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-20", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5, cache_read: 0.03 }, + limit: { context: 1048576, output: 65536 }, + }, + "anthropic/claude-haiku-4-5": { + id: "anthropic/claude-haiku-4-5", + name: "Claude Haiku 4.5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-sonnet-4-6": { + id: "anthropic/claude-sonnet-4-6", + name: "Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-02-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-opus-4-7": { + id: "anthropic/claude-opus-4-7", + name: "Claude Opus 4.7", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5 }, + limit: { context: 1000000, output: 128000 }, + }, + "anthropic/claude-opus-4-5": { + id: "anthropic/claude-opus-4-5", + name: "Claude Opus 4.5", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-24", + last_updated: "2025-11-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-opus-4-6": { + id: "anthropic/claude-opus-4-6", + name: "Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5 }, + limit: { context: 200000, output: 128000 }, + }, + "anthropic/claude-sonnet-4-5": { + id: "anthropic/claude-sonnet-4-5", + name: "Claude Sonnet 4.5", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3 }, + limit: { context: 200000, output: 64000 }, + }, + }, + }, + "siliconflow-cn": { + id: "siliconflow-cn", + env: ["SILICONFLOW_CN_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.siliconflow.cn/v1", + name: "SiliconFlow (China)", + doc: "https://cloud.siliconflow.com/models", + models: { + "Kwaipilot/KAT-Dev": { + id: "Kwaipilot/KAT-Dev", + name: "Kwaipilot/KAT-Dev", + family: "kat-coder", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-27", + last_updated: "2026-01-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.6 }, + limit: { context: 128000, output: 128000 }, + }, + "Qwen/Qwen3.5-397B-A17B": { + id: "Qwen/Qwen3.5-397B-A17B", + name: "Qwen/Qwen3.5-397B-A17B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-16", + last_updated: "2026-02-16", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.29, output: 1.74 }, + limit: { context: 262144, output: 65536 }, + }, + "Qwen/Qwen3.5-35B-A3B": { + id: "Qwen/Qwen3.5-35B-A3B", + name: "Qwen/Qwen3.5-35B-A3B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-25", + last_updated: "2026-02-25", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.23, output: 1.86 }, + limit: { context: 262144, output: 65536 }, + }, + "Qwen/Qwen3.5-122B-A10B": { + id: "Qwen/Qwen3.5-122B-A10B", + name: "Qwen/Qwen3.5-122B-A10B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-26", + last_updated: "2026-02-26", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.29, output: 2.32 }, + limit: { context: 262144, output: 65536 }, + }, + "Qwen/Qwen3.5-9B": { + id: "Qwen/Qwen3.5-9B", + name: "Qwen/Qwen3.5-9B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-03-03", + last_updated: "2026-03-03", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.22, output: 1.74 }, + limit: { context: 262144, output: 65536 }, + }, + "Qwen/Qwen3.5-27B": { + id: "Qwen/Qwen3.5-27B", + name: "Qwen/Qwen3.5-27B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-25", + last_updated: "2026-02-25", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.26, output: 2.09 }, + limit: { context: 262144, output: 65536 }, + }, + "Qwen/Qwen3.5-4B": { + id: "Qwen/Qwen3.5-4B", + name: "Qwen/Qwen3.5-4B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-03-03", + last_updated: "2026-03-03", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 65536 }, + }, + "Qwen/Qwen3.6-35B-A3B": { + id: "Qwen/Qwen3.6-35B-A3B", + name: "Qwen/Qwen3.6-35B-A3B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-04-17", + last_updated: "2026-04-17", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.23, output: 1.86 }, + limit: { context: 262144, output: 65536 }, + }, + "Qwen/Qwen2.5-72B-Instruct": { + id: "Qwen/Qwen2.5-72B-Instruct", + name: "Qwen/Qwen2.5-72B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-09-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.59, output: 0.59 }, + limit: { context: 33000, output: 4000 }, + }, + "Qwen/Qwen3-Coder-480B-A35B-Instruct": { + id: "Qwen/Qwen3-Coder-480B-A35B-Instruct", + name: "Qwen/Qwen3-Coder-480B-A35B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-31", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3-VL-8B-Instruct": { + id: "Qwen/Qwen3-VL-8B-Instruct", + name: "Qwen/Qwen3-VL-8B-Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-15", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.18, output: 0.68 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3-VL-32B-Instruct": { + id: "Qwen/Qwen3-VL-32B-Instruct", + name: "Qwen/Qwen3-VL-32B-Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-21", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.6 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3-VL-30B-A3B-Thinking": { + id: "Qwen/Qwen3-VL-30B-A3B-Thinking", + name: "Qwen/Qwen3-VL-30B-A3B-Thinking", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-11", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.29, output: 1 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen2.5-14B-Instruct": { + id: "Qwen/Qwen2.5-14B-Instruct", + name: "Qwen/Qwen2.5-14B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-09-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 33000, output: 4000 }, + }, + "Qwen/Qwen3-VL-235B-A22B-Instruct": { + id: "Qwen/Qwen3-VL-235B-A22B-Instruct", + name: "Qwen/Qwen3-VL-235B-A22B-Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-04", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.5 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3-Next-80B-A3B-Thinking": { + id: "Qwen/Qwen3-Next-80B-A3B-Thinking", + name: "Qwen/Qwen3-Next-80B-A3B-Thinking", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-25", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.57 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen2.5-VL-32B-Instruct": { + id: "Qwen/Qwen2.5-VL-32B-Instruct", + name: "Qwen/Qwen2.5-VL-32B-Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-03-24", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 0.27 }, + limit: { context: 131000, output: 131000 }, + }, + "Qwen/Qwen3-Omni-30B-A3B-Thinking": { + id: "Qwen/Qwen3-Omni-30B-A3B-Thinking", + name: "Qwen/Qwen3-Omni-30B-A3B-Thinking", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-04", + last_updated: "2025-11-25", + modalities: { input: ["text", "image", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 66000, output: 66000 }, + }, + "Qwen/Qwen3-235B-A22B-Thinking-2507": { + id: "Qwen/Qwen3-235B-A22B-Thinking-2507", + name: "Qwen/Qwen3-235B-A22B-Thinking-2507", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-28", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.13, output: 0.6 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen2.5-32B-Instruct": { + id: "Qwen/Qwen2.5-32B-Instruct", + name: "Qwen/Qwen2.5-32B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-09-19", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.18, output: 0.18 }, + limit: { context: 33000, output: 4000 }, + }, + "Qwen/Qwen2.5-72B-Instruct-128K": { + id: "Qwen/Qwen2.5-72B-Instruct-128K", + name: "Qwen/Qwen2.5-72B-Instruct-128K", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-09-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.59, output: 0.59 }, + limit: { context: 131000, output: 4000 }, + }, + "Qwen/Qwen3-14B": { + id: "Qwen/Qwen3-14B", + name: "Qwen/Qwen3-14B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-30", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.07, output: 0.28 }, + limit: { context: 131000, output: 131000 }, + }, + "Qwen/Qwen3-Omni-30B-A3B-Instruct": { + id: "Qwen/Qwen3-Omni-30B-A3B-Instruct", + name: "Qwen/Qwen3-Omni-30B-A3B-Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-04", + last_updated: "2025-11-25", + modalities: { input: ["text", "image", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 66000, output: 66000 }, + }, + "Qwen/Qwen3-Coder-30B-A3B-Instruct": { + id: "Qwen/Qwen3-Coder-30B-A3B-Instruct", + name: "Qwen/Qwen3-Coder-30B-A3B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-01", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.07, output: 0.28 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3-32B": { + id: "Qwen/Qwen3-32B", + name: "Qwen/Qwen3-32B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-30", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.57 }, + limit: { context: 131000, output: 131000 }, + }, + "Qwen/Qwen3-235B-A22B-Instruct-2507": { + id: "Qwen/Qwen3-235B-A22B-Instruct-2507", + name: "Qwen/Qwen3-235B-A22B-Instruct-2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-23", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.09, output: 0.6 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3-30B-A3B-Instruct-2507": { + id: "Qwen/Qwen3-30B-A3B-Instruct-2507", + name: "Qwen/Qwen3-30B-A3B-Instruct-2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-30", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.09, output: 0.3 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3-8B": { + id: "Qwen/Qwen3-8B", + name: "Qwen/Qwen3-8B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-30", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.06, output: 0.06 }, + limit: { context: 131000, output: 131000 }, + }, + "Qwen/Qwen3-Next-80B-A3B-Instruct": { + id: "Qwen/Qwen3-Next-80B-A3B-Instruct", + name: "Qwen/Qwen3-Next-80B-A3B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 1.4 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3-VL-8B-Thinking": { + id: "Qwen/Qwen3-VL-8B-Thinking", + name: "Qwen/Qwen3-VL-8B-Thinking", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-15", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.18, output: 2 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3-Omni-30B-A3B-Captioner": { + id: "Qwen/Qwen3-Omni-30B-A3B-Captioner", + name: "Qwen/Qwen3-Omni-30B-A3B-Captioner", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-04", + last_updated: "2025-11-25", + modalities: { input: ["audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 66000, output: 66000 }, + }, + "Qwen/QwQ-32B": { + id: "Qwen/QwQ-32B", + name: "Qwen/QwQ-32B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-03-06", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.58 }, + limit: { context: 131000, output: 131000 }, + }, + "Qwen/Qwen3-VL-30B-A3B-Instruct": { + id: "Qwen/Qwen3-VL-30B-A3B-Instruct", + name: "Qwen/Qwen3-VL-30B-A3B-Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-05", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.29, output: 1 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen2.5-Coder-32B-Instruct": { + id: "Qwen/Qwen2.5-Coder-32B-Instruct", + name: "Qwen/Qwen2.5-Coder-32B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-11-11", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.18, output: 0.18 }, + limit: { context: 33000, output: 4000 }, + }, + "Qwen/Qwen2.5-7B-Instruct": { + id: "Qwen/Qwen2.5-7B-Instruct", + name: "Qwen/Qwen2.5-7B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-09-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.05 }, + limit: { context: 33000, output: 4000 }, + }, + "Qwen/Qwen3-VL-235B-A22B-Thinking": { + id: "Qwen/Qwen3-VL-235B-A22B-Thinking", + name: "Qwen/Qwen3-VL-235B-A22B-Thinking", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-04", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.45, output: 3.5 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3-30B-A3B-Thinking-2507": { + id: "Qwen/Qwen3-30B-A3B-Thinking-2507", + name: "Qwen/Qwen3-30B-A3B-Thinking-2507", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-31", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.09, output: 0.3 }, + limit: { context: 262000, output: 131000 }, + }, + "Qwen/Qwen3-VL-32B-Thinking": { + id: "Qwen/Qwen3-VL-32B-Thinking", + name: "Qwen/Qwen3-VL-32B-Thinking", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-21", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.5 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen2.5-VL-72B-Instruct": { + id: "Qwen/Qwen2.5-VL-72B-Instruct", + name: "Qwen/Qwen2.5-VL-72B-Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-01-28", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.59, output: 0.59 }, + limit: { context: 131000, output: 4000 }, + }, + "stepfun-ai/Step-3.5-Flash": { + id: "stepfun-ai/Step-3.5-Flash", + name: "stepfun-ai/Step-3.5-Flash", + family: "step", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 262000, output: 262000 }, + }, + "zai-org/GLM-4.5V": { + id: "zai-org/GLM-4.5V", + name: "zai-org/GLM-4.5V", + family: "glm", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-13", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.86 }, + limit: { context: 66000, output: 66000 }, + }, + "zai-org/GLM-4.6": { + id: "zai-org/GLM-4.6", + name: "zai-org/GLM-4.6", + family: "glm", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-04", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 1.9 }, + limit: { context: 205000, output: 205000 }, + }, + "zai-org/GLM-4.6V": { + id: "zai-org/GLM-4.6V", + name: "zai-org/GLM-4.6V", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-12-07", + last_updated: "2025-12-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.9 }, + limit: { context: 131000, output: 131000 }, + }, + "zai-org/GLM-4.5-Air": { + id: "zai-org/GLM-4.5-Air", + name: "zai-org/GLM-4.5-Air", + family: "glm-air", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-28", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.86 }, + limit: { context: 131000, output: 131000 }, + }, + "inclusionAI/Ling-flash-2.0": { + id: "inclusionAI/Ling-flash-2.0", + name: "inclusionAI/Ling-flash-2.0", + family: "ling", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.57 }, + limit: { context: 131000, output: 131000 }, + }, + "inclusionAI/Ling-mini-2.0": { + id: "inclusionAI/Ling-mini-2.0", + name: "inclusionAI/Ling-mini-2.0", + family: "ling", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-10", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.07, output: 0.28 }, + limit: { context: 131000, output: 131000 }, + }, + "inclusionAI/Ring-flash-2.0": { + id: "inclusionAI/Ring-flash-2.0", + name: "inclusionAI/Ring-flash-2.0", + family: "ring", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-29", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.57 }, + limit: { context: 131000, output: 131000 }, + }, + "ascend-tribe/pangu-pro-moe": { + id: "ascend-tribe/pangu-pro-moe", + name: "ascend-tribe/pangu-pro-moe", + family: "pangu", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-07-02", + last_updated: "2026-01-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.6 }, + limit: { context: 128000, output: 128000 }, + }, + "tencent/Hunyuan-MT-7B": { + id: "tencent/Hunyuan-MT-7B", + name: "tencent/Hunyuan-MT-7B", + family: "hunyuan", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 33000, output: 33000 }, + }, + "tencent/Hunyuan-A13B-Instruct": { + id: "tencent/Hunyuan-A13B-Instruct", + name: "tencent/Hunyuan-A13B-Instruct", + family: "hunyuan", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-06-30", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.57 }, + limit: { context: 131000, output: 131000 }, + }, + "Pro/zai-org/GLM-4.7": { + id: "Pro/zai-org/GLM-4.7", + name: "Pro/zai-org/GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 2.2 }, + limit: { context: 205000, output: 205000 }, + }, + "Pro/zai-org/GLM-5.1": { + id: "Pro/zai-org/GLM-5.1", + name: "Pro/zai-org/GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-04-08", + last_updated: "2026-04-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.4, output: 4.4, cache_write: 0 }, + limit: { context: 205000, output: 205000 }, + }, + "Pro/zai-org/GLM-5": { + id: "Pro/zai-org/GLM-5", + name: "Pro/zai-org/GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3.2 }, + limit: { context: 205000, output: 205000 }, + }, + "Pro/deepseek-ai/DeepSeek-V3": { + id: "Pro/deepseek-ai/DeepSeek-V3", + name: "Pro/deepseek-ai/DeepSeek-V3", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-12-26", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1 }, + limit: { context: 164000, output: 164000 }, + }, + "Pro/deepseek-ai/DeepSeek-R1": { + id: "Pro/deepseek-ai/DeepSeek-R1", + name: "Pro/deepseek-ai/DeepSeek-R1", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-05-28", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 2.18 }, + limit: { context: 164000, output: 164000 }, + }, + "Pro/deepseek-ai/DeepSeek-V3.2": { + id: "Pro/deepseek-ai/DeepSeek-V3.2", + name: "Pro/deepseek-ai/DeepSeek-V3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-03", + last_updated: "2025-12-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 0.42 }, + limit: { context: 164000, output: 164000 }, + }, + "Pro/deepseek-ai/DeepSeek-V3.1-Terminus": { + id: "Pro/deepseek-ai/DeepSeek-V3.1-Terminus", + name: "Pro/deepseek-ai/DeepSeek-V3.1-Terminus", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-29", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 1 }, + limit: { context: 164000, output: 164000 }, + }, + "Pro/moonshotai/Kimi-K2-Thinking": { + id: "Pro/moonshotai/Kimi-K2-Thinking", + name: "Pro/moonshotai/Kimi-K2-Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-11-07", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.55, output: 2.5 }, + limit: { context: 262000, output: 262000 }, + }, + "Pro/moonshotai/Kimi-K2.6": { + id: "Pro/moonshotai/Kimi-K2.6", + name: "Pro/moonshotai/Kimi-K2.6", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4, cache_read: 0.16 }, + limit: { context: 262000, output: 262000 }, + }, + "Pro/moonshotai/Kimi-K2-Instruct-0905": { + id: "Pro/moonshotai/Kimi-K2-Instruct-0905", + name: "Pro/moonshotai/Kimi-K2-Instruct-0905", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-08", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2 }, + limit: { context: 262000, output: 262000 }, + }, + "Pro/moonshotai/Kimi-K2.5": { + id: "Pro/moonshotai/Kimi-K2.5", + name: "Pro/moonshotai/Kimi-K2.5", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.45, output: 2.25 }, + limit: { context: 262000, output: 262000 }, + }, + "Pro/MiniMaxAI/MiniMax-M2.5": { + id: "Pro/MiniMaxAI/MiniMax-M2.5", + name: "Pro/MiniMaxAI/MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: false, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-02-13", + last_updated: "2026-02-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.22 }, + limit: { context: 192000, output: 131000 }, + }, + "Pro/MiniMaxAI/MiniMax-M2.1": { + id: "Pro/MiniMaxAI/MiniMax-M2.1", + name: "Pro/MiniMaxAI/MiniMax-M2.1", + family: "minimax", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 197000, output: 131000 }, + }, + "PaddlePaddle/PaddleOCR-VL": { + id: "PaddlePaddle/PaddleOCR-VL", + name: "PaddlePaddle/PaddleOCR-VL", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-10-16", + last_updated: "2025-10-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 16384, output: 16384 }, + }, + "PaddlePaddle/PaddleOCR-VL-1.5": { + id: "PaddlePaddle/PaddleOCR-VL-1.5", + name: "PaddlePaddle/PaddleOCR-VL-1.5", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2026-01-29", + last_updated: "2026-01-29", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 16384, output: 16384 }, + }, + "deepseek-ai/DeepSeek-OCR": { + id: "deepseek-ai/DeepSeek-OCR", + name: "deepseek-ai/DeepSeek-OCR", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-10-20", + last_updated: "2025-10-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 8192, output: 8192 }, + }, + "deepseek-ai/DeepSeek-V3.1-Terminus": { + id: "deepseek-ai/DeepSeek-V3.1-Terminus", + name: "deepseek-ai/DeepSeek-V3.1-Terminus", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-29", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 1 }, + limit: { context: 164000, output: 164000 }, + }, + "deepseek-ai/DeepSeek-V3.2": { + id: "deepseek-ai/DeepSeek-V3.2", + name: "deepseek-ai/DeepSeek-V3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-03", + last_updated: "2025-12-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 0.42 }, + limit: { context: 164000, output: 164000 }, + }, + "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B": { + id: "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B", + name: "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-01-20", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 131000, output: 131000 }, + }, + "deepseek-ai/DeepSeek-R1": { + id: "deepseek-ai/DeepSeek-R1", + name: "deepseek-ai/DeepSeek-R1", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-05-28", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 2.18 }, + limit: { context: 164000, output: 164000 }, + }, + "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B": { + id: "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", + name: "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-01-20", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.18, output: 0.18 }, + limit: { context: 131000, output: 131000 }, + }, + "deepseek-ai/DeepSeek-V3": { + id: "deepseek-ai/DeepSeek-V3", + name: "deepseek-ai/DeepSeek-V3", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-12-26", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1 }, + limit: { context: 164000, output: 164000 }, + }, + "deepseek-ai/deepseek-vl2": { + id: "deepseek-ai/deepseek-vl2", + name: "deepseek-ai/deepseek-vl2", + family: "deepseek", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-12-13", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.15 }, + limit: { context: 4000, output: 4000 }, + }, + "baidu/ERNIE-4.5-300B-A47B": { + id: "baidu/ERNIE-4.5-300B-A47B", + name: "baidu/ERNIE-4.5-300B-A47B", + family: "ernie", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-02", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.28, output: 1.1 }, + limit: { context: 131000, output: 131000 }, + }, + "THUDM/GLM-Z1-32B-0414": { + id: "THUDM/GLM-Z1-32B-0414", + name: "THUDM/GLM-Z1-32B-0414", + family: "glm-z", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.57 }, + limit: { context: 131000, output: 131000 }, + }, + "THUDM/GLM-4-32B-0414": { + id: "THUDM/GLM-4-32B-0414", + name: "THUDM/GLM-4-32B-0414", + family: "glm", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 0.27 }, + limit: { context: 33000, output: 33000 }, + }, + "THUDM/GLM-4-9B-0414": { + id: "THUDM/GLM-4-9B-0414", + name: "THUDM/GLM-4-9B-0414", + family: "glm", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.086, output: 0.086 }, + limit: { context: 33000, output: 33000 }, + }, + "THUDM/GLM-Z1-9B-0414": { + id: "THUDM/GLM-Z1-9B-0414", + name: "THUDM/GLM-Z1-9B-0414", + family: "glm-z", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.086, output: 0.086 }, + limit: { context: 131000, output: 131000 }, + }, + "moonshotai/Kimi-K2-Instruct-0905": { + id: "moonshotai/Kimi-K2-Instruct-0905", + name: "moonshotai/Kimi-K2-Instruct-0905", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-08", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2 }, + limit: { context: 262000, output: 262000 }, + }, + "moonshotai/Kimi-K2-Thinking": { + id: "moonshotai/Kimi-K2-Thinking", + name: "moonshotai/Kimi-K2-Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-11-07", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.55, output: 2.5 }, + limit: { context: 262000, output: 262000 }, + }, + "ByteDance-Seed/Seed-OSS-36B-Instruct": { + id: "ByteDance-Seed/Seed-OSS-36B-Instruct", + name: "ByteDance-Seed/Seed-OSS-36B-Instruct", + family: "seed", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-04", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.21, output: 0.57 }, + limit: { context: 262000, output: 262000 }, + }, + }, + }, + submodel: { + id: "submodel", + env: ["SUBMODEL_INSTAGEN_ACCESS_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://llm.submodel.ai/v1", + name: "submodel", + doc: "https://submodel.gitbook.io", + models: { + "Qwen/Qwen3-235B-A22B-Instruct-2507": { + id: "Qwen/Qwen3-235B-A22B-Instruct-2507", + name: "Qwen3 235B A22B Instruct 2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-08-23", + last_updated: "2025-08-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.3 }, + limit: { context: 262144, output: 131072 }, + }, + "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8": { + id: "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8", + name: "Qwen3 Coder 480B A35B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-08-23", + last_updated: "2025-08-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.8 }, + limit: { context: 262144, output: 262144 }, + }, + "Qwen/Qwen3-235B-A22B-Thinking-2507": { + id: "Qwen/Qwen3-235B-A22B-Thinking-2507", + name: "Qwen3 235B A22B Thinking 2507", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-23", + last_updated: "2025-08-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.6 }, + limit: { context: 262144, output: 131072 }, + }, + "zai-org/GLM-4.5-Air": { + id: "zai-org/GLM-4.5-Air", + name: "GLM 4.5 Air", + family: "glm-air", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.5 }, + limit: { context: 131072, output: 131072 }, + }, + "zai-org/GLM-4.5-FP8": { + id: "zai-org/GLM-4.5-FP8", + name: "GLM 4.5 FP8", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.8 }, + limit: { context: 131072, output: 131072 }, + }, + "deepseek-ai/DeepSeek-V3.1": { + id: "deepseek-ai/DeepSeek-V3.1", + name: "DeepSeek V3.1", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-23", + last_updated: "2025-08-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.8 }, + limit: { context: 75000, output: 163840 }, + }, + "deepseek-ai/DeepSeek-V3-0324": { + id: "deepseek-ai/DeepSeek-V3-0324", + name: "DeepSeek V3 0324", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-08-23", + last_updated: "2025-08-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.8 }, + limit: { context: 75000, output: 163840 }, + }, + "deepseek-ai/DeepSeek-R1-0528": { + id: "deepseek-ai/DeepSeek-R1-0528", + name: "DeepSeek R1 0528", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-23", + last_updated: "2025-08-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 2.15 }, + limit: { context: 75000, output: 163840 }, + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "GPT OSS 120B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-23", + last_updated: "2025-08-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.5 }, + limit: { context: 131072, output: 32768 }, + }, + }, + }, + "minimax-coding-plan": { + id: "minimax-coding-plan", + env: ["MINIMAX_API_KEY"], + npm: "@ai-sdk/anthropic", + api: "https://api.minimax.io/anthropic/v1", + name: "MiniMax Coding Plan (minimax.io)", + doc: "https://platform.minimax.io/docs/coding-plan/intro", + models: { + "MiniMax-M2": { + id: "MiniMax-M2", + name: "MiniMax-M2", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-10-27", + last_updated: "2025-10-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 196608, output: 128000 }, + }, + "MiniMax-M2.5": { + id: "MiniMax-M2.5", + name: "MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMax-M2.7": { + id: "MiniMax-M2.7", + name: "MiniMax-M2.7", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMax-M2.7-highspeed": { + id: "MiniMax-M2.7-highspeed", + name: "MiniMax-M2.7-highspeed", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMax-M2.1": { + id: "MiniMax-M2.1", + name: "MiniMax-M2.1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMax-M2.5-highspeed": { + id: "MiniMax-M2.5-highspeed", + name: "MiniMax-M2.5-highspeed", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-13", + last_updated: "2026-02-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + }, + }, + perplexity: { + id: "perplexity", + env: ["PERPLEXITY_API_KEY"], + npm: "@ai-sdk/perplexity", + name: "Perplexity", + doc: "https://docs.perplexity.ai", + models: { + "sonar-pro": { + id: "sonar-pro", + name: "Sonar Pro", + family: "sonar-pro", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-09-01", + release_date: "2024-01-01", + last_updated: "2025-09-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 200000, output: 8192 }, + }, + "sonar-deep-research": { + id: "sonar-deep-research", + name: "Perplexity Sonar Deep Research", + attachment: false, + reasoning: true, + tool_call: false, + temperature: false, + knowledge: "2025-01", + release_date: "2025-02-01", + last_updated: "2025-09-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, reasoning: 3 }, + limit: { context: 128000, output: 32768 }, + }, + sonar: { + id: "sonar", + name: "Sonar", + family: "sonar", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-09-01", + release_date: "2024-01-01", + last_updated: "2025-09-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 1 }, + limit: { context: 128000, output: 4096 }, + }, + "sonar-reasoning-pro": { + id: "sonar-reasoning-pro", + name: "Sonar Reasoning Pro", + family: "sonar-reasoning", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2025-09-01", + release_date: "2024-01-01", + last_updated: "2025-09-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8 }, + limit: { context: 128000, output: 4096 }, + }, + }, + }, + deepseek: { + id: "deepseek", + env: ["DEEPSEEK_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.deepseek.com", + name: "DeepSeek", + doc: "https://api-docs.deepseek.com/quick_start/pricing", + models: { + "deepseek-chat": { + id: "deepseek-chat", + name: "DeepSeek Chat", + family: "deepseek", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-09", + release_date: "2025-12-01", + last_updated: "2026-02-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.28, cache_read: 0.028 }, + limit: { context: 1000000, output: 384000 }, + }, + "deepseek-reasoner": { + id: "deepseek-reasoner", + name: "DeepSeek Reasoner", + family: "deepseek-thinking", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-09", + release_date: "2025-12-01", + last_updated: "2026-02-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.28, cache_read: 0.028 }, + limit: { context: 1000000, output: 384000 }, + }, + "deepseek-v4-flash": { + id: "deepseek-v4-flash", + name: "DeepSeek V4 Flash", + family: "deepseek-flash", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.28, cache_read: 0.028 }, + limit: { context: 1000000, output: 384000 }, + }, + "deepseek-v4-pro": { + id: "deepseek-v4-pro", + name: "DeepSeek V4 Pro", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.74, output: 3.48, cache_read: 0.145 }, + limit: { context: 1000000, output: 384000 }, + }, + }, + }, + llama: { + id: "llama", + env: ["LLAMA_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.llama.com/compat/v1/", + name: "Llama", + doc: "https://llama.developer.meta.com/docs/models", + models: { + "llama-3.3-70b-instruct": { + id: "llama-3.3-70b-instruct", + name: "Llama-3.3-70B-Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "cerebras-llama-4-maverick-17b-128e-instruct": { + id: "cerebras-llama-4-maverick-17b-128e-instruct", + name: "Cerebras-Llama-4-Maverick-17B-128E-Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "llama-3.3-8b-instruct": { + id: "llama-3.3-8b-instruct", + name: "Llama-3.3-8B-Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "cerebras-llama-4-scout-17b-16e-instruct": { + id: "cerebras-llama-4-scout-17b-16e-instruct", + name: "Cerebras-Llama-4-Scout-17B-16E-Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "groq-llama-4-maverick-17b-128e-instruct": { + id: "groq-llama-4-maverick-17b-128e-instruct", + name: "Groq-Llama-4-Maverick-17B-128E-Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "llama-4-scout-17b-16e-instruct-fp8": { + id: "llama-4-scout-17b-16e-instruct-fp8", + name: "Llama-4-Scout-17B-16E-Instruct-FP8", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "llama-4-maverick-17b-128e-instruct-fp8": { + id: "llama-4-maverick-17b-128e-instruct-fp8", + name: "Llama-4-Maverick-17B-128E-Instruct-FP8", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + }, + }, + openrouter: { + id: "openrouter", + env: ["OPENROUTER_API_KEY"], + npm: "@openrouter/ai-sdk-provider", + api: "https://openrouter.ai/api/v1", + name: "OpenRouter", + doc: "https://openrouter.ai/models", + models: { + "liquid/lfm-2.5-1.2b-instruct:free": { + id: "liquid/lfm-2.5-1.2b-instruct:free", + name: "LFM2.5-1.2B-Instruct (free)", + family: "liquid", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-06", + release_date: "2026-01-20", + last_updated: "2026-01-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 32768 }, + }, + "liquid/lfm-2.5-1.2b-thinking:free": { + id: "liquid/lfm-2.5-1.2b-thinking:free", + name: "LFM2.5-1.2B-Thinking (free)", + family: "liquid", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2025-06", + release_date: "2026-01-20", + last_updated: "2026-01-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 32768 }, + }, + "deepseek/deepseek-chat-v3.1": { + id: "deepseek/deepseek-chat-v3.1", + name: "DeepSeek-V3.1", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-08-21", + last_updated: "2025-08-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.8 }, + limit: { context: 163840, output: 163840 }, + }, + "deepseek/deepseek-r1-distill-llama-70b": { + id: "deepseek/deepseek-r1-distill-llama-70b", + name: "DeepSeek R1 Distill Llama 70B", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-01-23", + last_updated: "2025-01-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 8192, output: 8192 }, + }, + "deepseek/deepseek-r1": { + id: "deepseek/deepseek-r1", + name: "DeepSeek: R1", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + knowledge: "2024-07", + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.7, output: 2.5 }, + limit: { context: 64000, output: 16000 }, + }, + "deepseek/deepseek-v3.2-speciale": { + id: "deepseek/deepseek-v3.2-speciale", + name: "DeepSeek V3.2 Speciale", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 0.41 }, + limit: { context: 163840, output: 65536 }, + }, + "deepseek/deepseek-v3.2": { + id: "deepseek/deepseek-v3.2", + name: "DeepSeek V3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.28, output: 0.4 }, + limit: { context: 163840, output: 65536 }, + }, + "deepseek/deepseek-v3.1-terminus:exacto": { + id: "deepseek/deepseek-v3.1-terminus:exacto", + name: "DeepSeek V3.1 Terminus (exacto)", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-09-22", + last_updated: "2025-09-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 1 }, + limit: { context: 131072, output: 65536 }, + }, + "deepseek/deepseek-chat-v3-0324": { + id: "deepseek/deepseek-chat-v3-0324", + name: "DeepSeek V3 0324", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-03-24", + last_updated: "2025-03-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 16384, output: 8192 }, + }, + "deepseek/deepseek-v3.1-terminus": { + id: "deepseek/deepseek-v3.1-terminus", + name: "DeepSeek V3.1 Terminus", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-09-22", + last_updated: "2025-09-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 1 }, + limit: { context: 131072, output: 65536 }, + }, + "openrouter/owl-alpha": { + id: "openrouter/owl-alpha", + name: "Owl Alpha", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-28", + last_updated: "2026-04-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 1048756, output: 262144 }, + status: "alpha", + }, + "openrouter/pareto-code": { + id: "openrouter/pareto-code", + name: "Pareto Code Router", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 200000, output: 200000 }, + }, + "openrouter/elephant-alpha": { + id: "openrouter/elephant-alpha", + name: "Elephant (free)", + family: "elephant", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-13", + last_updated: "2026-04-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 32768 }, + }, + "openrouter/free": { + id: "openrouter/free", + name: "Free Models Router", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-01", + last_updated: "2026-02-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 200000, input: 200000, output: 8000 }, + }, + "arcee-ai/trinity-large-thinking": { + id: "arcee-ai/trinity-large-thinking", + name: "Trinity Large Thinking", + family: "trinity", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-01", + last_updated: "2026-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.22, output: 0.85 }, + limit: { context: 262144, output: 80000 }, + }, + "arcee-ai/trinity-large-preview:free": { + id: "arcee-ai/trinity-large-preview:free", + name: "Trinity Large Preview", + family: "trinity", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-06", + release_date: "2026-01-28", + last_updated: "2026-01-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 131072 }, + }, + "cognitivecomputations/dolphin-mistral-24b-venice-edition:free": { + id: "cognitivecomputations/dolphin-mistral-24b-venice-edition:free", + name: "Uncensored (free)", + family: "mistral", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-07-09", + last_updated: "2026-01-31", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 32768, output: 32768 }, + }, + "bytedance-seed/seedream-4.5": { + id: "bytedance-seed/seedream-4.5", + name: "Seedream 4.5", + family: "seed", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-06", + release_date: "2025-12-23", + last_updated: "2026-01-31", + modalities: { input: ["image", "text"], output: ["image"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 4096, output: 4096 }, + }, + "black-forest-labs/flux.2-max": { + id: "black-forest-labs/flux.2-max", + name: "FLUX.2 Max", + family: "flux", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-06", + release_date: "2025-12-16", + last_updated: "2026-01-31", + modalities: { input: ["image", "text"], output: ["image"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 46864, output: 46864 }, + }, + "black-forest-labs/flux.2-flex": { + id: "black-forest-labs/flux.2-flex", + name: "FLUX.2 Flex", + family: "flux", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-06", + release_date: "2025-11-25", + last_updated: "2026-01-31", + modalities: { input: ["image", "text"], output: ["image"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 67344, output: 67344 }, + }, + "black-forest-labs/flux.2-pro": { + id: "black-forest-labs/flux.2-pro", + name: "FLUX.2 Pro", + family: "flux", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-06", + release_date: "2025-11-25", + last_updated: "2026-01-31", + modalities: { input: ["image", "text"], output: ["image"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 46864, output: 46864 }, + }, + "black-forest-labs/flux.2-klein-4b": { + id: "black-forest-labs/flux.2-klein-4b", + name: "FLUX.2 Klein 4B", + family: "flux", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-06", + release_date: "2026-01-14", + last_updated: "2026-01-31", + modalities: { input: ["image", "text"], output: ["image"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 40960, output: 40960 }, + }, + "nousresearch/hermes-3-llama-3.1-405b:free": { + id: "nousresearch/hermes-3-llama-3.1-405b:free", + name: "Hermes 3 405B Instruct (free)", + family: "hermes", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2023-12", + release_date: "2024-08-16", + last_updated: "2024-08-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 131072 }, + }, + "nousresearch/hermes-4-405b": { + id: "nousresearch/hermes-4-405b", + name: "Hermes 4 405B", + family: "hermes", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2025-08-25", + last_updated: "2025-08-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3 }, + limit: { context: 131072, output: 131072 }, + }, + "nousresearch/hermes-4-70b": { + id: "nousresearch/hermes-4-70b", + name: "Hermes 4 70B", + family: "hermes", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-12", + release_date: "2025-08-25", + last_updated: "2025-08-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.13, output: 0.4 }, + limit: { context: 131072, output: 131072 }, + }, + "stepfun/step-3.5-flash": { + id: "stepfun/step-3.5-flash", + name: "Step 3.5 Flash", + family: "step", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-01-29", + last_updated: "2026-01-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3, cache_read: 0.02 }, + limit: { context: 256000, output: 256000 }, + }, + "mistralai/mistral-small-3.1-24b-instruct": { + id: "mistralai/mistral-small-3.1-24b-instruct", + name: "Mistral Small 3.1 24B Instruct", + family: "mistral-small", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-03-17", + last_updated: "2025-03-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 8192 }, + }, + "mistralai/devstral-2512": { + id: "mistralai/devstral-2512", + name: "Devstral 2 2512", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-12", + release_date: "2025-09-12", + last_updated: "2025-09-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 262144, output: 262144 }, + }, + "mistralai/codestral-2508": { + id: "mistralai/codestral-2508", + name: "Codestral 2508", + family: "codestral", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2025-08-01", + last_updated: "2025-08-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.9 }, + limit: { context: 256000, output: 256000 }, + }, + "mistralai/mistral-medium-3.1": { + id: "mistralai/mistral-medium-3.1", + name: "Mistral Medium 3.1", + family: "mistral-medium", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2025-08-12", + last_updated: "2025-08-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2 }, + limit: { context: 262144, output: 262144 }, + }, + "mistralai/mistral-small-2603": { + id: "mistralai/mistral-small-2603", + name: "Mistral Small 4", + family: "mistral-small", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2026-03-16", + last_updated: "2026-03-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 262144, output: 262144 }, + }, + "mistralai/mistral-medium-3": { + id: "mistralai/mistral-medium-3", + name: "Mistral Medium 3", + family: "mistral-medium", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2025-05-07", + last_updated: "2025-05-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2 }, + limit: { context: 131072, output: 131072 }, + }, + "mistralai/devstral-small-2505": { + id: "mistralai/devstral-small-2505", + name: "Devstral Small", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-05", + release_date: "2025-05-07", + last_updated: "2025-05-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.06, output: 0.12 }, + limit: { context: 128000, output: 128000 }, + }, + "mistralai/mistral-small-3.2-24b-instruct": { + id: "mistralai/mistral-small-3.2-24b-instruct", + name: "Mistral Small 3.2 24B Instruct", + family: "mistral-small", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-06-20", + last_updated: "2025-06-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 96000, output: 8192 }, + }, + "mistralai/devstral-medium-2507": { + id: "mistralai/devstral-medium-2507", + name: "Devstral Medium", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2025-07-10", + last_updated: "2025-07-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 2 }, + limit: { context: 131072, output: 131072 }, + }, + "mistralai/devstral-small-2507": { + id: "mistralai/devstral-small-2507", + name: "Devstral Small 1.1", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2025-07-10", + last_updated: "2025-07-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 131072, output: 131072 }, + }, + "meta-llama/llama-3.2-11b-vision-instruct": { + id: "meta-llama/llama-3.2-11b-vision-instruct", + name: "Llama 3.2 11B Vision Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-12", + release_date: "2024-09-25", + last_updated: "2024-09-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 8192 }, + }, + "meta-llama/llama-3.2-3b-instruct:free": { + id: "meta-llama/llama-3.2-3b-instruct:free", + name: "Llama 3.2 3B Instruct (free)", + family: "llama", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-12", + release_date: "2024-09-25", + last_updated: "2024-09-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 131072 }, + }, + "meta-llama/llama-3.3-70b-instruct:free": { + id: "meta-llama/llama-3.3-70b-instruct:free", + name: "Llama 3.3 70B Instruct (free)", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-12", + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 131072 }, + }, + "x-ai/grok-4.20-multi-agent-beta": { + id: "x-ai/grok-4.20-multi-agent-beta", + name: "Grok 4.20 Multi - Agent Beta", + family: "grok", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2026-03-12", + last_updated: "2026-03-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6, cache_read: 0.2, context_over_200k: { input: 4, output: 12 } }, + limit: { context: 2000000, output: 30000 }, + status: "beta", + }, + "x-ai/grok-4-fast": { + id: "x-ai/grok-4-fast", + name: "Grok 4 Fast", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-08-19", + last_updated: "2025-08-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05, cache_write: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + "x-ai/grok-code-fast-1": { + id: "x-ai/grok-code-fast-1", + name: "Grok Code Fast 1", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-08", + release_date: "2025-08-26", + last_updated: "2025-08-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.5, cache_read: 0.02 }, + limit: { context: 256000, output: 10000 }, + }, + "x-ai/grok-3-beta": { + id: "x-ai/grok-3-beta", + name: "Grok 3 Beta", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.75, cache_write: 15 }, + limit: { context: 131072, output: 8192 }, + }, + "x-ai/grok-4": { + id: "x-ai/grok-4", + name: "Grok 4", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.75, cache_write: 15 }, + limit: { context: 256000, output: 64000 }, + }, + "x-ai/grok-3-mini": { + id: "x-ai/grok-3-mini", + name: "Grok 3 Mini", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.5, cache_read: 0.075, cache_write: 0.5 }, + limit: { context: 131072, output: 8192 }, + }, + "x-ai/grok-4.1-fast": { + id: "x-ai/grok-4.1-fast", + name: "Grok 4.1 Fast", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-11-19", + last_updated: "2025-11-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05, cache_write: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + "x-ai/grok-4.20-beta": { + id: "x-ai/grok-4.20-beta", + name: "Grok 4.20 Beta", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-12", + last_updated: "2026-03-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6, cache_read: 0.2, context_over_200k: { input: 4, output: 12 } }, + limit: { context: 2000000, output: 30000 }, + status: "beta", + }, + "x-ai/grok-3-mini-beta": { + id: "x-ai/grok-3-mini-beta", + name: "Grok 3 Mini Beta", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.5, cache_read: 0.075, cache_write: 0.5 }, + limit: { context: 131072, output: 8192 }, + }, + "x-ai/grok-3": { + id: "x-ai/grok-3", + name: "Grok 3", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.75, cache_write: 15 }, + limit: { context: 131072, output: 8192 }, + }, + "tencent/hy3-preview": { + id: "tencent/hy3-preview", + name: "Hy3 preview", + family: "Hy", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-20", + last_updated: "2026-04-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.066, output: 0.26, cache_read: 0.029, cache_write: 0.029 }, + limit: { context: 256000, output: 64000 }, + }, + "poolside/laguna-m.1:free": { + id: "poolside/laguna-m.1:free", + name: "Laguna M.1", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-04-28", + last_updated: "2026-04-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 131072, output: 8192 }, + }, + "poolside/laguna-xs.2:free": { + id: "poolside/laguna-xs.2:free", + name: "Laguna XS.2", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-04-28", + last_updated: "2026-04-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 131072, output: 8192 }, + }, + "prime-intellect/intellect-3": { + id: "prime-intellect/intellect-3", + name: "Intellect 3", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-01-15", + last_updated: "2025-01-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 1.1 }, + limit: { context: 131072, output: 8192 }, + }, + "nvidia/nemotron-3-super-120b-a12b": { + id: "nvidia/nemotron-3-super-120b-a12b", + name: "Nemotron 3 Super", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2026-03-11", + last_updated: "2026-03-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.5 }, + limit: { context: 262144, output: 262144 }, + }, + "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free": { + id: "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free", + name: "Nemotron 3 Nano Omni (free)", + family: "nemotron", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-28", + last_updated: "2026-04-28", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 256000, output: 65536 }, + }, + "nvidia/nemotron-3-nano-30b-a3b:free": { + id: "nvidia/nemotron-3-nano-30b-a3b:free", + name: "Nemotron 3 Nano 30B A3B (free)", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-11", + release_date: "2025-12-14", + last_updated: "2026-01-31", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 256000, output: 256000 }, + }, + "nvidia/nemotron-nano-9b-v2:free": { + id: "nvidia/nemotron-nano-9b-v2:free", + name: "Nemotron Nano 9B V2 (free)", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-09", + release_date: "2025-09-05", + last_updated: "2025-08-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 128000 }, + }, + "nvidia/nemotron-3-super-120b-a12b:free": { + id: "nvidia/nemotron-3-super-120b-a12b:free", + name: "Nemotron 3 Super (free)", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2026-03-11", + last_updated: "2026-03-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 262144 }, + }, + "nvidia/nemotron-nano-9b-v2": { + id: "nvidia/nemotron-nano-9b-v2", + name: "nvidia-nemotron-nano-9b-v2", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-09", + release_date: "2025-08-18", + last_updated: "2025-08-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.04, output: 0.16 }, + limit: { context: 131072, output: 131072 }, + }, + "nvidia/nemotron-nano-12b-v2-vl:free": { + id: "nvidia/nemotron-nano-12b-v2-vl:free", + name: "Nemotron Nano 12B 2 VL (free)", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-11", + release_date: "2025-10-28", + last_updated: "2026-01-31", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 128000 }, + }, + "inception/mercury-edit-2": { + id: "inception/mercury-edit-2", + name: "Mercury Edit 2", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2026-03-30", + last_updated: "2026-03-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 0.75, cache_read: 0.025 }, + limit: { context: 128000, output: 8192 }, + }, + "inception/mercury-2": { + id: "inception/mercury-2", + name: "Mercury 2", + family: "mercury", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-03-04", + last_updated: "2026-03-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 0.75, cache_read: 0.025 }, + limit: { context: 128000, output: 50000 }, + }, + "openai/gpt-5.1-codex-max": { + id: "openai/gpt-5.1-codex-max", + name: "GPT-5.1-Codex-Max", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 9, cache_read: 0.11 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.2-chat": { + id: "openai/gpt-5.2-chat", + name: "GPT-5.2 Chat", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-oss-120b:exacto": { + id: "openai/gpt-oss-120b:exacto", + name: "GPT OSS 120B (exacto)", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.24 }, + limit: { context: 131072, output: 32768 }, + }, + "openai/gpt-5-chat": { + id: "openai/gpt-5-chat", + name: "GPT-5 Chat (latest)", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: true, + temperature: true, + knowledge: "2024-09-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.2-pro": { + id: "openai/gpt-5.2-pro", + name: "GPT-5.2 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 21, output: 168 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5-mini": { + id: "openai/gpt-5-mini", + name: "GPT-5 Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-10-01", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5-nano": { + id: "openai/gpt-5-nano", + name: "GPT-5 Nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-10-01", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.4 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.3-codex": { + id: "openai/gpt-5.3-codex", + name: "GPT-5.3-Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-02-24", + last_updated: "2026-02-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.2": { + id: "openai/gpt-5.2", + name: "GPT-5.2", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-oss-20b:free": { + id: "openai/gpt-oss-20b:free", + name: "gpt-oss-20b (free)", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2026-01-31", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 32768 }, + }, + "openai/gpt-4o-mini": { + id: "openai/gpt-4o-mini", + name: "GPT-4o-mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6, cache_read: 0.08 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-5.4-mini": { + id: "openai/gpt-5.4-mini", + name: "GPT-5.4 Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.75, output: 4.5, cache_read: 0.075 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.1-chat": { + id: "openai/gpt-5.1-chat", + name: "GPT-5.1 Chat", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/o4-mini": { + id: "openai/o4-mini", + name: "o4 Mini", + family: "o-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-06", + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.28 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-5.4-nano": { + id: "openai/gpt-5.4-nano", + name: "GPT-5.4 Nano", + family: "gpt-nano", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.25, cache_read: 0.02 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.2-codex": { + id: "openai/gpt-5.2-codex", + name: "GPT-5.2-Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-01-14", + last_updated: "2026-01-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.1-codex-mini": { + id: "openai/gpt-5.1-codex-mini", + name: "GPT-5.1-Codex-Mini", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.025 }, + limit: { context: 400000, output: 100000 }, + }, + "openai/gpt-5-image": { + id: "openai/gpt-5-image", + name: "GPT-5 Image", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-10-01", + release_date: "2025-10-14", + last_updated: "2025-10-14", + modalities: { input: ["text", "image", "pdf"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 5, output: 10, cache_read: 1.25 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.1": { + id: "openai/gpt-5.1", + name: "GPT-5.1", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.4-pro": { + id: "openai/gpt-5.4-pro", + name: "GPT-5.4 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 180, cache_read: 30 }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "openai/gpt-5-codex": { + id: "openai/gpt-5-codex", + name: "GPT-5 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-10-01", + release_date: "2025-09-15", + last_updated: "2025-09-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-oss-20b": { + id: "openai/gpt-oss-20b", + name: "GPT OSS 20B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.2 }, + limit: { context: 131072, output: 32768 }, + }, + "openai/gpt-5-pro": { + id: "openai/gpt-5-pro", + name: "GPT-5 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-10-06", + last_updated: "2025-10-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 120 }, + limit: { context: 400000, output: 272000 }, + }, + "openai/gpt-oss-120b:free": { + id: "openai/gpt-oss-120b:free", + name: "gpt-oss-120b (free)", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 32768 }, + }, + "openai/gpt-5": { + id: "openai/gpt-5", + name: "GPT-5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-10-01", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-oss-safeguard-20b": { + id: "openai/gpt-oss-safeguard-20b", + name: "GPT OSS Safeguard 20B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-10-29", + last_updated: "2025-10-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.075, output: 0.3 }, + limit: { context: 131072, output: 65536 }, + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "GPT OSS 120B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.072, output: 0.28 }, + limit: { context: 131072, output: 32768 }, + }, + "openai/gpt-4.1": { + id: "openai/gpt-4.1", + name: "GPT-4.1", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 1047576, output: 32768 }, + }, + "openai/gpt-4.1-mini": { + id: "openai/gpt-4.1-mini", + name: "GPT-4.1 Mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.6, cache_read: 0.1 }, + limit: { context: 1047576, output: 32768 }, + }, + "openai/gpt-5.1-codex": { + id: "openai/gpt-5.1-codex", + name: "GPT-5.1-Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "z-ai/glm-4.7": { + id: "z-ai/glm-4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_details" }, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2, cache_read: 0.11 }, + limit: { context: 204800, output: 131072 }, + }, + "z-ai/glm-4.5-air:free": { + id: "z-ai/glm-4.5-air:free", + name: "GLM 4.5 Air (free)", + family: "glm-air", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 96000 }, + }, + "z-ai/glm-5": { + id: "z-ai/glm-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3.2, cache_read: 0.2 }, + limit: { context: 202752, output: 131000 }, + }, + "z-ai/glm-5.1": { + id: "z-ai/glm-5.1", + name: "GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-04-07", + last_updated: "2026-04-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.4, output: 4.4, cache_read: 0.26 }, + limit: { context: 202752, output: 131072 }, + }, + "z-ai/glm-4.5": { + id: "z-ai/glm-4.5", + name: "GLM 4.5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2 }, + limit: { context: 128000, output: 96000 }, + }, + "z-ai/glm-4.6:exacto": { + id: "z-ai/glm-4.6:exacto", + name: "GLM 4.6 (exacto)", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-09", + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 1.9, cache_read: 0.11 }, + limit: { context: 200000, output: 128000 }, + }, + "z-ai/glm-4.5-air": { + id: "z-ai/glm-4.5-air", + name: "GLM 4.5 Air", + family: "glm-air", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 1.1 }, + limit: { context: 128000, output: 96000 }, + }, + "z-ai/glm-5-turbo": { + id: "z-ai/glm-5-turbo", + name: "GLM-5-Turbo", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-03-16", + last_updated: "2026-03-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.96, output: 3.2, cache_read: 0.192, cache_write: 0 }, + limit: { context: 202752, output: 131072 }, + }, + "z-ai/glm-4.5v": { + id: "z-ai/glm-4.5v", + name: "GLM 4.5V", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-08-11", + last_updated: "2025-08-11", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 1.8 }, + limit: { context: 64000, output: 16384 }, + }, + "z-ai/glm-4.6": { + id: "z-ai/glm-4.6", + name: "GLM 4.6", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-09", + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2, cache_read: 0.11 }, + limit: { context: 200000, output: 128000 }, + }, + "z-ai/glm-4.7-flash": { + id: "z-ai/glm-4.7-flash", + name: "GLM-4.7-Flash", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_details" }, + structured_output: true, + temperature: true, + release_date: "2026-01-19", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.4 }, + limit: { context: 200000, output: 65535 }, + }, + "sourceful/riverflow-v2-standard-preview": { + id: "sourceful/riverflow-v2-standard-preview", + name: "Riverflow V2 Standard Preview", + family: "sourceful", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-06", + release_date: "2025-12-08", + last_updated: "2026-01-28", + modalities: { input: ["text", "image"], output: ["image"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 8192, output: 8192 }, + }, + "sourceful/riverflow-v2-fast-preview": { + id: "sourceful/riverflow-v2-fast-preview", + name: "Riverflow V2 Fast Preview", + family: "sourceful", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-06", + release_date: "2025-12-08", + last_updated: "2026-01-28", + modalities: { input: ["text", "image"], output: ["image"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 8192, output: 8192 }, + }, + "sourceful/riverflow-v2-max-preview": { + id: "sourceful/riverflow-v2-max-preview", + name: "Riverflow V2 Max Preview", + family: "sourceful", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-06", + release_date: "2025-12-08", + last_updated: "2026-01-28", + modalities: { input: ["text", "image"], output: ["image"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 8192, output: 8192 }, + }, + "minimax/minimax-m2.7": { + id: "minimax/minimax-m2.7", + name: "MiniMax M2.7", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.06, cache_write: 0.375 }, + limit: { context: 204800, output: 131072 }, + }, + "minimax/minimax-m2": { + id: "minimax/minimax-m2", + name: "MiniMax M2", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_details" }, + structured_output: true, + temperature: true, + release_date: "2025-10-23", + last_updated: "2025-10-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.28, output: 1.15, cache_read: 0.28, cache_write: 1.15 }, + limit: { context: 196600, output: 118000 }, + }, + "minimax/minimax-01": { + id: "minimax/minimax-01", + name: "MiniMax-01", + family: "minimax", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-01-15", + last_updated: "2025-01-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 1.1 }, + limit: { context: 1000000, output: 1000000 }, + }, + "minimax/minimax-m2.1": { + id: "minimax/minimax-m2.1", + name: "MiniMax M2.1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_details" }, + structured_output: true, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 204800, output: 131072 }, + }, + "minimax/minimax-m2.5:free": { + id: "minimax/minimax-m2.5:free", + name: "MiniMax M2.5 (free)", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_details" }, + structured_output: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "minimax/minimax-m1": { + id: "minimax/minimax-m1", + name: "MiniMax M1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 2.2 }, + limit: { context: 1000000, output: 40000 }, + }, + "minimax/minimax-m2.5": { + id: "minimax/minimax-m2.5", + name: "MiniMax M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_details" }, + structured_output: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.03 }, + limit: { context: 204800, output: 131072 }, + }, + "qwen/qwen3-coder-plus": { + id: "qwen/qwen3-coder-plus", + name: "Qwen3 Coder Plus", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-23", + last_updated: "2025-09-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.65, output: 3.25, cache_read: 0.13, cache_write: 0.8125 }, + limit: { context: 1000000, output: 65536 }, + }, + "qwen/qwen3.5-397b-a17b": { + id: "qwen/qwen3.5-397b-a17b", + name: "Qwen3.5 397B A17B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-16", + last_updated: "2026-02-16", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3.6 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen/qwen2.5-vl-72b-instruct": { + id: "qwen/qwen2.5-vl-72b-instruct", + name: "Qwen2.5 VL 72B Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-02-01", + last_updated: "2025-02-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 32768, output: 8192 }, + }, + "qwen/qwen-plus": { + id: "qwen/qwen-plus", + name: "Qwen: Qwen-Plus", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + knowledge: "2024-04", + release_date: "2025-01-25", + last_updated: "2025-01-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.26, output: 0.78, cache_read: 0.052, cache_write: 0.325 }, + limit: { context: 1000000, output: 32768 }, + }, + "qwen/qwen3.5-flash-02-23": { + id: "qwen/qwen3.5-flash-02-23", + name: "Qwen: Qwen3.5-Flash", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-25", + last_updated: "2026-02-25", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.065, output: 0.26 }, + limit: { context: 1000000, output: 65536 }, + }, + "qwen/qwen3.6-plus": { + id: "qwen/qwen3.6-plus", + name: "Qwen3.6 Plus", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.325, output: 1.95, cache_read: 0.0325, cache_write: 0.40625 }, + limit: { context: 1000000, output: 65536 }, + }, + "qwen/qwen3-max": { + id: "qwen/qwen3-max", + name: "Qwen3 Max", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.2, output: 6, cache_read: 0.156, cache_write: 0.975 }, + limit: { context: 262144, output: 32768 }, + }, + "qwen/qwen3-coder:exacto": { + id: "qwen/qwen3-coder:exacto", + name: "Qwen3 Coder (exacto)", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.38, output: 1.53 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen/qwen3-30b-a3b-instruct-2507": { + id: "qwen/qwen3-30b-a3b-instruct-2507", + name: "Qwen3 30B A3B Instruct 2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-29", + last_updated: "2025-07-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.8 }, + limit: { context: 262000, output: 262000 }, + }, + "qwen/qwen-3.6-27b": { + id: "qwen/qwen-3.6-27b", + name: "Qwen3.6 27B", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.195, output: 1.56 }, + limit: { context: 262144, output: 81920 }, + }, + "qwen/qwen3-235b-a22b-thinking-2507": { + id: "qwen/qwen3-235b-a22b-thinking-2507", + name: "Qwen3 235B A22B Thinking 2507", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-25", + last_updated: "2025-07-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.078, output: 0.312 }, + limit: { context: 262144, output: 81920 }, + }, + "qwen/qwen3-next-80b-a3b-thinking": { + id: "qwen/qwen3-next-80b-a3b-thinking", + name: "Qwen3 Next 80B A3B Thinking", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-11", + last_updated: "2025-09-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 1.4 }, + limit: { context: 262144, output: 262144 }, + }, + "qwen/qwen3-30b-a3b-thinking-2507": { + id: "qwen/qwen3-30b-a3b-thinking-2507", + name: "Qwen3 30B A3B Thinking 2507", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-29", + last_updated: "2025-07-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.8 }, + limit: { context: 262000, output: 262000 }, + }, + "qwen/qwen3-coder-flash": { + id: "qwen/qwen3-coder-flash", + name: "Qwen3 Coder Flash", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.5, cache_read: 0.039, cache_write: 0.24375 }, + limit: { context: 128000, output: 66536 }, + }, + "qwen/qwen3-next-80b-a3b-instruct": { + id: "qwen/qwen3-next-80b-a3b-instruct", + name: "Qwen3 Next 80B A3B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-11", + last_updated: "2025-09-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 1.4 }, + limit: { context: 262144, output: 262144 }, + }, + "qwen/qwen-2.5-coder-32b-instruct": { + id: "qwen/qwen-2.5-coder-32b-instruct", + name: "Qwen2.5 Coder 32B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-11-11", + last_updated: "2024-11-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 32768, output: 8192 }, + }, + "qwen/qwen3-coder-30b-a3b-instruct": { + id: "qwen/qwen3-coder-30b-a3b-instruct", + name: "Qwen3 Coder 30B A3B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-31", + last_updated: "2025-07-31", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.27 }, + limit: { context: 160000, output: 65536 }, + }, + "qwen/qwen3-coder": { + id: "qwen/qwen3-coder", + name: "Qwen3 Coder", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 262144, output: 66536 }, + }, + "qwen/qwen3.5-plus-02-15": { + id: "qwen/qwen3.5-plus-02-15", + name: "Qwen3.5 Plus 2026-02-15", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-16", + last_updated: "2026-02-16", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2.4 }, + limit: { context: 1000000, output: 65536 }, + }, + "qwen/qwen3-235b-a22b-07-25": { + id: "qwen/qwen3-235b-a22b-07-25", + name: "Qwen3 235B A22B Instruct 2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04-28", + last_updated: "2025-07-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.85 }, + limit: { context: 262144, output: 131072 }, + }, + "google/gemini-2.5-pro-preview-05-06": { + id: "google/gemini-2.5-pro-preview-05-06", + name: "Gemini 2.5 Pro Preview 05-06", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-05-06", + last_updated: "2025-05-06", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.31 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-3.1-pro-preview-customtools": { + id: "google/gemini-3.1-pro-preview-customtools", + name: "Gemini 3.1 Pro Preview Custom Tools", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_details" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-19", + last_updated: "2026-02-19", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, reasoning: 12, context_over_200k: { input: 4, output: 18, cache_read: 0.4 } }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemma-3-4b-it:free": { + id: "google/gemma-3-4b-it:free", + name: "Gemma 3 4B (free)", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2025-03-13", + last_updated: "2025-03-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 32768, output: 8192 }, + }, + "google/gemini-2.5-flash-lite-preview-09-2025": { + id: "google/gemini-2.5-flash-lite-preview-09-2025", + name: "Gemini 2.5 Flash Lite Preview 09-25", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.025 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-2.0-flash-001": { + id: "google/gemini-2.0-flash-001", + name: "Gemini 2.0 Flash", + family: "gemini-flash", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-06", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.025 }, + limit: { context: 1048576, output: 8192 }, + }, + "google/gemma-3n-e4b-it": { + id: "google/gemma-3n-e4b-it", + name: "Gemma 3n 4B", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-06", + release_date: "2025-05-20", + last_updated: "2025-05-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0.04 }, + limit: { context: 32768, output: 32768 }, + }, + "google/gemini-3.1-flash-lite-preview": { + id: "google/gemini-3.1-flash-lite-preview", + name: "Gemini 3.1 Flash Lite Preview", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-03-03", + last_updated: "2026-03-03", + modalities: { input: ["text", "image", "video", "pdf", "audio"], output: ["text"] }, + open_weights: false, + cost: { + input: 0.25, + output: 1.5, + reasoning: 1.5, + cache_read: 0.025, + cache_write: 0.083, + input_audio: 0.5, + output_audio: 0.5, + }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemma-3n-e4b-it:free": { + id: "google/gemma-3n-e4b-it:free", + name: "Gemma 3n 4B (free)", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-06", + release_date: "2025-05-20", + last_updated: "2025-05-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 8192, output: 2000 }, + }, + "google/gemini-3.1-pro-preview": { + id: "google/gemini-3.1-pro-preview", + name: "Gemini 3.1 Pro Preview", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_details" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-19", + last_updated: "2026-02-19", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, reasoning: 12, context_over_200k: { input: 4, output: 18, cache_read: 0.4 } }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-3-flash-preview": { + id: "google/gemini-3-flash-preview", + name: "Gemini 3 Flash Preview", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_details" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 3, cache_read: 0.05 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-3-pro-preview": { + id: "google/gemini-3-pro-preview", + name: "Gemini 3 Pro Preview", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_details" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-11-18", + last_updated: "2025-11", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12 }, + limit: { context: 1050000, output: 66000 }, + }, + "google/gemma-3n-e2b-it:free": { + id: "google/gemma-3n-e2b-it:free", + name: "Gemma 3n 2B (free)", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-06", + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 8192, output: 2000 }, + }, + "google/gemma-2-9b-it": { + id: "google/gemma-2-9b-it", + name: "Gemma 2 9B", + family: "gemma", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-06", + release_date: "2024-06-28", + last_updated: "2024-06-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.09 }, + limit: { context: 8192, output: 8192 }, + }, + "google/gemma-4-31b-it": { + id: "google/gemma-4-31b-it", + name: "Gemma 4 31B", + family: "gemma", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.4 }, + limit: { context: 262144, output: 262144 }, + }, + "google/gemini-2.5-pro-preview-06-05": { + id: "google/gemini-2.5-pro-preview-06-05", + name: "Gemini 2.5 Pro Preview 06-05", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-06-05", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.31 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemma-3-12b-it": { + id: "google/gemma-3-12b-it", + name: "Gemma 3 12B", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-03-13", + last_updated: "2025-03-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.1 }, + limit: { context: 131072, output: 131072 }, + }, + "google/gemma-3-27b-it:free": { + id: "google/gemma-3-27b-it:free", + name: "Gemma 3 27B (free)", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-03-12", + last_updated: "2025-03-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 8192 }, + }, + "google/gemini-2.5-flash": { + id: "google/gemini-2.5-flash", + name: "Gemini 2.5 Flash", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-07-17", + last_updated: "2025-07-17", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5, cache_read: 0.0375 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-3.1-flash-image-preview": { + id: "google/gemini-3.1-flash-image-preview", + name: "Gemini 3.1 Flash Image Preview (Nano Banana 2)", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-26", + last_updated: "2026-02-26", + modalities: { input: ["text", "image"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 0.5, output: 3 }, + limit: { context: 65536, output: 65536 }, + }, + "google/gemma-4-31b-it:free": { + id: "google/gemma-4-31b-it:free", + name: "Gemma 4 31B (free)", + family: "gemma", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 32768 }, + }, + "google/gemma-3-12b-it:free": { + id: "google/gemma-3-12b-it:free", + name: "Gemma 3 12B (free)", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2025-03-13", + last_updated: "2025-03-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 32768, output: 8192 }, + }, + "google/gemma-3-4b-it": { + id: "google/gemma-3-4b-it", + name: "Gemma 3 4B", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2025-03-13", + last_updated: "2025-03-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.01703, output: 0.06815 }, + limit: { context: 96000, output: 96000 }, + }, + "google/gemini-2.5-flash-preview-09-2025": { + id: "google/gemini-2.5-flash-preview-09-2025", + name: "Gemini 2.5 Flash Preview 09-25", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5, cache_read: 0.031 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemma-3-27b-it": { + id: "google/gemma-3-27b-it", + name: "Gemma 3 27B", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-03-12", + last_updated: "2025-03-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.04, output: 0.15 }, + limit: { context: 96000, output: 96000 }, + }, + "google/gemma-4-26b-a4b-it": { + id: "google/gemma-4-26b-a4b-it", + name: "Gemma 4 26B A4B", + family: "gemma", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-03", + last_updated: "2026-04-03", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.13, output: 0.4 }, + limit: { context: 262144, output: 262144 }, + }, + "google/gemma-4-26b-a4b-it:free": { + id: "google/gemma-4-26b-a4b-it:free", + name: "Gemma 4 26B A4B (free)", + family: "gemma", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-03", + last_updated: "2026-04-03", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 32768 }, + }, + "google/gemini-2.5-flash-lite": { + id: "google/gemini-2.5-flash-lite", + name: "Gemini 2.5 Flash Lite", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.025 }, + limit: { context: 1048576, output: 65536 }, + }, + "moonshotai/kimi-k2.5": { + id: "moonshotai/kimi-k2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_details" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3, cache_read: 0.1 }, + limit: { context: 262144, output: 262144 }, + }, + "moonshotai/kimi-k2-0905": { + id: "moonshotai/kimi-k2-0905", + name: "Kimi K2 Instruct 0905", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5 }, + limit: { context: 262144, output: 16384 }, + }, + "moonshotai/kimi-k2.6": { + id: "moonshotai/kimi-k2.6", + name: "Kimi K2.6", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_details" }, + structured_output: true, + temperature: true, + release_date: "2026-04-20", + last_updated: "2026-04-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4, cache_read: 0.16 }, + limit: { context: 262144, output: 262144 }, + }, + "moonshotai/kimi-k2-0905:exacto": { + id: "moonshotai/kimi-k2-0905:exacto", + name: "Kimi K2 Instruct 0905 (exacto)", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5 }, + limit: { context: 262144, output: 16384 }, + }, + "moonshotai/kimi-k2": { + id: "moonshotai/kimi-k2", + name: "Kimi K2", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-07-11", + last_updated: "2025-07-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 2.2 }, + limit: { context: 131072, output: 32768 }, + }, + "moonshotai/kimi-k2-thinking": { + id: "moonshotai/kimi-k2-thinking", + name: "Kimi K2 Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_details" }, + structured_output: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-11-06", + last_updated: "2025-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5, cache_read: 0.15 }, + limit: { context: 262144, output: 262144 }, + }, + "anthropic/claude-opus-4.1": { + id: "anthropic/claude-opus-4.1", + name: "Claude Opus 4.1", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "anthropic/claude-3.7-sonnet": { + id: "anthropic/claude-3.7-sonnet", + name: "Claude Sonnet 3.7", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-01", + release_date: "2025-02-19", + last_updated: "2025-02-19", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 128000 }, + }, + "anthropic/claude-opus-4.6": { + id: "anthropic/claude-opus-4.6", + name: "Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 5, + output: 25, + cache_read: 0.5, + cache_write: 6.25, + context_over_200k: { input: 10, output: 37.5, cache_read: 1, cache_write: 12.5 }, + }, + limit: { context: 1000000, output: 128000 }, + }, + "anthropic/claude-opus-4.7": { + id: "anthropic/claude-opus-4.7", + name: "Claude Opus 4.7", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 5, + output: 25, + cache_read: 0.5, + cache_write: 6.25, + context_over_200k: { input: 10, output: 37.5, cache_read: 1, cache_write: 12.5 }, + }, + limit: { context: 1000000, output: 128000 }, + }, + "anthropic/claude-sonnet-4": { + id: "anthropic/claude-sonnet-4", + name: "Claude Sonnet 4", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 3, + output: 15, + cache_read: 0.3, + cache_write: 3.75, + context_over_200k: { input: 6, output: 22.5, cache_read: 0.6, cache_write: 7.5 }, + }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-sonnet-4.5": { + id: "anthropic/claude-sonnet-4.5", + name: "Claude Sonnet 4.5", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 3, + output: 15, + cache_read: 0.3, + cache_write: 3.75, + context_over_200k: { input: 6, output: 22.5, cache_read: 0.6, cache_write: 7.5 }, + }, + limit: { context: 1000000, output: 64000 }, + }, + "anthropic/claude-opus-4.5": { + id: "anthropic/claude-opus-4.5", + name: "Claude Opus 4.5", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-05-30", + release_date: "2025-11-24", + last_updated: "2025-11-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 32000 }, + }, + "anthropic/claude-haiku-4.5": { + id: "anthropic/claude-haiku-4.5", + name: "Claude Haiku 4.5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-sonnet-4.6": { + id: "anthropic/claude-sonnet-4.6", + name: "Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-02-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { + input: 3, + output: 15, + cache_read: 0.3, + cache_write: 3.75, + context_over_200k: { input: 6, output: 22.5, cache_read: 0.6, cache_write: 7.5 }, + }, + limit: { context: 1000000, output: 128000 }, + }, + "deepseek/deepseek-v4-flash": { + id: "deepseek/deepseek-v4-flash", + name: "DeepSeek V4 Flash", + family: "deepseek-flash", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.28, cache_read: 0.028 }, + limit: { context: 1048576, output: 393216 }, + }, + "deepseek/deepseek-v4-pro": { + id: "deepseek/deepseek-v4-pro", + name: "DeepSeek V4 Pro", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.74, output: 3.48, cache_read: 0.145 }, + limit: { context: 1048576, output: 393216 }, + }, + "x-ai/grok-4.3": { + id: "x-ai/grok-4.3", + name: "Grok 4.3", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-05-01", + last_updated: "2026-05-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { + input: 1.25, + output: 2.5, + cache_read: 0.2, + context_over_200k: { input: 2.5, output: 5, cache_read: 0.4 }, + }, + limit: { context: 1000000, output: 1000000 }, + }, + "openai/gpt-5.5": { + id: "openai/gpt-5.5", + name: "GPT-5.5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-12-01", + release_date: "2026-04-23", + last_updated: "2026-04-23", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 30, cache_read: 0.5, context_over_200k: { input: 10, output: 45, cache_read: 1 } }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "openai/gpt-5.4": { + id: "openai/gpt-5.4", + name: "GPT-5.4", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 2.5, + output: 15, + cache_read: 0.25, + context_over_200k: { input: 5, output: 22.5, cache_read: 0.5 }, + }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "openai/gpt-5.5-pro": { + id: "openai/gpt-5.5-pro", + name: "GPT-5.5 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-12-01", + release_date: "2026-04-23", + last_updated: "2026-04-23", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 180, context_over_200k: { input: 60, output: 270 } }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "google/gemini-2.5-pro": { + id: "google/gemini-2.5-pro", + name: "Gemini 2.5 Pro", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-20", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 1.25, + output: 10, + cache_read: 0.125, + context_over_200k: { input: 2.5, output: 15, cache_read: 0.25 }, + }, + limit: { context: 1048576, output: 65536 }, + }, + "anthropic/claude-opus-4": { + id: "anthropic/claude-opus-4", + name: "Claude Opus 4", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "anthropic/claude-3.5-haiku": { + id: "anthropic/claude-3.5-haiku", + name: "Claude Haiku 3.5", + family: "claude-haiku", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07-31", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 4, cache_read: 0.08, cache_write: 1 }, + limit: { context: 200000, output: 8192 }, + }, + "xiaomi/mimo-v2.5-pro": { + id: "xiaomi/mimo-v2.5-pro", + name: "Xiaomi: MiMo-V2.5-Pro", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3, cache_read: 0.2, context_over_200k: { input: 2, output: 6, cache_read: 0.4 } }, + limit: { context: 1048576, output: 131072 }, + }, + "xiaomi/mimo-v2-omni": { + id: "xiaomi/mimo-v2-omni", + name: "Xiaomi: MiMo-V2-Omni", + family: "mimo", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_details" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2, cache_read: 0.08 }, + limit: { context: 262144, output: 131072 }, + }, + "xiaomi/mimo-v2.5": { + id: "xiaomi/mimo-v2.5", + name: "Xiaomi: MiMo-V2.5", + family: "mimo", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_details" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: true, + cost: { + input: 0.4, + output: 2, + cache_read: 0.08, + context_over_200k: { input: 0.8, output: 4, cache_read: 0.16 }, + }, + limit: { context: 1048576, output: 131072 }, + }, + "xiaomi/mimo-v2-pro": { + id: "xiaomi/mimo-v2-pro", + name: "Xiaomi: MiMo-V2-Pro", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_details" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 3, cache_read: 0.2, context_over_200k: { input: 2, output: 6, cache_read: 0.4 } }, + limit: { context: 1048576, output: 131072 }, + }, + "xiaomi/mimo-v2-flash": { + id: "xiaomi/mimo-v2-flash", + name: "Xiaomi: MiMo-V2-Flash", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_details" }, + temperature: true, + knowledge: "2024-12-01", + release_date: "2025-12-16", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3, cache_read: 0.01 }, + limit: { context: 262144, output: 65536 }, + }, + }, + }, + "fireworks-ai": { + id: "fireworks-ai", + env: ["FIREWORKS_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.fireworks.ai/inference/v1/", + name: "Fireworks AI", + doc: "https://fireworks.ai/docs/", + models: { + "accounts/fireworks/models/glm-5p1": { + id: "accounts/fireworks/models/glm-5p1", + name: "GLM 5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-04-01", + last_updated: "2026-04-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.4, output: 4.4, cache_read: 0.26 }, + limit: { context: 202800, output: 131072 }, + }, + "accounts/fireworks/models/deepseek-v3p2": { + id: "accounts/fireworks/models/deepseek-v3p2", + name: "DeepSeek V3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-09", + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.56, output: 1.68, cache_read: 0.28 }, + limit: { context: 160000, output: 160000 }, + }, + "accounts/fireworks/models/minimax-m2p5": { + id: "accounts/fireworks/models/minimax-m2p5", + name: "MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.03 }, + limit: { context: 196608, output: 196608 }, + }, + "accounts/fireworks/models/glm-4p5-air": { + id: "accounts/fireworks/models/glm-4p5-air", + name: "GLM 4.5 Air", + family: "glm-air", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-08-01", + last_updated: "2025-08-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.22, output: 0.88 }, + limit: { context: 131072, output: 131072 }, + }, + "accounts/fireworks/models/glm-5": { + id: "accounts/fireworks/models/glm-5", + name: "GLM 5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3.2, cache_read: 0.5 }, + limit: { context: 202752, output: 131072 }, + }, + "accounts/fireworks/models/deepseek-v3p1": { + id: "accounts/fireworks/models/deepseek-v3p1", + name: "DeepSeek V3.1", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-08-21", + last_updated: "2025-08-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.56, output: 1.68 }, + limit: { context: 163840, output: 163840 }, + }, + "accounts/fireworks/models/kimi-k2p6": { + id: "accounts/fireworks/models/kimi-k2p6", + name: "Kimi K2.6", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-04-17", + last_updated: "2026-04-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4, cache_read: 0.16 }, + limit: { context: 262000, output: 262000 }, + }, + "accounts/fireworks/models/kimi-k2-instruct": { + id: "accounts/fireworks/models/kimi-k2-instruct", + name: "Kimi K2 Instruct", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-07-11", + last_updated: "2025-07-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3 }, + limit: { context: 128000, output: 16384 }, + }, + "accounts/fireworks/models/qwen3p6-plus": { + id: "accounts/fireworks/models/qwen3p6-plus", + name: "Qwen 3.6 Plus", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-04", + last_updated: "2026-04-04", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 3, cache_read: 0.1 }, + limit: { context: 128000, output: 8192 }, + }, + "accounts/fireworks/models/minimax-m2p1": { + id: "accounts/fireworks/models/minimax-m2p1", + name: "MiniMax-M2.1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.03 }, + limit: { context: 200000, output: 200000 }, + }, + "accounts/fireworks/models/minimax-m2p7": { + id: "accounts/fireworks/models/minimax-m2p7", + name: "MiniMax-M2.7", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-04-12", + last_updated: "2026-04-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.03 }, + limit: { context: 196608, output: 196608 }, + }, + "accounts/fireworks/models/glm-4p7": { + id: "accounts/fireworks/models/glm-4p7", + name: "GLM 4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2, cache_read: 0.3 }, + limit: { context: 198000, output: 198000 }, + }, + "accounts/fireworks/models/glm-4p5": { + id: "accounts/fireworks/models/glm-4p5", + name: "GLM 4.5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-29", + last_updated: "2025-07-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 2.19 }, + limit: { context: 131072, output: 131072 }, + }, + "accounts/fireworks/models/kimi-k2p5": { + id: "accounts/fireworks/models/kimi-k2p5", + name: "Kimi K2.5", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-01", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3, cache_read: 0.1 }, + limit: { context: 256000, output: 256000 }, + }, + "accounts/fireworks/models/gpt-oss-20b": { + id: "accounts/fireworks/models/gpt-oss-20b", + name: "GPT OSS 20B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.2 }, + limit: { context: 131072, output: 32768 }, + }, + "accounts/fireworks/models/gpt-oss-120b": { + id: "accounts/fireworks/models/gpt-oss-120b", + name: "GPT OSS 120B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 131072, output: 32768 }, + }, + "accounts/fireworks/models/kimi-k2-thinking": { + id: "accounts/fireworks/models/kimi-k2-thinking", + name: "Kimi K2 Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2025-11-06", + last_updated: "2025-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5, cache_read: 0.3 }, + limit: { context: 256000, output: 256000 }, + }, + "accounts/fireworks/routers/kimi-k2p5-turbo": { + id: "accounts/fireworks/routers/kimi-k2p5-turbo", + name: "Kimi K2.5 Turbo", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-01", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0 }, + limit: { context: 256000, output: 256000 }, + }, + "accounts/fireworks/models/deepseek-v4-pro": { + id: "accounts/fireworks/models/deepseek-v4-pro", + name: "DeepSeek V4 Pro", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.74, output: 3.48, cache_read: 0.15 }, + limit: { context: 1000000, output: 384000 }, + }, + }, + }, + "kimi-for-coding": { + id: "kimi-for-coding", + env: ["KIMI_API_KEY"], + npm: "@ai-sdk/anthropic", + api: "https://api.kimi.com/coding/v1", + name: "Kimi For Coding", + doc: "https://www.kimi.com/coding/docs/en/third-party-agents.html", + models: { + k2p6: { + id: "k2p6", + name: "Kimi K2.6", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04", + last_updated: "2026-04", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 262144, output: 32768 }, + }, + k2p5: { + id: "k2p5", + name: "Kimi K2.5", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 262144, output: 32768 }, + }, + "kimi-k2-thinking": { + id: "kimi-k2-thinking", + name: "Kimi K2 Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-11", + last_updated: "2025-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 262144, output: 32768 }, + }, + }, + }, + moark: { + id: "moark", + env: ["MOARK_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://moark.com/v1", + name: "Moark", + doc: "https://moark.com/docs/openapi/v1#tag/%E6%96%87%E6%9C%AC%E7%94%9F%E6%88%90", + models: { + "GLM-4.7": { + id: "GLM-4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 3.5, output: 14 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMax-M2.1": { + id: "MiniMax-M2.1", + name: "MiniMax-M2.1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.1, output: 8.4 }, + limit: { context: 204800, output: 131072 }, + }, + }, + }, + "opencode-go": { + id: "opencode-go", + env: ["OPENCODE_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://opencode.ai/zen/go/v1", + name: "OpenCode Go", + doc: "https://opencode.ai/docs/zen", + models: { + "minimax-m2.7": { + id: "minimax-m2.7", + name: "MiniMax M2.7", + family: "minimax-m2.7", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.06 }, + limit: { context: 204800, output: 131072 }, + provider: { npm: "@ai-sdk/anthropic" }, + }, + "kimi-k2.5": { + id: "kimi-k2.5", + name: "Kimi K2.5", + family: "kimi-k2.5", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-10", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3, cache_read: 0.1 }, + limit: { context: 262144, output: 65536 }, + }, + "mimo-v2.5-pro": { + id: "mimo-v2.5-pro", + name: "MiMo V2.5 Pro", + family: "mimo-v2.5-pro", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3, cache_read: 0.2, context_over_200k: { input: 2, output: 6, cache_read: 0.4 } }, + limit: { context: 1048576, output: 128000 }, + }, + "glm-5": { + id: "glm-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3.2, cache_read: 0.2 }, + limit: { context: 202752, output: 32768 }, + }, + "mimo-v2-omni": { + id: "mimo-v2-omni", + name: "MiMo V2 Omni", + family: "mimo-v2-omni", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text", "image", "audio", "pdf"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 2, cache_read: 0.08 }, + limit: { context: 262144, output: 128000 }, + status: "deprecated", + }, + "mimo-v2.5": { + id: "mimo-v2.5", + name: "MiMo V2.5", + family: "mimo-v2.5", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: true, + cost: { + input: 0.4, + output: 2, + cache_read: 0.08, + context_over_200k: { input: 0.8, output: 4, cache_read: 0.16 }, + }, + limit: { context: 1000000, output: 128000 }, + }, + "qwen3.6-plus": { + id: "qwen3.6-plus", + name: "Qwen3.6 Plus", + family: "qwen3.6", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 3, cache_read: 0.05, cache_write: 0.625 }, + limit: { context: 262144, output: 65536 }, + provider: { npm: "@ai-sdk/anthropic" }, + }, + "glm-5.1": { + id: "glm-5.1", + name: "GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2026-04-07", + last_updated: "2026-04-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.4, output: 4.4, cache_read: 0.26 }, + limit: { context: 202752, output: 32768 }, + }, + "deepseek-v4-flash": { + id: "deepseek-v4-flash", + name: "DeepSeek V4 Flash", + family: "deepseek-flash", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.28, cache_read: 0.0028 }, + limit: { context: 1000000, output: 384000 }, + }, + "kimi-k2.6": { + id: "kimi-k2.6", + name: "Kimi K2.6", + family: "kimi-k2.6", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-10", + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4, cache_read: 0.16 }, + limit: { context: 262144, output: 65536 }, + }, + "deepseek-v4-pro": { + id: "deepseek-v4-pro", + name: "DeepSeek V4 Pro", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.74, output: 3.48, cache_read: 0.0145 }, + limit: { context: 1000000, output: 384000 }, + }, + "minimax-m2.5": { + id: "minimax-m2.5", + name: "MiniMax M2.5", + family: "minimax-m2.5", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.03 }, + limit: { context: 204800, output: 65536 }, + }, + "mimo-v2-pro": { + id: "mimo-v2-pro", + name: "MiMo V2 Pro", + family: "mimo-v2-pro", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3, cache_read: 0.2, context_over_200k: { input: 2, output: 6, cache_read: 0.4 } }, + limit: { context: 1048576, output: 128000 }, + status: "deprecated", + }, + "qwen3.5-plus": { + id: "qwen3.5-plus", + name: "Qwen3.5 Plus", + family: "qwen3.5", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-16", + last_updated: "2026-02-16", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.2, cache_read: 0.02, cache_write: 0.25 }, + limit: { context: 262144, output: 65536 }, + provider: { npm: "@ai-sdk/anthropic" }, + }, + }, + }, + "io-net": { + id: "io-net", + env: ["IOINTELLIGENCE_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.intelligence.io.solutions/api/v1", + name: "IO.NET", + doc: "https://io.net/docs/guides/intelligence/io-intelligence", + models: { + "Intel/Qwen3-Coder-480B-A35B-Instruct-int4-mixed-ar": { + id: "Intel/Qwen3-Coder-480B-A35B-Instruct-int4-mixed-ar", + name: "Qwen 3 Coder 480B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-01-15", + last_updated: "2025-01-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.22, output: 0.95, cache_read: 0.11, cache_write: 0.44 }, + limit: { context: 106000, output: 4096 }, + }, + "Qwen/Qwen3-Next-80B-A3B-Instruct": { + id: "Qwen/Qwen3-Next-80B-A3B-Instruct", + name: "Qwen 3 Next 80B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-01-10", + last_updated: "2025-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.8, cache_read: 0.05, cache_write: 0.2 }, + limit: { context: 262144, output: 4096 }, + }, + "Qwen/Qwen3-235B-A22B-Thinking-2507": { + id: "Qwen/Qwen3-235B-A22B-Thinking-2507", + name: "Qwen 3 235B Thinking", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-07-01", + last_updated: "2025-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.11, output: 0.6, cache_read: 0.055, cache_write: 0.22 }, + limit: { context: 262144, output: 4096 }, + }, + "Qwen/Qwen2.5-VL-32B-Instruct": { + id: "Qwen/Qwen2.5-VL-32B-Instruct", + name: "Qwen 2.5 VL 32B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-09", + release_date: "2024-11-01", + last_updated: "2024-11-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.22, cache_read: 0.025, cache_write: 0.1 }, + limit: { context: 32000, output: 4096 }, + }, + "zai-org/GLM-4.6": { + id: "zai-org/GLM-4.6", + name: "GLM 4.6", + family: "glm", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-11-15", + last_updated: "2024-11-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.75, cache_read: 0.2, cache_write: 0.8 }, + limit: { context: 200000, output: 4096 }, + }, + "mistralai/Magistral-Small-2506": { + id: "mistralai/Magistral-Small-2506", + name: "Magistral Small 2506", + family: "magistral-small", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-06-01", + last_updated: "2025-06-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 1.5, cache_read: 0.25, cache_write: 1 }, + limit: { context: 128000, output: 4096 }, + }, + "mistralai/Mistral-Large-Instruct-2411": { + id: "mistralai/Mistral-Large-Instruct-2411", + name: "Mistral Large Instruct 2411", + family: "mistral-large", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-11-01", + last_updated: "2024-11-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6, cache_read: 1, cache_write: 4 }, + limit: { context: 128000, output: 4096 }, + }, + "mistralai/Mistral-Nemo-Instruct-2407": { + id: "mistralai/Mistral-Nemo-Instruct-2407", + name: "Mistral Nemo Instruct 2407", + family: "mistral-nemo", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-05", + release_date: "2024-07-01", + last_updated: "2024-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0.04, cache_read: 0.01, cache_write: 0.04 }, + limit: { context: 128000, output: 4096 }, + }, + "mistralai/Devstral-Small-2505": { + id: "mistralai/Devstral-Small-2505", + name: "Devstral Small 2505", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-05-01", + last_updated: "2025-05-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.22, cache_read: 0.025, cache_write: 0.1 }, + limit: { context: 128000, output: 4096 }, + }, + "meta-llama/Llama-3.3-70B-Instruct": { + id: "meta-llama/Llama-3.3-70B-Instruct", + name: "Llama 3.3 70B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.13, output: 0.38, cache_read: 0.065, cache_write: 0.26 }, + limit: { context: 128000, output: 4096 }, + }, + "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8": { + id: "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8", + name: "Llama 4 Maverick 17B 128E Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-01-15", + last_updated: "2025-01-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6, cache_read: 0.075, cache_write: 0.3 }, + limit: { context: 430000, output: 4096 }, + }, + "meta-llama/Llama-3.2-90B-Vision-Instruct": { + id: "meta-llama/Llama-3.2-90B-Vision-Instruct", + name: "Llama 3.2 90B Vision Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-09-25", + last_updated: "2024-09-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.35, output: 0.4, cache_read: 0.175, cache_write: 0.7 }, + limit: { context: 16000, output: 4096 }, + }, + "deepseek-ai/DeepSeek-R1-0528": { + id: "deepseek-ai/DeepSeek-R1-0528", + name: "DeepSeek R1", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-01-20", + last_updated: "2025-05-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2, output: 8.75, cache_read: 1, cache_write: 4 }, + limit: { context: 128000, output: 4096 }, + }, + "openai/gpt-oss-20b": { + id: "openai/gpt-oss-20b", + name: "GPT-OSS 20B", + family: "gpt-oss", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.14, cache_read: 0.015, cache_write: 0.06 }, + limit: { context: 64000, output: 4096 }, + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "GPT-OSS 120B", + family: "gpt-oss", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.04, output: 0.4, cache_read: 0.02, cache_write: 0.08 }, + limit: { context: 131072, output: 4096 }, + }, + "moonshotai/Kimi-K2-Thinking": { + id: "moonshotai/Kimi-K2-Thinking", + name: "Kimi K2 Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2024-11-01", + last_updated: "2024-11-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.55, output: 2.25, cache_read: 0.275, cache_write: 1.1 }, + limit: { context: 32768, output: 4096 }, + }, + "moonshotai/Kimi-K2-Instruct-0905": { + id: "moonshotai/Kimi-K2-Instruct-0905", + name: "Kimi K2 Instruct", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2024-09-05", + last_updated: "2024-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.39, output: 1.9, cache_read: 0.195, cache_write: 0.78 }, + limit: { context: 32768, output: 4096 }, + }, + }, + }, + "alibaba-cn": { + id: "alibaba-cn", + env: ["DASHSCOPE_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://dashscope.aliyuncs.com/compatible-mode/v1", + name: "Alibaba (China)", + doc: "https://www.alibabacloud.com/help/en/model-studio/models", + models: { + "qwen3-235b-a22b": { + id: "qwen3-235b-a22b", + name: "Qwen3 235B-A22B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.287, output: 1.147, reasoning: 2.868 }, + limit: { context: 131072, output: 16384 }, + }, + "qwen-plus-character": { + id: "qwen-plus-character", + name: "Qwen Plus Character", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-01", + last_updated: "2024-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.115, output: 0.287 }, + limit: { context: 32768, output: 4096 }, + }, + "qwen2-5-math-7b-instruct": { + id: "qwen2-5-math-7b-instruct", + name: "Qwen2.5-Math 7B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-09", + last_updated: "2024-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.144, output: 0.287 }, + limit: { context: 4096, output: 3072 }, + }, + "kimi-k2.5": { + id: "kimi-k2.5", + name: "Moonshot Kimi K2.5", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: false, + temperature: true, + knowledge: "2025-01", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.574, output: 2.411 }, + limit: { context: 262144, output: 32768 }, + }, + "qwen-doc-turbo": { + id: "qwen-doc-turbo", + name: "Qwen Doc Turbo", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-01", + last_updated: "2024-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.087, output: 0.144 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen-vl-ocr": { + id: "qwen-vl-ocr", + name: "Qwen-VL OCR", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-04", + release_date: "2024-10-28", + last_updated: "2025-04-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.717, output: 0.717 }, + limit: { context: 34096, output: 4096 }, + }, + "qwen-omni-turbo-realtime": { + id: "qwen-omni-turbo-realtime", + name: "Qwen-Omni Turbo Realtime", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-05-08", + last_updated: "2025-05-08", + modalities: { input: ["text", "image", "audio"], output: ["text", "audio"] }, + open_weights: false, + cost: { input: 0.23, output: 0.918, input_audio: 3.584, output_audio: 7.168 }, + limit: { context: 32768, output: 2048 }, + }, + "qwen3-8b": { + id: "qwen3-8b", + name: "Qwen3 8B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.072, output: 0.287, reasoning: 0.717 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen3.5-397b-a17b": { + id: "qwen3.5-397b-a17b", + name: "Qwen3.5 397B-A17B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-16", + last_updated: "2026-02-16", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.43, output: 2.58, reasoning: 2.58 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen-math-turbo": { + id: "qwen-math-turbo", + name: "Qwen Math Turbo", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-09-19", + last_updated: "2024-09-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.287, output: 0.861 }, + limit: { context: 4096, output: 3072 }, + }, + "qwq-plus": { + id: "qwq-plus", + name: "QwQ Plus", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-03-05", + last_updated: "2025-03-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.23, output: 0.574 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen-vl-plus": { + id: "qwen-vl-plus", + name: "Qwen-VL Plus", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-01-25", + last_updated: "2025-08-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.115, output: 0.287 }, + limit: { context: 131072, output: 8192 }, + }, + "glm-5": { + id: "glm-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.86, output: 3.15 }, + limit: { context: 202752, output: 16384 }, + }, + "deepseek-r1-distill-llama-70b": { + id: "deepseek-r1-distill-llama-70b", + name: "DeepSeek R1 Distill Llama 70B", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.287, output: 0.861 }, + limit: { context: 32768, output: 16384 }, + }, + "qwen3-32b": { + id: "qwen3-32b", + name: "Qwen3 32B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.287, output: 1.147, reasoning: 2.868 }, + limit: { context: 131072, output: 16384 }, + }, + "MiniMax-M2.5": { + id: "MiniMax-M2.5", + name: "MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 204800, output: 131072 }, + }, + "qwen-max": { + id: "qwen-max", + name: "Qwen Max", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-04-03", + last_updated: "2025-01-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.345, output: 1.377 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen-plus": { + id: "qwen-plus", + name: "Qwen Plus", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-01-25", + last_updated: "2025-09-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.115, output: 0.287, reasoning: 1.147 }, + limit: { context: 1000000, output: 32768 }, + }, + "qwen-omni-turbo": { + id: "qwen-omni-turbo", + name: "Qwen-Omni Turbo", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-01-19", + last_updated: "2025-03-26", + modalities: { input: ["text", "image", "audio", "video"], output: ["text", "audio"] }, + open_weights: false, + cost: { input: 0.058, output: 0.23, input_audio: 3.584, output_audio: 7.168 }, + limit: { context: 32768, output: 2048 }, + }, + "qwen-flash": { + id: "qwen-flash", + name: "Qwen Flash", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.022, output: 0.216 }, + limit: { context: 1000000, output: 32768 }, + }, + "qwen2-5-vl-7b-instruct": { + id: "qwen2-5-vl-7b-instruct", + name: "Qwen2.5-VL 7B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-09", + last_updated: "2024-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.287, output: 0.717 }, + limit: { context: 131072, output: 8192 }, + }, + "deepseek-r1": { + id: "deepseek-r1", + name: "DeepSeek R1", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.574, output: 2.294 }, + limit: { context: 131072, output: 16384 }, + }, + "qwen3.5-flash": { + id: "qwen3.5-flash", + name: "Qwen3.5 Flash", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-23", + last_updated: "2026-02-23", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.172, output: 1.72, reasoning: 1.72 }, + limit: { context: 1000000, output: 65536 }, + }, + "qwen3.6-plus": { + id: "qwen3.6-plus", + name: "Qwen3.6 Plus", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.276, output: 1.651, cache_read: 0.028, cache_write: 0.344 }, + limit: { context: 1000000, output: 65536 }, + }, + "qwen3-max": { + id: "qwen3-max", + name: "Qwen3 Max", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-23", + last_updated: "2025-09-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.861, output: 3.441 }, + limit: { context: 262144, output: 65536 }, + }, + "glm-5.1": { + id: "glm-5.1", + name: "GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-04-14", + last_updated: "2026-04-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.87, output: 3.48, cache_read: 0.17 }, + limit: { context: 202752, output: 128000 }, + }, + "qwen3-omni-flash": { + id: "qwen3-omni-flash", + name: "Qwen3-Omni Flash", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-09-15", + last_updated: "2025-09-15", + modalities: { input: ["text", "image", "audio", "video"], output: ["text", "audio"] }, + open_weights: false, + cost: { input: 0.058, output: 0.23, input_audio: 3.584, output_audio: 7.168 }, + limit: { context: 65536, output: 16384 }, + }, + "deepseek-v3-1": { + id: "deepseek-v3-1", + name: "DeepSeek V3.1", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.574, output: 1.721 }, + limit: { context: 131072, output: 65536 }, + }, + "qwen2-5-72b-instruct": { + id: "qwen2-5-72b-instruct", + name: "Qwen2.5 72B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-09", + last_updated: "2024-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.574, output: 1.721 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen3-vl-235b-a22b": { + id: "qwen3-vl-235b-a22b", + name: "Qwen3-VL 235B-A22B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.286705, output: 1.14682, reasoning: 2.867051 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen-math-plus": { + id: "qwen-math-plus", + name: "Qwen Math Plus", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-08-16", + last_updated: "2024-09-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.574, output: 1.721 }, + limit: { context: 4096, output: 3072 }, + }, + "qwen2-5-coder-32b-instruct": { + id: "qwen2-5-coder-32b-instruct", + name: "Qwen2.5-Coder 32B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-11", + last_updated: "2024-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.287, output: 0.861 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen3-asr-flash": { + id: "qwen3-asr-flash", + name: "Qwen3-ASR Flash", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + knowledge: "2024-04", + release_date: "2025-09-08", + last_updated: "2025-09-08", + modalities: { input: ["audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.032, output: 0.032 }, + limit: { context: 53248, output: 4096 }, + }, + "qwen-deep-research": { + id: "qwen-deep-research", + name: "Qwen Deep Research", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-01", + last_updated: "2024-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 7.742, output: 23.367 }, + limit: { context: 1000000, output: 32768 }, + }, + "qwen3-next-80b-a3b-thinking": { + id: "qwen3-next-80b-a3b-thinking", + name: "Qwen3-Next 80B-A3B (Thinking)", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09", + last_updated: "2025-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.144, output: 1.434 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen-mt-plus": { + id: "qwen-mt-plus", + name: "Qwen-MT Plus", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-04", + release_date: "2025-01", + last_updated: "2025-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.259, output: 0.775 }, + limit: { context: 16384, output: 8192 }, + }, + "deepseek-r1-distill-qwen-32b": { + id: "deepseek-r1-distill-qwen-32b", + name: "DeepSeek R1 Distill Qwen 32B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.287, output: 0.861 }, + limit: { context: 32768, output: 16384 }, + }, + "qwen-vl-max": { + id: "qwen-vl-max", + name: "Qwen-VL Max", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-04-08", + last_updated: "2025-08-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.23, output: 0.574 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen3-coder-flash": { + id: "qwen3-coder-flash", + name: "Qwen3 Coder Flash", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.144, output: 0.574 }, + limit: { context: 1000000, output: 65536 }, + }, + "deepseek-r1-distill-qwen-7b": { + id: "deepseek-r1-distill-qwen-7b", + name: "DeepSeek R1 Distill Qwen 7B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.072, output: 0.144 }, + limit: { context: 32768, output: 16384 }, + }, + "qwen2-5-7b-instruct": { + id: "qwen2-5-7b-instruct", + name: "Qwen2.5 7B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-09", + last_updated: "2024-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.072, output: 0.144 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen2-5-14b-instruct": { + id: "qwen2-5-14b-instruct", + name: "Qwen2.5 14B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-09", + last_updated: "2024-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.144, output: 0.431 }, + limit: { context: 131072, output: 8192 }, + }, + "tongyi-intent-detect-v3": { + id: "tongyi-intent-detect-v3", + name: "Tongyi Intent Detect V3", + family: "yi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-04", + release_date: "2024-01", + last_updated: "2024-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.058, output: 0.144 }, + limit: { context: 8192, output: 1024 }, + }, + "qwq-32b": { + id: "qwq-32b", + name: "QwQ 32B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-12", + last_updated: "2024-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.287, output: 0.861 }, + limit: { context: 131072, output: 8192 }, + }, + "moonshot-kimi-k2-instruct": { + id: "moonshot-kimi-k2-instruct", + name: "Moonshot Kimi K2 Instruct", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.574, output: 2.294 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen2-5-32b-instruct": { + id: "qwen2-5-32b-instruct", + name: "Qwen2.5 32B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-09", + last_updated: "2024-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.287, output: 0.861 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen3-next-80b-a3b-instruct": { + id: "qwen3-next-80b-a3b-instruct", + name: "Qwen3-Next 80B-A3B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09", + last_updated: "2025-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.144, output: 0.574 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen3-omni-flash-realtime": { + id: "qwen3-omni-flash-realtime", + name: "Qwen3-Omni Flash Realtime", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-09-15", + last_updated: "2025-09-15", + modalities: { input: ["text", "image", "audio"], output: ["text", "audio"] }, + open_weights: false, + cost: { input: 0.23, output: 0.918, input_audio: 3.584, output_audio: 7.168 }, + limit: { context: 65536, output: 16384 }, + }, + "kimi-k2.6": { + id: "kimi-k2.6", + name: "Moonshot Kimi K2.6", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: false, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.929, output: 3.858 }, + limit: { context: 262144, output: 16384 }, + }, + "qwen3-vl-30b-a3b": { + id: "qwen3-vl-30b-a3b", + name: "Qwen3-VL 30B-A3B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.108, output: 0.431, reasoning: 1.076 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen3-vl-plus": { + id: "qwen3-vl-plus", + name: "Qwen3-VL Plus", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-23", + last_updated: "2025-09-23", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.143353, output: 1.433525, reasoning: 4.300576 }, + limit: { context: 262144, output: 32768 }, + }, + "deepseek-v3-2-exp": { + id: "deepseek-v3-2-exp", + name: "DeepSeek V3.2 Exp", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.287, output: 0.431 }, + limit: { context: 131072, output: 65536 }, + }, + "qwen3-coder-480b-a35b-instruct": { + id: "qwen3-coder-480b-a35b-instruct", + name: "Qwen3-Coder 480B-A35B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.861, output: 3.441 }, + limit: { context: 262144, output: 65536 }, + }, + "deepseek-r1-distill-qwen-1-5b": { + id: "deepseek-r1-distill-qwen-1-5b", + name: "DeepSeek R1 Distill Qwen 1.5B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 32768, output: 16384 }, + }, + "qwen3-coder-30b-a3b-instruct": { + id: "qwen3-coder-30b-a3b-instruct", + name: "Qwen3-Coder 30B-A3B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.216, output: 0.861 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen2-5-coder-7b-instruct": { + id: "qwen2-5-coder-7b-instruct", + name: "Qwen2.5-Coder 7B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-11", + last_updated: "2024-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.144, output: 0.287 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen-turbo": { + id: "qwen-turbo", + name: "Qwen Turbo", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-11-01", + last_updated: "2025-07-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.044, output: 0.087, reasoning: 0.431 }, + limit: { context: 1000000, output: 16384 }, + }, + "qwen-mt-turbo": { + id: "qwen-mt-turbo", + name: "Qwen-MT Turbo", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-04", + release_date: "2025-01", + last_updated: "2025-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.101, output: 0.28 }, + limit: { context: 16384, output: 8192 }, + }, + "qwen2-5-math-72b-instruct": { + id: "qwen2-5-math-72b-instruct", + name: "Qwen2.5-Math 72B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-09", + last_updated: "2024-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.574, output: 1.721 }, + limit: { context: 4096, output: 3072 }, + }, + "qwen3.6-max-preview": { + id: "qwen3.6-max-preview", + name: "Qwen3.6 Max Preview", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-20", + last_updated: "2026-04-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.32, output: 7.9, cache_read: 0.132 }, + limit: { context: 245800, output: 65536 }, + }, + "qwen2-5-omni-7b": { + id: "qwen2-5-omni-7b", + name: "Qwen2.5-Omni 7B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-12", + last_updated: "2024-12", + modalities: { input: ["text", "image", "audio", "video"], output: ["text", "audio"] }, + open_weights: true, + cost: { input: 0.087, output: 0.345, input_audio: 5.448 }, + limit: { context: 32768, output: 2048 }, + }, + "qwen3.5-plus": { + id: "qwen3.5-plus", + name: "Qwen3.5 Plus", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-16", + last_updated: "2026-02-16", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.573, output: 3.44, reasoning: 3.44 }, + limit: { context: 1000000, output: 65536 }, + }, + "deepseek-r1-distill-qwen-14b": { + id: "deepseek-r1-distill-qwen-14b", + name: "DeepSeek R1 Distill Qwen 14B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.144, output: 0.431 }, + limit: { context: 32768, output: 16384 }, + }, + "qwen2-5-vl-72b-instruct": { + id: "qwen2-5-vl-72b-instruct", + name: "Qwen2.5-VL 72B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-09", + last_updated: "2024-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 2.294, output: 6.881 }, + limit: { context: 131072, output: 8192 }, + }, + "deepseek-v3": { + id: "deepseek-v3", + name: "DeepSeek V3", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.287, output: 1.147 }, + limit: { context: 65536, output: 8192 }, + }, + "deepseek-r1-0528": { + id: "deepseek-r1-0528", + name: "DeepSeek R1 0528", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-05-28", + last_updated: "2025-05-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.574, output: 2.294 }, + limit: { context: 131072, output: 16384 }, + }, + "qvq-max": { + id: "qvq-max", + name: "QVQ Max", + family: "qvq", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-03-25", + last_updated: "2025-03-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.147, output: 4.588 }, + limit: { context: 131072, output: 8192 }, + }, + "kimi-k2-thinking": { + id: "kimi-k2-thinking", + name: "Moonshot Kimi K2 Thinking", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-11-06", + last_updated: "2025-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.574, output: 2.294 }, + limit: { context: 262144, output: 16384 }, + }, + "qwen3-14b": { + id: "qwen3-14b", + name: "Qwen3 14B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.144, output: 0.574, reasoning: 1.434 }, + limit: { context: 131072, output: 8192 }, + }, + "deepseek-r1-distill-llama-8b": { + id: "deepseek-r1-distill-llama-8b", + name: "DeepSeek R1 Distill Llama 8B", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 32768, output: 16384 }, + }, + "qwen-long": { + id: "qwen-long", + name: "Qwen Long", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-01-25", + last_updated: "2025-01-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.072, output: 0.287 }, + limit: { context: 10000000, output: 8192 }, + }, + "kimi/kimi-k2.5": { + id: "kimi/kimi-k2.5", + name: "kimi/kimi-k2.5", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: false, + knowledge: "2025-01", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3, cache_read: 0.1 }, + limit: { context: 262144, output: 262144 }, + }, + "MiniMax/MiniMax-M2.7": { + id: "MiniMax/MiniMax-M2.7", + name: "MiniMax-M2.7", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.06, cache_write: 0.375 }, + limit: { context: 204800, output: 131072 }, + }, + "siliconflow/deepseek-v3-0324": { + id: "siliconflow/deepseek-v3-0324", + name: "siliconflow/deepseek-v3-0324", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-12-26", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1 }, + limit: { context: 163840, output: 163840 }, + }, + "siliconflow/deepseek-v3.2": { + id: "siliconflow/deepseek-v3.2", + name: "siliconflow/deepseek-v3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-03", + last_updated: "2025-12-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 0.42 }, + limit: { context: 163840, output: 65536 }, + }, + "siliconflow/deepseek-r1-0528": { + id: "siliconflow/deepseek-r1-0528", + name: "siliconflow/deepseek-r1-0528", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-05-28", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 2.18 }, + limit: { context: 163840, output: 32768 }, + }, + "siliconflow/deepseek-v3.1-terminus": { + id: "siliconflow/deepseek-v3.1-terminus", + name: "siliconflow/deepseek-v3.1-terminus", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-29", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 1 }, + limit: { context: 163840, output: 65536 }, + }, + "qwen3-coder-plus": { + id: "qwen3-coder-plus", + name: "Qwen3 Coder Plus", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 5 }, + limit: { context: 1048576, output: 65536 }, + }, + "deepseek-v4-flash": { + id: "deepseek-v4-flash", + name: "DeepSeek V4 Flash", + family: "deepseek-flash", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.28, cache_read: 0.028 }, + limit: { context: 1000000, output: 384000 }, + }, + "deepseek-v4-pro": { + id: "deepseek-v4-pro", + name: "DeepSeek V4 Pro", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.74, output: 3.48, cache_read: 0.145 }, + limit: { context: 1000000, output: 384000 }, + }, + }, + }, + firepass: { + id: "firepass", + env: ["FIREPASS_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.fireworks.ai/inference/v1/", + name: "Fireworks (Firepass)", + doc: "https://docs.fireworks.ai/firepass", + models: { + "accounts/fireworks/routers/kimi-k2p6-turbo": { + id: "accounts/fireworks/routers/kimi-k2p6-turbo", + name: "Kimi K2.6 Turbo", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-04-17", + last_updated: "2026-04-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0 }, + limit: { context: 262000, output: 262000 }, + }, + }, + }, + "minimax-cn-coding-plan": { + id: "minimax-cn-coding-plan", + env: ["MINIMAX_API_KEY"], + npm: "@ai-sdk/anthropic", + api: "https://api.minimaxi.com/anthropic/v1", + name: "MiniMax Coding Plan (minimaxi.com)", + doc: "https://platform.minimaxi.com/docs/coding-plan/intro", + models: { + "MiniMax-M2": { + id: "MiniMax-M2", + name: "MiniMax-M2", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-10-27", + last_updated: "2025-10-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 196608, output: 128000 }, + }, + "MiniMax-M2.5": { + id: "MiniMax-M2.5", + name: "MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMax-M2.7": { + id: "MiniMax-M2.7", + name: "MiniMax-M2.7", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMax-M2.7-highspeed": { + id: "MiniMax-M2.7-highspeed", + name: "MiniMax-M2.7-highspeed", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMax-M2.1": { + id: "MiniMax-M2.1", + name: "MiniMax-M2.1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMax-M2.5-highspeed": { + id: "MiniMax-M2.5-highspeed", + name: "MiniMax-M2.5-highspeed", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-13", + last_updated: "2026-02-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + }, + }, + jiekou: { + id: "jiekou", + env: ["JIEKOU_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.jiekou.ai/openai", + name: "Jiekou.AI", + doc: "https://docs.jiekou.ai/docs/support/quickstart?utm_source=github_models.dev", + models: { + "gpt-5.1-codex-max": { + id: "gpt-5.1-codex-max", + name: "gpt-5.1-codex-max", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.125, output: 9 }, + limit: { context: 400000, output: 128000 }, + }, + "grok-4-1-fast-reasoning": { + id: "grok-4-1-fast-reasoning", + name: "grok-4-1-fast-reasoning", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.18, output: 0.45 }, + limit: { context: 2000000, output: 2000000 }, + }, + "claude-opus-4-5-20251101": { + id: "claude-opus-4-5-20251101", + name: "claude-opus-4-5-20251101", + family: "claude-opus", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 4.5, output: 22.5 }, + limit: { context: 200000, output: 65536 }, + }, + "gemini-2.5-flash-lite-preview-09-2025": { + id: "gemini-2.5-flash-lite-preview-09-2025", + name: "gemini-2.5-flash-lite-preview-09-2025", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.09, output: 0.36 }, + limit: { context: 1048576, output: 65536 }, + }, + "gpt-5.2-pro": { + id: "gpt-5.2-pro", + name: "gpt-5.2-pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 18.9, output: 151.2 }, + limit: { context: 400000, output: 128000 }, + }, + "gemini-3-flash-preview": { + id: "gemini-3-flash-preview", + name: "gemini-3-flash-preview", + family: "gemini-flash", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 3 }, + limit: { context: 1048576, output: 65536 }, + }, + "gpt-5-mini": { + id: "gpt-5-mini", + name: "gpt-5-mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.225, output: 1.8 }, + limit: { context: 400000, output: 128000 }, + }, + "gpt-5-nano": { + id: "gpt-5-nano", + name: "gpt-5-nano", + family: "gpt-nano", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.045, output: 0.36 }, + limit: { context: 400000, output: 128000 }, + }, + "gemini-3-pro-preview": { + id: "gemini-3-pro-preview", + name: "gemini-3-pro-preview", + family: "gemini-pro", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 1.8, output: 10.8 }, + limit: { context: 1048576, output: 65536 }, + }, + "gemini-2.5-flash-preview-05-20": { + id: "gemini-2.5-flash-preview-05-20", + name: "gemini-2.5-flash-preview-05-20", + family: "gemini-flash", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.135, output: 3.15 }, + limit: { context: 1048576, output: 200000 }, + }, + "claude-sonnet-4-5-20250929": { + id: "claude-sonnet-4-5-20250929", + name: "claude-sonnet-4-5-20250929", + family: "claude-sonnet", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.7, output: 13.5 }, + limit: { context: 200000, output: 64000 }, + }, + "gemini-2.5-pro": { + id: "gemini-2.5-pro", + name: "gemini-2.5-pro", + family: "gemini-pro", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 1.125, output: 9 }, + limit: { context: 1048576, output: 65535 }, + }, + "grok-4-1-fast-non-reasoning": { + id: "grok-4-1-fast-non-reasoning", + name: "grok-4-1-fast-non-reasoning", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.18, output: 0.45 }, + limit: { context: 2000000, output: 2000000 }, + }, + "gpt-5.2": { + id: "gpt-5.2", + name: "gpt-5.2", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.575, output: 12.6 }, + limit: { context: 400000, output: 128000 }, + }, + "o4-mini": { + id: "o4-mini", + name: "o4-mini", + family: "o", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4 }, + limit: { context: 200000, output: 100000 }, + }, + "gemini-2.5-pro-preview-06-05": { + id: "gemini-2.5-pro-preview-06-05", + name: "gemini-2.5-pro-preview-06-05", + family: "gemini-pro", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 1.125, output: 9 }, + limit: { context: 1048576, output: 200000 }, + }, + "gemini-2.5-flash-lite-preview-06-17": { + id: "gemini-2.5-flash-lite-preview-06-17", + name: "gemini-2.5-flash-lite-preview-06-17", + family: "gemini-flash-lite", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "video", "image", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.09, output: 0.36 }, + limit: { context: 1048576, output: 65535 }, + }, + "gpt-5.2-codex": { + id: "gpt-5.2-codex", + name: "gpt-5.2-codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14 }, + limit: { context: 400000, output: 128000 }, + }, + "gemini-2.5-flash": { + id: "gemini-2.5-flash", + name: "gemini-2.5-flash", + family: "gemini-flash", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 2.25 }, + limit: { context: 1048576, output: 65535 }, + }, + "gpt-5.1-codex-mini": { + id: "gpt-5.1-codex-mini", + name: "gpt-5.1-codex-mini", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.225, output: 1.8 }, + limit: { context: 400000, output: 128000 }, + }, + "grok-code-fast-1": { + id: "grok-code-fast-1", + name: "grok-code-fast-1", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.18, output: 1.35 }, + limit: { context: 256000, output: 256000 }, + }, + "gpt-5.1": { + id: "gpt-5.1", + name: "gpt-5.1", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02", + last_updated: "2026-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.125, output: 9 }, + limit: { context: 400000, output: 128000 }, + }, + "grok-4-fast-reasoning": { + id: "grok-4-fast-reasoning", + name: "grok-4-fast-reasoning", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.18, output: 0.45 }, + limit: { context: 2000000, output: 2000000 }, + }, + "o3-mini": { + id: "o3-mini", + name: "o3-mini", + family: "o", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4 }, + limit: { context: 131072, output: 131072 }, + }, + "grok-4-0709": { + id: "grok-4-0709", + name: "grok-4-0709", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.7, output: 13.5 }, + limit: { context: 256000, output: 8192 }, + }, + "gpt-5-codex": { + id: "gpt-5-codex", + name: "gpt-5-codex", + family: "gpt-codex", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.125, output: 9 }, + limit: { context: 400000, output: 128000 }, + }, + "claude-opus-4-1-20250805": { + id: "claude-opus-4-1-20250805", + name: "claude-opus-4-1-20250805", + family: "claude-opus", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 13.5, output: 67.5 }, + limit: { context: 200000, output: 32000 }, + }, + "claude-haiku-4-5-20251001": { + id: "claude-haiku-4-5-20251001", + name: "claude-haiku-4-5-20251001", + family: "claude-haiku", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.9, output: 4.5 }, + limit: { context: 20000, output: 64000 }, + }, + "claude-sonnet-4-20250514": { + id: "claude-sonnet-4-20250514", + name: "claude-sonnet-4-20250514", + family: "claude-sonnet", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.7, output: 13.5 }, + limit: { context: 200000, output: 64000 }, + }, + "claude-opus-4-6": { + id: "claude-opus-4-6", + name: "claude-opus-4-6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02", + last_updated: "2026-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25 }, + limit: { context: 1000000, output: 128000 }, + }, + o3: { + id: "o3", + name: "o3", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 10, output: 40 }, + limit: { context: 131072, output: 131072 }, + }, + "gpt-5-pro": { + id: "gpt-5-pro", + name: "gpt-5-pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 13.5, output: 108 }, + limit: { context: 400000, output: 272000 }, + }, + "gemini-2.5-flash-lite": { + id: "gemini-2.5-flash-lite", + name: "gemini-2.5-flash-lite", + family: "gemini-flash-lite", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.09, output: 0.36 }, + limit: { context: 1048576, output: 65535 }, + }, + "gpt-5-chat-latest": { + id: "gpt-5-chat-latest", + name: "gpt-5-chat-latest", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.125, output: 9 }, + limit: { context: 400000, output: 128000 }, + }, + "claude-opus-4-20250514": { + id: "claude-opus-4-20250514", + name: "claude-opus-4-20250514", + family: "claude-opus", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 13.5, output: 67.5 }, + limit: { context: 200000, output: 32000 }, + }, + "gpt-5.1-codex": { + id: "gpt-5.1-codex", + name: "gpt-5.1-codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.125, output: 9 }, + limit: { context: 400000, output: 128000 }, + }, + "grok-4-fast-non-reasoning": { + id: "grok-4-fast-non-reasoning", + name: "grok-4-fast-non-reasoning", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.18, output: 0.45 }, + limit: { context: 2000000, output: 2000000 }, + }, + "deepseek/deepseek-v3-0324": { + id: "deepseek/deepseek-v3-0324", + name: "DeepSeek V3 0324", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.28, output: 1.14 }, + limit: { context: 163840, output: 163840 }, + }, + "deepseek/deepseek-v3.1": { + id: "deepseek/deepseek-v3.1", + name: "DeepSeek V3.1", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 1 }, + limit: { context: 163840, output: 32768 }, + }, + "deepseek/deepseek-r1-0528": { + id: "deepseek/deepseek-r1-0528", + name: "DeepSeek R1 0528", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.7, output: 2.5 }, + limit: { context: 163840, output: 32768 }, + }, + "zai-org/glm-4.7": { + id: "zai-org/glm-4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2 }, + limit: { context: 204800, output: 131072 }, + }, + "zai-org/glm-4.5": { + id: "zai-org/glm-4.5", + name: "GLM-4.5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2 }, + limit: { context: 131072, output: 98304 }, + }, + "zai-org/glm-4.5v": { + id: "zai-org/glm-4.5v", + name: "GLM 4.5V", + family: "glmv", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 1.8 }, + limit: { context: 65536, output: 16384 }, + }, + "zai-org/glm-4.7-flash": { + id: "zai-org/glm-4.7-flash", + name: "GLM-4.7-Flash", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.4 }, + limit: { context: 200000, output: 128000 }, + }, + "minimaxai/minimax-m1-80k": { + id: "minimaxai/minimax-m1-80k", + name: "MiniMax M1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 2.2 }, + limit: { context: 1000000, output: 40000 }, + }, + "xiaomimimo/mimo-v2-flash": { + id: "xiaomimimo/mimo-v2-flash", + name: "XiaomiMiMo/MiMo-V2-Flash", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 131072 }, + }, + "baidu/ernie-4.5-vl-424b-a47b": { + id: "baidu/ernie-4.5-vl-424b-a47b", + name: "ERNIE 4.5 VL 424B A47B", + family: "ernie", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.42, output: 1.25 }, + limit: { context: 123000, output: 16000 }, + }, + "baidu/ernie-4.5-300b-a47b-paddle": { + id: "baidu/ernie-4.5-300b-a47b-paddle", + name: "ERNIE 4.5 300B A47B", + family: "ernie", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.28, output: 1.1 }, + limit: { context: 123000, output: 12000 }, + }, + "minimax/minimax-m2.1": { + id: "minimax/minimax-m2.1", + name: "Minimax M2.1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 204800, output: 131072 }, + }, + "qwen/qwen3-235b-a22b-instruct-2507": { + id: "qwen/qwen3-235b-a22b-instruct-2507", + name: "Qwen3 235B A22B Instruct 2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.8 }, + limit: { context: 131072, output: 16384 }, + }, + "qwen/qwen3-32b-fp8": { + id: "qwen/qwen3-32b-fp8", + name: "Qwen3 32B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.45 }, + limit: { context: 40960, output: 20000 }, + }, + "qwen/qwen3-235b-a22b-thinking-2507": { + id: "qwen/qwen3-235b-a22b-thinking-2507", + name: "Qwen3 235B A22b Thinking 2507", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 3 }, + limit: { context: 131072, output: 131072 }, + }, + "qwen/qwen3-next-80b-a3b-thinking": { + id: "qwen/qwen3-next-80b-a3b-thinking", + name: "Qwen3 Next 80B A3B Thinking", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 1.5 }, + limit: { context: 65536, output: 65536 }, + }, + "qwen/qwen3-next-80b-a3b-instruct": { + id: "qwen/qwen3-next-80b-a3b-instruct", + name: "Qwen3 Next 80B A3B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 1.5 }, + limit: { context: 65536, output: 65536 }, + }, + "qwen/qwen3-30b-a3b-fp8": { + id: "qwen/qwen3-30b-a3b-fp8", + name: "Qwen3 30B A3B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.09, output: 0.45 }, + limit: { context: 40960, output: 20000 }, + }, + "qwen/qwen3-coder-next": { + id: "qwen/qwen3-coder-next", + name: "qwen/qwen3-coder-next", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02", + last_updated: "2026-02", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 1.5 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen/qwen3-coder-480b-a35b-instruct": { + id: "qwen/qwen3-coder-480b-a35b-instruct", + name: "Qwen3 Coder 480B A35B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.29, output: 1.2 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen/qwen3-235b-a22b-fp8": { + id: "qwen/qwen3-235b-a22b-fp8", + name: "Qwen3 235B A22B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.8 }, + limit: { context: 40960, output: 20000 }, + }, + "moonshotai/kimi-k2.5": { + id: "moonshotai/kimi-k2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3 }, + limit: { context: 262144, output: 262144 }, + }, + "moonshotai/kimi-k2-instruct": { + id: "moonshotai/kimi-k2-instruct", + name: "Kimi K2 Instruct", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.57, output: 2.3 }, + limit: { context: 131072, output: 131072 }, + }, + "moonshotai/kimi-k2-0905": { + id: "moonshotai/kimi-k2-0905", + name: "Kimi K2 0905", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5 }, + limit: { context: 262144, output: 262144 }, + }, + }, + }, + bailing: { + id: "bailing", + env: ["BAILING_API_TOKEN"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.tbox.cn/api/llm/v1/chat/completions", + name: "Bailing", + doc: "https://alipaytbox.yuque.com/sxs0ba/ling/intro", + models: { + "Ring-1T": { + id: "Ring-1T", + name: "Ring-1T", + family: "ring", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2024-06", + release_date: "2025-10", + last_updated: "2025-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.57, output: 2.29 }, + limit: { context: 128000, output: 32000 }, + }, + "Ling-1T": { + id: "Ling-1T", + name: "Ling-1T", + family: "ling", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-06", + release_date: "2025-10", + last_updated: "2025-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.57, output: 2.29 }, + limit: { context: 128000, output: 32000 }, + }, + }, + }, + iflowcn: { + id: "iflowcn", + env: ["IFLOW_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://apis.iflow.cn/v1", + name: "iFlow", + doc: "https://platform.iflow.cn/en/docs", + models: { + "qwen3-coder-plus": { + id: "qwen3-coder-plus", + name: "Qwen3-Coder-Plus", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-01", + last_updated: "2025-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 256000, output: 64000 }, + }, + "qwen3-32b": { + id: "qwen3-32b", + name: "Qwen3-32B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 32000 }, + }, + "deepseek-r1": { + id: "deepseek-r1", + name: "DeepSeek-R1", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 32000 }, + }, + "qwen3-max": { + id: "qwen3-max", + name: "Qwen3-Max", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 256000, output: 32000 }, + }, + "qwen3-235b-a22b-instruct": { + id: "qwen3-235b-a22b-instruct", + name: "Qwen3-235B-A22B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-01", + last_updated: "2025-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 256000, output: 64000 }, + }, + "qwen3-235b-a22b-thinking-2507": { + id: "qwen3-235b-a22b-thinking-2507", + name: "Qwen3-235B-A22B-Thinking", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-01", + last_updated: "2025-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 256000, output: 64000 }, + }, + "kimi-k2-0905": { + id: "kimi-k2-0905", + name: "Kimi-K2-0905", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 256000, output: 64000 }, + }, + "glm-4.6": { + id: "glm-4.6", + name: "GLM-4.6", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-12-01", + last_updated: "2025-11-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 200000, output: 128000 }, + }, + "qwen3-vl-plus": { + id: "qwen3-vl-plus", + name: "Qwen3-VL-Plus", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 256000, output: 32000 }, + }, + "deepseek-v3.2": { + id: "deepseek-v3.2", + name: "DeepSeek-V3.2-Exp", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 64000 }, + }, + "qwen3-235b": { + id: "qwen3-235b", + name: "Qwen3-235B-A22B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 32000 }, + }, + "kimi-k2": { + id: "kimi-k2", + name: "Kimi-K2", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 64000 }, + }, + "qwen3-max-preview": { + id: "qwen3-max-preview", + name: "Qwen3-Max-Preview", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 256000, output: 32000 }, + }, + "deepseek-v3": { + id: "deepseek-v3", + name: "DeepSeek-V3", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-12-26", + last_updated: "2024-12-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 32000 }, + }, + }, + }, + v0: { + id: "v0", + env: ["V0_API_KEY"], + npm: "@ai-sdk/vercel", + name: "v0", + doc: "https://sdk.vercel.ai/providers/ai-sdk-providers/vercel", + models: { + "v0-1.5-lg": { + id: "v0-1.5-lg", + name: "v0-1.5-lg", + family: "v0", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-06-09", + last_updated: "2025-06-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75 }, + limit: { context: 512000, output: 32000 }, + }, + "v0-1.0-md": { + id: "v0-1.0-md", + name: "v0-1.0-md", + family: "v0", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 128000, output: 32000 }, + }, + "v0-1.5-md": { + id: "v0-1.5-md", + name: "v0-1.5-md", + family: "v0", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-06-09", + last_updated: "2025-06-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 128000, output: 32000 }, + }, + }, + }, + huggingface: { + id: "huggingface", + env: ["HF_TOKEN"], + npm: "@ai-sdk/openai-compatible", + api: "https://router.huggingface.co/v1", + name: "Hugging Face", + doc: "https://huggingface.co/docs/inference-providers", + models: { + "Qwen/Qwen3.5-397B-A17B": { + id: "Qwen/Qwen3.5-397B-A17B", + name: "Qwen3.5-397B-A17B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-01", + last_updated: "2026-02-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3.6 }, + limit: { context: 262144, output: 32768 }, + }, + "Qwen/Qwen3-Coder-Next": { + id: "Qwen/Qwen3-Coder-Next", + name: "Qwen3-Coder-Next", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-03", + last_updated: "2026-02-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 1.5 }, + limit: { context: 262144, output: 65536 }, + }, + "Qwen/Qwen3-Next-80B-A3B-Instruct": { + id: "Qwen/Qwen3-Next-80B-A3B-Instruct", + name: "Qwen3-Next-80B-A3B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-11", + last_updated: "2025-09-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 1 }, + limit: { context: 262144, output: 66536 }, + }, + "Qwen/Qwen3-Embedding-8B": { + id: "Qwen/Qwen3-Embedding-8B", + name: "Qwen 3 Embedding 8B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + knowledge: "2024-12", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.01, output: 0 }, + limit: { context: 32000, output: 4096 }, + }, + "Qwen/Qwen3-235B-A22B-Thinking-2507": { + id: "Qwen/Qwen3-235B-A22B-Thinking-2507", + name: "Qwen3-235B-A22B-Thinking-2507", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-25", + last_updated: "2025-07-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 3 }, + limit: { context: 262144, output: 131072 }, + }, + "Qwen/Qwen3-Next-80B-A3B-Thinking": { + id: "Qwen/Qwen3-Next-80B-A3B-Thinking", + name: "Qwen3-Next-80B-A3B-Thinking", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-11", + last_updated: "2025-09-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 2 }, + limit: { context: 262144, output: 131072 }, + }, + "Qwen/Qwen3-Embedding-4B": { + id: "Qwen/Qwen3-Embedding-4B", + name: "Qwen 3 Embedding 4B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + knowledge: "2024-12", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.01, output: 0 }, + limit: { context: 32000, output: 2048 }, + }, + "Qwen/Qwen3-Coder-480B-A35B-Instruct": { + id: "Qwen/Qwen3-Coder-480B-A35B-Instruct", + name: "Qwen3-Coder-480B-A35B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2, output: 2 }, + limit: { context: 262144, output: 66536 }, + }, + "zai-org/GLM-4.7-Flash": { + id: "zai-org/GLM-4.7-Flash", + name: "GLM-4.7-Flash", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-08-08", + last_updated: "2025-08-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 200000, output: 128000 }, + }, + "zai-org/GLM-4.7": { + id: "zai-org/GLM-4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2, cache_read: 0.11 }, + limit: { context: 204800, output: 131072 }, + }, + "zai-org/GLM-5.1": { + id: "zai-org/GLM-5.1", + name: "GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-04-03", + last_updated: "2026-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3.2, cache_read: 0.2 }, + limit: { context: 202752, output: 131072 }, + }, + "zai-org/GLM-5": { + id: "zai-org/GLM-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3.2, cache_read: 0.2 }, + limit: { context: 202752, output: 131072 }, + }, + "XiaomiMiMo/MiMo-V2-Flash": { + id: "XiaomiMiMo/MiMo-V2-Flash", + name: "MiMo-V2-Flash", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-12-16", + last_updated: "2025-12-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 262144, output: 4096 }, + }, + "deepseek-ai/DeepSeek-R1-0528": { + id: "deepseek-ai/DeepSeek-R1-0528", + name: "DeepSeek-R1-0528", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05", + release_date: "2025-05-28", + last_updated: "2025-05-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 3, output: 5 }, + limit: { context: 163840, output: 163840 }, + }, + "deepseek-ai/DeepSeek-V3.2": { + id: "deepseek-ai/DeepSeek-V3.2", + name: "DeepSeek-V3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.28, output: 0.4 }, + limit: { context: 163840, output: 65536 }, + }, + "moonshotai/Kimi-K2-Thinking": { + id: "moonshotai/Kimi-K2-Thinking", + name: "Kimi-K2-Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-08", + release_date: "2025-11-06", + last_updated: "2025-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5, cache_read: 0.15 }, + limit: { context: 262144, output: 262144 }, + }, + "moonshotai/Kimi-K2.6": { + id: "moonshotai/Kimi-K2.6", + name: "Kimi-K2.6", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-20", + last_updated: "2026-04-20", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4, cache_read: 0.16 }, + limit: { context: 262144, output: 262144 }, + }, + "moonshotai/Kimi-K2-Instruct": { + id: "moonshotai/Kimi-K2-Instruct", + name: "Kimi-K2-Instruct", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-07-14", + last_updated: "2025-07-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3 }, + limit: { context: 131072, output: 16384 }, + }, + "moonshotai/Kimi-K2-Instruct-0905": { + id: "moonshotai/Kimi-K2-Instruct-0905", + name: "Kimi-K2-Instruct-0905", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-09-04", + last_updated: "2025-09-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3 }, + limit: { context: 262144, output: 16384 }, + }, + "moonshotai/Kimi-K2.5": { + id: "moonshotai/Kimi-K2.5", + name: "Kimi-K2.5", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-01", + release_date: "2026-01-01", + last_updated: "2026-01-01", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3, cache_read: 0.1 }, + limit: { context: 262144, output: 262144 }, + }, + "MiniMaxAI/MiniMax-M2.5": { + id: "MiniMaxAI/MiniMax-M2.5", + name: "MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.03 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMaxAI/MiniMax-M2.7": { + id: "MiniMaxAI/MiniMax-M2.7", + name: "MiniMax-M2.7", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.06 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMaxAI/MiniMax-M2.1": { + id: "MiniMaxAI/MiniMax-M2.1", + name: "MiniMax-M2.1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-10", + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 204800, output: 131072 }, + }, + "deepseek-ai/DeepSeek-V4-Pro": { + id: "deepseek-ai/DeepSeek-V4-Pro", + name: "DeepSeek V4 Pro", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.74, output: 3.48, cache_read: 0.145 }, + limit: { context: 1048576, output: 393216 }, + }, + }, + }, + zenmux: { + id: "zenmux", + env: ["ZENMUX_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://zenmux.ai/api/v1", + name: "ZenMux", + doc: "https://docs.zenmux.ai", + models: { + "deepseek/deepseek-chat": { + id: "deepseek/deepseek-chat", + name: "DeepSeek-V3.2 (Non-thinking Mode)", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.28, output: 0.42, cache_read: 0.03 }, + limit: { context: 128000, output: 64000 }, + }, + "deepseek/deepseek-v3.2-exp": { + id: "deepseek/deepseek-v3.2-exp", + name: "DeepSeek-V3.2-Exp", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.22, output: 0.33 }, + limit: { context: 163000, output: 64000 }, + }, + "deepseek/deepseek-v3.2": { + id: "deepseek/deepseek-v3.2", + name: "DeepSeek V3.2", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-12-05", + last_updated: "2025-12-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.28, output: 0.43 }, + limit: { context: 128000, output: 64000 }, + }, + "inclusionai/ring-1t": { + id: "inclusionai/ring-1t", + name: "Ring-1T", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-10-12", + last_updated: "2025-10-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.56, output: 2.24, cache_read: 0.11 }, + limit: { context: 128000, output: 64000 }, + }, + "inclusionai/ling-1t": { + id: "inclusionai/ling-1t", + name: "Ling-1T", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-10-09", + last_updated: "2025-10-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.56, output: 2.24, cache_read: 0.11 }, + limit: { context: 128000, output: 64000 }, + }, + "stepfun/step-3.5-flash-free": { + id: "stepfun/step-3.5-flash-free", + name: "Step 3.5 Flash (Free)", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2026-02-02", + last_updated: "2026-02-02", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 256000, output: 64000 }, + }, + "stepfun/step-3.5-flash": { + id: "stepfun/step-3.5-flash", + name: "Step 3.5 Flash", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2026-02-02", + last_updated: "2026-02-02", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 256000, output: 64000 }, + }, + "stepfun/step-3": { + id: "stepfun/step-3", + name: "Step-3", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-07-31", + last_updated: "2025-07-31", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.21, output: 0.57 }, + limit: { context: 65536, output: 64000 }, + }, + "kuaishou/kat-coder-pro-v2": { + id: "kuaishou/kat-coder-pro-v2", + name: "KAT-Coder-Pro-V2", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-03-30", + last_updated: "2026-03-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2, cache_read: 0.06 }, + limit: { context: 256000, output: 80000 }, + }, + "x-ai/grok-4-fast": { + id: "x-ai/grok-4-fast", + name: "Grok 4 Fast", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-09-19", + last_updated: "2025-09-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 64000 }, + }, + "x-ai/grok-code-fast-1": { + id: "x-ai/grok-code-fast-1", + name: "Grok Code Fast 1", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-08-26", + last_updated: "2025-08-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.5, cache_read: 0.02 }, + limit: { context: 256000, output: 64000 }, + }, + "x-ai/grok-4.1-fast-non-reasoning": { + id: "x-ai/grok-4.1-fast-non-reasoning", + name: "Grok 4.1 Fast Non Reasoning", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-11-20", + last_updated: "2025-11-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 64000 }, + }, + "x-ai/grok-4": { + id: "x-ai/grok-4", + name: "Grok 4", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.75 }, + limit: { context: 256000, output: 64000 }, + }, + "x-ai/grok-4.1-fast": { + id: "x-ai/grok-4.1-fast", + name: "Grok 4.1 Fast", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-11-20", + last_updated: "2025-11-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 64000 }, + }, + "x-ai/grok-4.2-fast": { + id: "x-ai/grok-4.2-fast", + name: "Grok 4.2 Fast", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-03-20", + last_updated: "2026-03-20", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 9 }, + limit: { context: 2000000, output: 30000 }, + }, + "x-ai/grok-4.2-fast-non-reasoning": { + id: "x-ai/grok-4.2-fast-non-reasoning", + name: "Grok 4.2 Fast Non Reasoning", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-03-20", + last_updated: "2026-03-20", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 9 }, + limit: { context: 2000000, output: 30000 }, + }, + "openai/gpt-5.3-chat": { + id: "openai/gpt-5.3-chat", + name: "GPT-5.3 Chat", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-03-20", + last_updated: "2026-03-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14 }, + limit: { context: 128000, output: 16380 }, + provider: { npm: "@ai-sdk/openai", api: "https://zenmux.ai/api/v1" }, + }, + "openai/gpt-5.2-pro": { + id: "openai/gpt-5.2-pro", + name: "GPT-5.2-Pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 21, output: 168 }, + limit: { context: 400000, output: 128000 }, + provider: { npm: "@ai-sdk/openai", api: "https://zenmux.ai/api/v1" }, + }, + "openai/gpt-5.3-codex": { + id: "openai/gpt-5.3-codex", + name: "GPT-5.3 Codex", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-03-20", + last_updated: "2026-03-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14 }, + limit: { context: 400000, output: 128000 }, + provider: { npm: "@ai-sdk/openai", api: "https://zenmux.ai/api/v1" }, + }, + "openai/gpt-5.2": { + id: "openai/gpt-5.2", + name: "GPT-5.2", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-01-01", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["image", "text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.17 }, + limit: { context: 400000, output: 64000 }, + provider: { npm: "@ai-sdk/openai", api: "https://zenmux.ai/api/v1" }, + }, + "openai/gpt-5.4-mini": { + id: "openai/gpt-5.4-mini", + name: "GPT-5.4 Mini", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-03-20", + last_updated: "2026-03-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.75, output: 4.5 }, + limit: { context: 400000, output: 128000 }, + provider: { npm: "@ai-sdk/openai", api: "https://zenmux.ai/api/v1" }, + }, + "openai/gpt-5.1-chat": { + id: "openai/gpt-5.1-chat", + name: "GPT-5.1 Chat", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["pdf", "image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.12 }, + limit: { context: 128000, output: 64000 }, + provider: { npm: "@ai-sdk/openai", api: "https://zenmux.ai/api/v1" }, + }, + "openai/gpt-5.4-nano": { + id: "openai/gpt-5.4-nano", + name: "GPT-5.4 Nano", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-03-20", + last_updated: "2026-03-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.25 }, + limit: { context: 400000, output: 128000 }, + provider: { npm: "@ai-sdk/openai", api: "https://zenmux.ai/api/v1" }, + }, + "openai/gpt-5.2-codex": { + id: "openai/gpt-5.2-codex", + name: "GPT-5.2-Codex", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-01-01", + release_date: "2026-01-15", + last_updated: "2026-01-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.17 }, + limit: { context: 400000, output: 64000 }, + provider: { npm: "@ai-sdk/openai", api: "https://zenmux.ai/api/v1" }, + }, + "openai/gpt-5.1-codex-mini": { + id: "openai/gpt-5.1-codex-mini", + name: "GPT-5.1-Codex-Mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.03 }, + limit: { context: 400000, output: 64000 }, + provider: { npm: "@ai-sdk/openai", api: "https://zenmux.ai/api/v1" }, + }, + "openai/gpt-5.1": { + id: "openai/gpt-5.1", + name: "GPT-5.1", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["image", "text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.12 }, + limit: { context: 400000, output: 64000 }, + provider: { npm: "@ai-sdk/openai", api: "https://zenmux.ai/api/v1" }, + }, + "openai/gpt-5.4-pro": { + id: "openai/gpt-5.4-pro", + name: "GPT-5.4 Pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-03-20", + last_updated: "2026-03-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 45, output: 225 }, + limit: { context: 1050000, output: 128000 }, + provider: { npm: "@ai-sdk/openai", api: "https://zenmux.ai/api/v1" }, + }, + "openai/gpt-5-codex": { + id: "openai/gpt-5-codex", + name: "GPT-5 Codex", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-09-23", + last_updated: "2025-09-23", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.12 }, + limit: { context: 400000, output: 64000 }, + provider: { npm: "@ai-sdk/openai", api: "https://zenmux.ai/api/v1" }, + }, + "openai/gpt-5.4": { + id: "openai/gpt-5.4", + name: "GPT-5.4", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-03-20", + last_updated: "2026-03-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3.75, output: 18.75 }, + limit: { context: 1050000, output: 128000 }, + provider: { npm: "@ai-sdk/openai", api: "https://zenmux.ai/api/v1" }, + }, + "openai/gpt-5": { + id: "openai/gpt-5", + name: "GPT-5", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.12 }, + limit: { context: 400000, output: 64000 }, + provider: { npm: "@ai-sdk/openai", api: "https://zenmux.ai/api/v1" }, + }, + "openai/gpt-5.1-codex": { + id: "openai/gpt-5.1-codex", + name: "GPT-5.1-Codex", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.12 }, + limit: { context: 400000, output: 64000 }, + provider: { npm: "@ai-sdk/openai", api: "https://zenmux.ai/api/v1" }, + }, + "z-ai/glm-4.7-flash-free": { + id: "z-ai/glm-4.7-flash-free", + name: "GLM 4.7 Flash (Free)", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-01-01", + release_date: "2026-01-19", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 200000, output: 64000 }, + }, + "z-ai/glm-5v-turbo": { + id: "z-ai/glm-5v-turbo", + name: "GLM 5V Turbo", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-04-01", + last_updated: "2026-04-01", + modalities: { input: ["text", "image", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.726, output: 3.1946, cache_read: 0.1743 }, + limit: { context: 200000, output: 128000 }, + }, + "z-ai/glm-4.7": { + id: "z-ai/glm-4.7", + name: "GLM 4.7", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.28, output: 1.14, cache_read: 0.06 }, + limit: { context: 200000, output: 64000 }, + }, + "z-ai/glm-5": { + id: "z-ai/glm-5", + name: "GLM 5", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-01-01", + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.58, output: 2.6, cache_read: 0.14 }, + limit: { context: 200000, output: 128000 }, + }, + "z-ai/glm-4.7-flashx": { + id: "z-ai/glm-4.7-flashx", + name: "GLM 4.7 FlashX", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-01-01", + release_date: "2026-01-19", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.07, output: 0.42, cache_read: 0.01 }, + limit: { context: 200000, output: 64000 }, + }, + "z-ai/glm-4.6v-flash-free": { + id: "z-ai/glm-4.6v-flash-free", + name: "GLM 4.6V Flash (Free)", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-12-08", + last_updated: "2025-12-08", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 200000, output: 64000 }, + }, + "z-ai/glm-5.1": { + id: "z-ai/glm-5.1", + name: "GLM-5.1", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-04-03", + last_updated: "2026-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8781, output: 3.5126, cache_read: 0.1903 }, + limit: { context: 200000, output: 131072 }, + }, + "z-ai/glm-4.6v-flash": { + id: "z-ai/glm-4.6v-flash", + name: "GLM 4.6V FlashX", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-12-08", + last_updated: "2025-12-08", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.02, output: 0.21, cache_read: 0.0043 }, + limit: { context: 200000, output: 64000 }, + }, + "z-ai/glm-4.5": { + id: "z-ai/glm-4.5", + name: "GLM 4.5", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-07-25", + last_updated: "2025-07-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.35, output: 1.54, cache_read: 0.07 }, + limit: { context: 128000, output: 64000 }, + }, + "z-ai/glm-4.5-air": { + id: "z-ai/glm-4.5-air", + name: "GLM 4.5 Air", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-07-25", + last_updated: "2025-07-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.11, output: 0.56, cache_read: 0.02 }, + limit: { context: 128000, output: 64000 }, + }, + "z-ai/glm-5-turbo": { + id: "z-ai/glm-5-turbo", + name: "GLM 5 Turbo", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2026-03-20", + last_updated: "2026-03-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.88, output: 3.48 }, + limit: { context: 200000, output: 128000 }, + }, + "z-ai/glm-4.6": { + id: "z-ai/glm-4.6", + name: "GLM 4.6", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.35, output: 1.54, cache_read: 0.07 }, + limit: { context: 200000, output: 64000 }, + }, + "z-ai/glm-4.6v": { + id: "z-ai/glm-4.6v", + name: "GLM 4.6V", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-12-08", + last_updated: "2025-12-08", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.42, cache_read: 0.03 }, + limit: { context: 200000, output: 64000 }, + }, + "volcengine/doubao-seed-2.0-code": { + id: "volcengine/doubao-seed-2.0-code", + name: "Doubao Seed 2.0 Code", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2026-03-20", + last_updated: "2026-03-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.9, output: 4.48 }, + limit: { context: 256000, output: 32000 }, + }, + "volcengine/doubao-seed-code": { + id: "volcengine/doubao-seed-code", + name: "Doubao-Seed-Code", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-11-11", + last_updated: "2025-11-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.17, output: 1.12, cache_read: 0.03 }, + limit: { context: 256000, output: 64000 }, + }, + "volcengine/doubao-seed-2.0-mini": { + id: "volcengine/doubao-seed-2.0-mini", + name: "Doubao-Seed-2.0-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2026-02-14", + release_date: "2026-02-14", + last_updated: "2026-02-14", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.03, output: 0.28, cache_read: 0.01, cache_write: 0.0024 }, + limit: { context: 256000, output: 64000 }, + }, + "volcengine/doubao-seed-2.0-lite": { + id: "volcengine/doubao-seed-2.0-lite", + name: "Doubao-Seed-2.0-lite", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2026-02-14", + release_date: "2026-02-14", + last_updated: "2026-02-14", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.09, output: 0.51, cache_read: 0.02, cache_write: 0.0024 }, + limit: { context: 256000, output: 64000 }, + }, + "volcengine/doubao-seed-1.8": { + id: "volcengine/doubao-seed-1.8", + name: "Doubao-Seed-1.8", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-12-18", + last_updated: "2025-12-18", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.11, output: 0.28, cache_read: 0.02, cache_write: 0.0024 }, + limit: { context: 256000, output: 64000 }, + }, + "volcengine/doubao-seed-2.0-pro": { + id: "volcengine/doubao-seed-2.0-pro", + name: "Doubao-Seed-2.0-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2026-02-14", + release_date: "2026-02-14", + last_updated: "2026-02-14", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.45, output: 2.24, cache_read: 0.09, cache_write: 0.0024 }, + limit: { context: 256000, output: 64000 }, + }, + "baidu/ernie-5.0-thinking-preview": { + id: "baidu/ernie-5.0-thinking-preview", + name: "ERNIE 5.0", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2026-01-22", + last_updated: "2026-01-22", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.84, output: 3.37 }, + limit: { context: 128000, output: 64000 }, + }, + "minimax/minimax-m2.7": { + id: "minimax/minimax-m2.7", + name: "MiniMax M2.7", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2026-03-20", + last_updated: "2026-03-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3055, output: 1.2219 }, + limit: { context: 204800, output: 131070 }, + provider: { npm: "@ai-sdk/anthropic", api: "https://zenmux.ai/api/anthropic/v1" }, + }, + "minimax/minimax-m2.7-highspeed": { + id: "minimax/minimax-m2.7-highspeed", + name: "MiniMax M2.7 highspeed", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2026-03-20", + last_updated: "2026-03-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.611, output: 2.4439 }, + limit: { context: 204800, output: 131070 }, + provider: { npm: "@ai-sdk/anthropic", api: "https://zenmux.ai/api/anthropic/v1" }, + }, + "minimax/minimax-m2": { + id: "minimax/minimax-m2", + name: "MiniMax M2", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-10-27", + last_updated: "2025-10-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2, cache_read: 0.03, cache_write: 0.38 }, + limit: { context: 204000, output: 64000 }, + provider: { npm: "@ai-sdk/anthropic", api: "https://zenmux.ai/api/anthropic/v1" }, + }, + "minimax/minimax-m2.1": { + id: "minimax/minimax-m2.1", + name: "MiniMax M2.1", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2, cache_read: 0.03, cache_write: 0.38 }, + limit: { context: 204000, output: 64000 }, + provider: { npm: "@ai-sdk/anthropic", api: "https://zenmux.ai/api/anthropic/v1" }, + }, + "minimax/minimax-m2.5-lightning": { + id: "minimax/minimax-m2.5-lightning", + name: "MiniMax M2.5 highspeed", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2026-02-13", + last_updated: "2026-02-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 4.8, cache_read: 0.06, cache_write: 0.75 }, + limit: { context: 204800, output: 131072 }, + provider: { npm: "@ai-sdk/anthropic", api: "https://zenmux.ai/api/anthropic/v1" }, + }, + "minimax/minimax-m2.5": { + id: "minimax/minimax-m2.5", + name: "MiniMax M2.5", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2026-02-13", + last_updated: "2026-02-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2, cache_read: 0.03, cache_write: 0.375 }, + limit: { context: 204800, output: 131072 }, + provider: { npm: "@ai-sdk/anthropic", api: "https://zenmux.ai/api/anthropic/v1" }, + }, + "qwen/qwen3-coder-plus": { + id: "qwen/qwen3-coder-plus", + name: "Qwen3-Coder-Plus", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 1000000, output: 64000 }, + }, + "qwen/qwen3.5-flash": { + id: "qwen/qwen3.5-flash", + name: "Qwen3.5 Flash", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2026-03-20", + last_updated: "2026-03-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 1020000, output: 1020000 }, + }, + "qwen/qwen3.6-plus": { + id: "qwen/qwen3.6-plus", + name: "Qwen3.6-Plus", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-30", + last_updated: "2026-03-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { + input: 0.5, + output: 3, + cache_read: 0.05, + cache_write: 0.625, + context_over_200k: { input: 2, output: 6, cache_read: 0.2, cache_write: 2.5 }, + }, + limit: { context: 1000000, output: 64000 }, + }, + "qwen/qwen3-max": { + id: "qwen/qwen3-max", + name: "Qwen3-Max-Thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2026-01-23", + last_updated: "2026-01-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.2, output: 6 }, + limit: { context: 256000, output: 64000 }, + }, + "qwen/qwen3.5-plus": { + id: "qwen/qwen3.5-plus", + name: "Qwen3.5 Plus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2026-03-20", + last_updated: "2026-03-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 4.8 }, + limit: { context: 1000000, output: 64000 }, + }, + "google/gemini-3.1-flash-lite-preview": { + id: "google/gemini-3.1-flash-lite-preview", + name: "Gemini 3.1 Flash Lite Preview", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-03-20", + last_updated: "2025-03-20", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.5 }, + limit: { context: 1050000, output: 65530 }, + }, + "google/gemini-3.1-pro-preview": { + id: "google/gemini-3.1-pro-preview", + name: "Gemini 3.1 Pro Preview", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2026-02-19", + release_date: "2026-02-19", + last_updated: "2026-02-19", + modalities: { input: ["text", "image", "pdf", "audio", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2, cache_write: 4.5 }, + limit: { context: 1048000, output: 64000 }, + }, + "google/gemini-3-flash-preview": { + id: "google/gemini-3-flash-preview", + name: "Gemini 3 Flash Preview", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text", "image", "pdf", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 3, cache_read: 0.05, cache_write: 1 }, + limit: { context: 1048000, output: 64000 }, + }, + "google/gemini-2.5-pro": { + id: "google/gemini-2.5-pro", + name: "Gemini 2.5 Pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["pdf", "image", "text", "audio", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.31, cache_write: 4.5 }, + limit: { context: 1048000, output: 64000 }, + }, + "google/gemini-2.5-flash": { + id: "google/gemini-2.5-flash", + name: "Gemini 2.5 Flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["pdf", "image", "text", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5, cache_read: 0.07, cache_write: 1 }, + limit: { context: 1048000, output: 64000 }, + }, + "google/gemini-2.5-flash-lite": { + id: "google/gemini-2.5-flash-lite", + name: "Gemini 2.5 Flash Lite", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-07-22", + last_updated: "2025-07-22", + modalities: { input: ["pdf", "image", "text", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.03, cache_write: 1 }, + limit: { context: 1048000, output: 64000 }, + }, + "sapiens-ai/agnes-1.5-lite": { + id: "sapiens-ai/agnes-1.5-lite", + name: "Agnes 1.5 Lite", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-03-26", + last_updated: "2026-03-26", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.12, output: 0.6 }, + limit: { context: 256000, output: 256000 }, + }, + "sapiens-ai/agnes-1.5-pro": { + id: "sapiens-ai/agnes-1.5-pro", + name: "Agnes 1.5 Pro", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-21", + last_updated: "2026-03-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.16, output: 0.8 }, + limit: { context: 256000, output: 256000 }, + }, + "moonshotai/kimi-k2.5": { + id: "moonshotai/kimi-k2.5", + name: "Kimi K2.5", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: false, + knowledge: "2025-01-01", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.58, output: 3.02, cache_read: 0.1 }, + limit: { context: 262000, output: 64000 }, + }, + "moonshotai/kimi-k2-thinking-turbo": { + id: "moonshotai/kimi-k2-thinking-turbo", + name: "Kimi K2 Thinking Turbo", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-11-06", + last_updated: "2025-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.15, output: 8, cache_read: 0.15 }, + limit: { context: 262000, output: 64000 }, + }, + "moonshotai/kimi-k2-0905": { + id: "moonshotai/kimi-k2-0905", + name: "Kimi K2 0905", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-09-04", + last_updated: "2025-09-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 2.5, cache_read: 0.15 }, + limit: { context: 262000, output: 64000 }, + }, + "moonshotai/kimi-k2.6": { + id: "moonshotai/kimi-k2.6", + name: "Kimi K2.6", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: false, + knowledge: "2025-01-01", + release_date: "2026-04-20", + last_updated: "2026-04-20", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4, cache_read: 0.16 }, + limit: { context: 262140, output: 262140 }, + }, + "moonshotai/kimi-k2-thinking": { + id: "moonshotai/kimi-k2-thinking", + name: "Kimi K2 Thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-11-06", + last_updated: "2025-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 2.5, cache_read: 0.15 }, + limit: { context: 262000, output: 64000 }, + }, + "anthropic/claude-opus-4.1": { + id: "anthropic/claude-opus-4.1", + name: "Claude Opus 4.1", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["image", "text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 64000 }, + provider: { npm: "@ai-sdk/anthropic", api: "https://zenmux.ai/api/anthropic/v1" }, + }, + "anthropic/claude-3.7-sonnet": { + id: "anthropic/claude-3.7-sonnet", + name: "Claude 3.7 Sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-02-24", + last_updated: "2025-02-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + provider: { npm: "@ai-sdk/anthropic", api: "https://zenmux.ai/api/anthropic/v1" }, + }, + "anthropic/claude-opus-4.6": { + id: "anthropic/claude-opus-4.6", + name: "Claude Opus 4.6", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-06", + last_updated: "2026-02-06", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + provider: { npm: "@ai-sdk/anthropic", api: "https://zenmux.ai/api/anthropic/v1" }, + }, + "anthropic/claude-opus-4.7": { + id: "anthropic/claude-opus-4.7", + name: "Claude Opus 4.7", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + provider: { npm: "@ai-sdk/anthropic", api: "https://zenmux.ai/api/anthropic/v1" }, + }, + "anthropic/claude-sonnet-4": { + id: "anthropic/claude-sonnet-4", + name: "Claude Sonnet 4", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["image", "text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 1000000, output: 64000 }, + provider: { npm: "@ai-sdk/anthropic", api: "https://zenmux.ai/api/anthropic/v1" }, + }, + "anthropic/claude-sonnet-4.5": { + id: "anthropic/claude-sonnet-4.5", + name: "Claude Sonnet 4.5", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 1000000, output: 64000 }, + provider: { npm: "@ai-sdk/anthropic", api: "https://zenmux.ai/api/anthropic/v1" }, + }, + "anthropic/claude-opus-4.5": { + id: "anthropic/claude-opus-4.5", + name: "Claude Opus 4.5", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-11-24", + last_updated: "2025-11-24", + modalities: { input: ["pdf", "image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 64000 }, + provider: { npm: "@ai-sdk/anthropic", api: "https://zenmux.ai/api/anthropic/v1" }, + }, + "anthropic/claude-opus-4": { + id: "anthropic/claude-opus-4", + name: "Claude Opus 4", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["image", "text", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + provider: { npm: "@ai-sdk/anthropic", api: "https://zenmux.ai/api/anthropic/v1" }, + }, + "anthropic/claude-3.5-haiku": { + id: "anthropic/claude-3.5-haiku", + name: "Claude 3.5 Haiku", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2024-11-04", + last_updated: "2024-11-04", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 4, cache_read: 0.08, cache_write: 1 }, + limit: { context: 200000, output: 64000 }, + provider: { npm: "@ai-sdk/anthropic", api: "https://zenmux.ai/api/anthropic/v1" }, + }, + "anthropic/claude-haiku-4.5": { + id: "anthropic/claude-haiku-4.5", + name: "Claude Haiku 4.5", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + provider: { npm: "@ai-sdk/anthropic", api: "https://zenmux.ai/api/anthropic/v1" }, + }, + "anthropic/claude-sonnet-4.6": { + id: "anthropic/claude-sonnet-4.6", + name: "Claude Sonnet 4.6", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-18", + last_updated: "2026-02-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 1000000, output: 64000 }, + provider: { npm: "@ai-sdk/anthropic", api: "https://zenmux.ai/api/anthropic/v1" }, + }, + "deepseek/deepseek-v4-flash": { + id: "deepseek/deepseek-v4-flash", + name: "DeepSeek V4 Flash", + family: "deepseek-flash", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.28, cache_read: 0.028 }, + limit: { context: 1000000, output: 384000 }, + }, + "deepseek/deepseek-v4-pro": { + id: "deepseek/deepseek-v4-pro", + name: "DeepSeek V4 Pro", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.74, output: 3.48, cache_read: 0.145 }, + limit: { context: 1000000, output: 384000 }, + }, + "tencent/hy3-preview": { + id: "tencent/hy3-preview", + name: "Hy3 preview", + family: "Hy", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-20", + last_updated: "2026-04-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.172, output: 0.572, cache_read: 0.058, cache_write: 0 }, + limit: { context: 256000, output: 64000 }, + }, + "openai/gpt-5.5": { + id: "openai/gpt-5.5", + name: "GPT-5.5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-12-01", + release_date: "2026-04-23", + last_updated: "2026-04-23", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 30, cache_read: 0.5, context_over_200k: { input: 10, output: 45, cache_read: 1 } }, + limit: { context: 1050000, input: 922000, output: 128000 }, + experimental: { + modes: { + fast: { + cost: { input: 12.5, output: 75, cache_read: 1.25 }, + provider: { body: { service_tier: "priority" } }, + }, + }, + }, + }, + "openai/gpt-5.5-pro": { + id: "openai/gpt-5.5-pro", + name: "GPT-5.5 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-12-01", + release_date: "2026-04-23", + last_updated: "2026-04-23", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 180, context_over_200k: { input: 60, output: 270 } }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "xiaomi/mimo-v2.5-pro": { + id: "xiaomi/mimo-v2.5-pro", + name: "MiMo-V2.5-Pro", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3, cache_read: 0.2, context_over_200k: { input: 2, output: 6, cache_read: 0.4 } }, + limit: { context: 1048576, output: 131072 }, + }, + "xiaomi/mimo-v2-omni": { + id: "xiaomi/mimo-v2-omni", + name: "MiMo V2 Omni", + family: "mimo", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2, cache_read: 0.08 }, + limit: { context: 265000, output: 265000 }, + }, + "xiaomi/mimo-v2.5": { + id: "xiaomi/mimo-v2.5", + name: "MiMo-V2.5", + family: "mimo", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: true, + cost: { + input: 0.4, + output: 2, + cache_read: 0.08, + context_over_200k: { input: 0.8, output: 4, cache_read: 0.16 }, + }, + limit: { context: 1048576, output: 131072 }, + }, + "xiaomi/mimo-v2-pro": { + id: "xiaomi/mimo-v2-pro", + name: "MiMo V2 Pro", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 3, cache_read: 0.2, context_over_200k: { input: 2, output: 6, cache_read: 0.4 } }, + limit: { context: 1000000, output: 256000 }, + }, + "xiaomi/mimo-v2-flash": { + id: "xiaomi/mimo-v2-flash", + name: "MiMo-V2-Flash", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-12-01", + release_date: "2025-12-16", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3, cache_read: 0.01 }, + limit: { context: 262144, output: 65536 }, + }, + }, + }, + upstage: { + id: "upstage", + env: ["UPSTAGE_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.upstage.ai/v1/solar", + name: "Upstage", + doc: "https://developers.upstage.ai/docs/apis/chat", + models: { + "solar-pro2": { + id: "solar-pro2", + name: "solar-pro2", + family: "solar-pro", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03", + release_date: "2025-05-20", + last_updated: "2025-05-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 0.25 }, + limit: { context: 65536, output: 8192 }, + }, + "solar-mini": { + id: "solar-mini", + name: "solar-mini", + family: "solar-mini", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-09", + release_date: "2024-06-12", + last_updated: "2025-04-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.15 }, + limit: { context: 32768, output: 4096 }, + }, + "solar-pro3": { + id: "solar-pro3", + name: "solar-pro3", + family: "solar-pro", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03", + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 0.25 }, + limit: { context: 131072, output: 8192 }, + }, + }, + }, + "novita-ai": { + id: "novita-ai", + env: ["NOVITA_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.novita.ai/openai", + name: "NovitaAI", + doc: "https://novita.ai/docs/guides/introduction", + models: { + "deepseek/deepseek-r1-turbo": { + id: "deepseek/deepseek-r1-turbo", + name: "DeepSeek R1 (Turbo)\t", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-03-05", + last_updated: "2025-03-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.7, output: 2.5 }, + limit: { context: 64000, output: 16000 }, + }, + "deepseek/deepseek-v3-0324": { + id: "deepseek/deepseek-v3-0324", + name: "DeepSeek V3 0324", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-03-25", + last_updated: "2025-03-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 1.12, cache_read: 0.135 }, + limit: { context: 163840, output: 163840 }, + }, + "deepseek/deepseek-ocr-2": { + id: "deepseek/deepseek-ocr-2", + name: "deepseek/deepseek-ocr-2", + attachment: true, + reasoning: false, + tool_call: false, + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.03 }, + limit: { context: 8192, output: 8192 }, + }, + "deepseek/deepseek-ocr": { + id: "deepseek/deepseek-ocr", + name: "DeepSeek-OCR", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-10-24", + last_updated: "2025-10-24", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.03 }, + limit: { context: 8192, output: 8192 }, + }, + "deepseek/deepseek-r1-distill-llama-70b": { + id: "deepseek/deepseek-r1-distill-llama-70b", + name: "DeepSeek R1 Distill LLama 70B", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-01-27", + last_updated: "2025-01-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.8, output: 0.8 }, + limit: { context: 8192, output: 8192 }, + }, + "deepseek/deepseek-prover-v2-671b": { + id: "deepseek/deepseek-prover-v2-671b", + name: "Deepseek Prover V2 671B", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-30", + last_updated: "2025-04-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.7, output: 2.5 }, + limit: { context: 160000, output: 160000 }, + }, + "deepseek/deepseek-r1-0528-qwen3-8b": { + id: "deepseek/deepseek-r1-0528-qwen3-8b", + name: "DeepSeek R1 0528 Qwen3 8B", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-05-29", + last_updated: "2025-05-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.06, output: 0.09 }, + limit: { context: 128000, output: 32000 }, + }, + "deepseek/deepseek-r1-distill-qwen-32b": { + id: "deepseek/deepseek-r1-distill-qwen-32b", + name: "DeepSeek R1 Distill Qwen 32B", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.3 }, + limit: { context: 64000, output: 32000 }, + }, + "deepseek/deepseek-v3.2-exp": { + id: "deepseek/deepseek-v3.2-exp", + name: "Deepseek V3.2 Exp", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 0.41 }, + limit: { context: 163840, output: 65536 }, + }, + "deepseek/deepseek-v3.1": { + id: "deepseek/deepseek-v3.1", + name: "DeepSeek V3.1", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-21", + last_updated: "2025-08-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 1, cache_read: 0.135 }, + limit: { context: 131072, output: 32768 }, + }, + "deepseek/deepseek-v3.2": { + id: "deepseek/deepseek-v3.2", + name: "Deepseek V3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.269, output: 0.4, cache_read: 0.1345 }, + limit: { context: 163840, output: 65536 }, + }, + "deepseek/deepseek-v3-turbo": { + id: "deepseek/deepseek-v3-turbo", + name: "DeepSeek V3 (Turbo)\t", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-03-05", + last_updated: "2025-03-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 1.3 }, + limit: { context: 64000, output: 16000 }, + }, + "deepseek/deepseek-r1-distill-qwen-14b": { + id: "deepseek/deepseek-r1-distill-qwen-14b", + name: "DeepSeek R1 Distill Qwen 14B", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.15 }, + limit: { context: 32768, output: 16384 }, + }, + "deepseek/deepseek-r1-0528": { + id: "deepseek/deepseek-r1-0528", + name: "DeepSeek R1 0528", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-05-28", + last_updated: "2025-05-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.7, output: 2.5, cache_read: 0.35 }, + limit: { context: 163840, output: 32768 }, + }, + "deepseek/deepseek-v3.1-terminus": { + id: "deepseek/deepseek-v3.1-terminus", + name: "Deepseek V3.1 Terminus", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-22", + last_updated: "2025-09-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 1, cache_read: 0.135 }, + limit: { context: 131072, output: 32768 }, + }, + "inclusionai/ling-2.6-1t": { + id: "inclusionai/ling-2.6-1t", + name: "Ling-2.6-1T", + family: "ling", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-23", + last_updated: "2026-04-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 32768 }, + }, + "paddlepaddle/paddleocr-vl": { + id: "paddlepaddle/paddleocr-vl", + name: "PaddleOCR-VL", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-10-22", + last_updated: "2025-10-22", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0.02 }, + limit: { context: 16384, output: 16384 }, + }, + "nousresearch/hermes-2-pro-llama-3-8b": { + id: "nousresearch/hermes-2-pro-llama-3-8b", + name: "Hermes 2 Pro Llama 3 8B", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2024-06-27", + last_updated: "2024-06-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.14 }, + limit: { context: 8192, output: 8192 }, + }, + "zai-org/glm-4.7": { + id: "zai-org/glm-4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2, cache_read: 0.11 }, + limit: { context: 204800, output: 131072 }, + }, + "zai-org/glm-5": { + id: "zai-org/glm-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3.2, cache_read: 0.2 }, + limit: { context: 202800, output: 131072 }, + }, + "zai-org/glm-5.1": { + id: "zai-org/glm-5.1", + name: "GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-03-27", + last_updated: "2026-03-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.4, output: 4.4, cache_read: 0.26 }, + limit: { context: 204800, output: 131072 }, + }, + "zai-org/glm-4.5": { + id: "zai-org/glm-4.5", + name: "GLM-4.5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2, cache_read: 0.11 }, + limit: { context: 131072, output: 98304 }, + }, + "zai-org/glm-4.5-air": { + id: "zai-org/glm-4.5-air", + name: "GLM 4.5 Air", + family: "glm-air", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-10-13", + last_updated: "2025-10-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.13, output: 0.85 }, + limit: { context: 131072, output: 98304 }, + }, + "zai-org/glm-4.5v": { + id: "zai-org/glm-4.5v", + name: "GLM 4.5V", + family: "glmv", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-08-11", + last_updated: "2025-08-11", + modalities: { input: ["text", "video", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 1.8, cache_read: 0.11 }, + limit: { context: 65536, output: 16384 }, + }, + "zai-org/glm-4.6": { + id: "zai-org/glm-4.6", + name: "GLM 4.6", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 2.2, cache_read: 0.11 }, + limit: { context: 204800, output: 131072 }, + }, + "zai-org/glm-4.6v": { + id: "zai-org/glm-4.6v", + name: "GLM 4.6V", + family: "glmv", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-08", + last_updated: "2025-12-08", + modalities: { input: ["text", "video", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.9, cache_read: 0.055 }, + limit: { context: 131072, output: 32768 }, + }, + "zai-org/glm-4.7-flash": { + id: "zai-org/glm-4.7-flash", + name: "GLM-4.7-Flash", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-01-19", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.4, cache_read: 0.01 }, + limit: { context: 200000, output: 128000 }, + }, + "zai-org/autoglm-phone-9b-multilingual": { + id: "zai-org/autoglm-phone-9b-multilingual", + name: "AutoGLM-Phone-9B-Multilingual", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-12-10", + last_updated: "2025-12-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.035, output: 0.138 }, + limit: { context: 65536, output: 65536 }, + }, + "mistralai/mistral-nemo": { + id: "mistralai/mistral-nemo", + name: "Mistral Nemo", + family: "mistral-nemo", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2024-07-30", + last_updated: "2024-07-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.04, output: 0.17 }, + limit: { context: 60288, output: 16000 }, + }, + "baichuan/baichuan-m2-32b": { + id: "baichuan/baichuan-m2-32b", + name: "baichuan-m2-32b", + family: "baichuan", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + knowledge: "2024-12", + release_date: "2025-08-13", + last_updated: "2025-08-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.07 }, + limit: { context: 131072, output: 131072 }, + }, + "meta-llama/llama-4-scout-17b-16e-instruct": { + id: "meta-llama/llama-4-scout-17b-16e-instruct", + name: "Llama 4 Scout Instruct", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-06", + last_updated: "2025-04-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.18, output: 0.59 }, + limit: { context: 131072, output: 131072 }, + }, + "meta-llama/llama-3.3-70b-instruct": { + id: "meta-llama/llama-3.3-70b-instruct", + name: "Llama 3.3 70B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-12-07", + last_updated: "2024-12-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.135, output: 0.4 }, + limit: { context: 131072, output: 120000 }, + }, + "meta-llama/llama-3.2-3b-instruct": { + id: "meta-llama/llama-3.2-3b-instruct", + name: "Llama 3.2 3B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2024-09-18", + last_updated: "2024-09-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.05 }, + limit: { context: 32768, output: 32000 }, + }, + "meta-llama/llama-3-8b-instruct": { + id: "meta-llama/llama-3-8b-instruct", + name: "Llama 3 8B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-04-25", + last_updated: "2024-04-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.04, output: 0.04 }, + limit: { context: 8192, output: 8192 }, + }, + "meta-llama/llama-3.1-8b-instruct": { + id: "meta-llama/llama-3.1-8b-instruct", + name: "Llama 3.1 8B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-07-24", + last_updated: "2024-07-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0.05 }, + limit: { context: 16384, output: 16384 }, + }, + "meta-llama/llama-3-70b-instruct": { + id: "meta-llama/llama-3-70b-instruct", + name: "Llama3 70B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2024-04-25", + last_updated: "2024-04-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.51, output: 0.74 }, + limit: { context: 8192, output: 8000 }, + }, + "meta-llama/llama-4-maverick-17b-128e-instruct-fp8": { + id: "meta-llama/llama-4-maverick-17b-128e-instruct-fp8", + name: "Llama 4 Maverick Instruct", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-06", + last_updated: "2025-04-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 0.85 }, + limit: { context: 1048576, output: 8192 }, + }, + "gryphe/mythomax-l2-13b": { + id: "gryphe/mythomax-l2-13b", + name: "Mythomax L2 13B", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-04-25", + last_updated: "2024-04-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.09, output: 0.09 }, + limit: { context: 4096, output: 3200 }, + }, + "sao10k/l31-70b-euryale-v2.2": { + id: "sao10k/l31-70b-euryale-v2.2", + name: "L31 70B Euryale V2.2", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-09-19", + last_updated: "2024-09-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.48, output: 1.48 }, + limit: { context: 8192, output: 8192 }, + }, + "sao10k/l3-70b-euryale-v2.1": { + id: "sao10k/l3-70b-euryale-v2.1", + name: "L3 70B Euryale V2.1\t", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-06-18", + last_updated: "2024-06-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.48, output: 1.48 }, + limit: { context: 8192, output: 8192 }, + }, + "sao10k/L3-8B-Stheno-v3.2": { + id: "sao10k/L3-8B-Stheno-v3.2", + name: "L3 8B Stheno V3.2", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-11-29", + last_updated: "2024-11-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.05 }, + limit: { context: 8192, output: 32000 }, + }, + "sao10k/l3-8b-lunaris": { + id: "sao10k/l3-8b-lunaris", + name: "Sao10k L3 8B Lunaris\t", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2024-11-28", + last_updated: "2024-11-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.05 }, + limit: { context: 8192, output: 8192 }, + }, + "microsoft/wizardlm-2-8x22b": { + id: "microsoft/wizardlm-2-8x22b", + name: "Wizardlm 2 8x22B", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-04-24", + last_updated: "2024-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.62, output: 0.62 }, + limit: { context: 65535, output: 8000 }, + }, + "openai/gpt-oss-20b": { + id: "openai/gpt-oss-20b", + name: "OpenAI: GPT OSS 20B", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-08-06", + last_updated: "2025-08-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.04, output: 0.15 }, + limit: { context: 131072, output: 32768 }, + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "OpenAI GPT OSS 120B", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-06", + last_updated: "2025-08-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.25 }, + limit: { context: 131072, output: 32768 }, + }, + "minimaxai/minimax-m1-80k": { + id: "minimaxai/minimax-m1-80k", + name: "MiniMax M1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 2.2 }, + limit: { context: 1000000, output: 40000 }, + }, + "xiaomimimo/mimo-v2-flash": { + id: "xiaomimimo/mimo-v2-flash", + name: "XiaomiMiMo/MiMo-V2-Flash", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-12-19", + last_updated: "2025-12-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3, cache_read: 0.3 }, + limit: { context: 262144, output: 32000 }, + }, + "baidu/ernie-4.5-vl-28b-a3b-thinking": { + id: "baidu/ernie-4.5-vl-28b-a3b-thinking", + name: "ERNIE-4.5-VL-28B-A3B-Thinking", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-11-26", + last_updated: "2025-11-26", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.39, output: 0.39 }, + limit: { context: 131072, output: 65536 }, + }, + "baidu/ernie-4.5-vl-424b-a47b": { + id: "baidu/ernie-4.5-vl-424b-a47b", + name: "ERNIE 4.5 VL 424B A47B", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-06-30", + last_updated: "2025-06-30", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.42, output: 1.25 }, + limit: { context: 123000, output: 16000 }, + }, + "baidu/ernie-4.5-21B-a3b": { + id: "baidu/ernie-4.5-21B-a3b", + name: "ERNIE 4.5 21B A3B", + family: "ernie", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-03", + release_date: "2025-06-30", + last_updated: "2025-06-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.28 }, + limit: { context: 120000, output: 8000 }, + }, + "baidu/ernie-4.5-300b-a47b-paddle": { + id: "baidu/ernie-4.5-300b-a47b-paddle", + name: "ERNIE 4.5 300B A47B", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-06-30", + last_updated: "2025-06-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.28, output: 1.1 }, + limit: { context: 123000, output: 12000 }, + }, + "baidu/ernie-4.5-21B-a3b-thinking": { + id: "baidu/ernie-4.5-21B-a3b-thinking", + name: "ERNIE-4.5-21B-A3B-Thinking", + family: "ernie", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2025-03", + release_date: "2025-09-19", + last_updated: "2025-09-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.28 }, + limit: { context: 131072, output: 65536 }, + }, + "baidu/ernie-4.5-vl-28b-a3b": { + id: "baidu/ernie-4.5-vl-28b-a3b", + name: "ERNIE 4.5 VL 28B A3B", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-06-30", + last_updated: "2025-06-30", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 1.4, output: 5.6 }, + limit: { context: 30000, output: 8000 }, + }, + "minimax/minimax-m2.7": { + id: "minimax/minimax-m2.7", + name: "MiniMax M2.7", + family: "minimax-m2.7", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.06 }, + limit: { context: 204800, output: 131072 }, + }, + "minimax/minimax-m2": { + id: "minimax/minimax-m2", + name: "MiniMax-M2", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2025-10-27", + last_updated: "2025-10-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.03 }, + limit: { context: 204800, output: 131072 }, + }, + "minimax/minimax-m2.1": { + id: "minimax/minimax-m2.1", + name: "Minimax M2.1", + family: "minimax", + attachment: false, + reasoning: false, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.03 }, + limit: { context: 204800, output: 131072 }, + }, + "minimax/minimax-m2.5": { + id: "minimax/minimax-m2.5", + name: "MiniMax M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2, cache_read: 0.03 }, + limit: { context: 204800, output: 131100 }, + }, + "minimax/minimax-m2.5-highspeed": { + id: "minimax/minimax-m2.5-highspeed", + name: "MiniMax M2.5 Highspeed", + family: "minimax-m2.5", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 2.4, cache_read: 0.03 }, + limit: { context: 204800, output: 131100 }, + }, + "qwen/qwen2.5-7b-instruct": { + id: "qwen/qwen2.5-7b-instruct", + name: "Qwen2.5 7B Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.07 }, + limit: { context: 32000, output: 32000 }, + }, + "qwen/qwen3.5-122b-a10b": { + id: "qwen/qwen3.5-122b-a10b", + name: "Qwen3.5-122B-A10B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-26", + last_updated: "2026-02-26", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 3.2 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen/qwen3.6-27b": { + id: "qwen/qwen3.6-27b", + name: "Qwen3.6-27B", + family: "qwen3.6", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 3.6 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen/qwen3.5-27b": { + id: "qwen/qwen3.5-27b", + name: "Qwen3.5-27B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-26", + last_updated: "2026-02-26", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 2.4 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen/qwen3-235b-a22b-instruct-2507": { + id: "qwen/qwen3-235b-a22b-instruct-2507", + name: "Qwen3 235B A22B Instruct 2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-22", + last_updated: "2025-07-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.09, output: 0.58 }, + limit: { context: 131072, output: 16384 }, + }, + "qwen/qwen3-omni-30b-a3b-instruct": { + id: "qwen/qwen3-omni-30b-a3b-instruct", + name: "Qwen3 Omni 30B A3B Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-09-24", + last_updated: "2025-09-24", + modalities: { input: ["text", "video", "audio", "image"], output: ["text", "audio"] }, + open_weights: true, + cost: { input: 0.25, output: 0.97, input_audio: 2.2, output_audio: 1.788 }, + limit: { context: 65536, output: 16384 }, + }, + "qwen/qwen3.5-397b-a17b": { + id: "qwen/qwen3.5-397b-a17b", + name: "Qwen3.5-397B-A17B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-17", + last_updated: "2026-02-17", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3.6 }, + limit: { context: 262144, output: 64000 }, + }, + "qwen/qwen2.5-vl-72b-instruct": { + id: "qwen/qwen2.5-vl-72b-instruct", + name: "Qwen2.5 VL 72B Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-03-25", + last_updated: "2025-03-25", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.8, output: 0.8 }, + limit: { context: 32768, output: 32768 }, + }, + "qwen/qwen3-vl-235b-a22b-thinking": { + id: "qwen/qwen3-vl-235b-a22b-thinking", + name: "Qwen3 VL 235B A22B Thinking", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-09-24", + last_updated: "2025-09-24", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.98, output: 3.95 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen/qwen3-vl-30b-a3b-thinking": { + id: "qwen/qwen3-vl-30b-a3b-thinking", + name: "qwen/qwen3-vl-30b-a3b-thinking", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-11", + last_updated: "2025-10-11", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 1 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen/qwen3-omni-30b-a3b-thinking": { + id: "qwen/qwen3-omni-30b-a3b-thinking", + name: "Qwen3 Omni 30B A3B Thinking", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-24", + last_updated: "2025-09-24", + modalities: { input: ["text", "audio", "video", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 0.97, input_audio: 2.2, output_audio: 1.788 }, + limit: { context: 65536, output: 16384 }, + }, + "qwen/qwen3-vl-8b-instruct": { + id: "qwen/qwen3-vl-8b-instruct", + name: "qwen/qwen3-vl-8b-instruct", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-17", + last_updated: "2025-10-17", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.08, output: 0.5 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen/qwen3-max": { + id: "qwen/qwen3-max", + name: "Qwen3 Max", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-24", + last_updated: "2025-09-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.11, output: 8.45 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen/qwen3-32b-fp8": { + id: "qwen/qwen3-32b-fp8", + name: "Qwen3 32B", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-04-29", + last_updated: "2025-04-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.45 }, + limit: { context: 40960, output: 20000 }, + }, + "qwen/qwen3-4b-fp8": { + id: "qwen/qwen3-4b-fp8", + name: "Qwen3 4B", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-04-29", + last_updated: "2025-04-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.03 }, + limit: { context: 128000, output: 20000 }, + }, + "qwen/qwen3-235b-a22b-thinking-2507": { + id: "qwen/qwen3-235b-a22b-thinking-2507", + name: "Qwen3 235B A22b Thinking 2507", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-25", + last_updated: "2025-07-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 3 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen/qwen3-next-80b-a3b-thinking": { + id: "qwen/qwen3-next-80b-a3b-thinking", + name: "Qwen3 Next 80B A3B Thinking", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-10", + last_updated: "2025-09-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 1.5 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen/qwen-mt-plus": { + id: "qwen/qwen-mt-plus", + name: "Qwen MT Plus", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-09-03", + last_updated: "2025-09-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 0.75 }, + limit: { context: 16384, output: 8192 }, + }, + "qwen/qwen3-next-80b-a3b-instruct": { + id: "qwen/qwen3-next-80b-a3b-instruct", + name: "Qwen3 Next 80B A3B Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-10", + last_updated: "2025-09-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 1.5 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen/qwen3-30b-a3b-fp8": { + id: "qwen/qwen3-30b-a3b-fp8", + name: "Qwen3 30B A3B", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-04-29", + last_updated: "2025-04-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.09, output: 0.45 }, + limit: { context: 40960, output: 20000 }, + }, + "qwen/qwen3-coder-next": { + id: "qwen/qwen3-coder-next", + name: "Qwen3 Coder Next", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-03", + last_updated: "2026-02-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 1.5 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen/qwen3-coder-480b-a35b-instruct": { + id: "qwen/qwen3-coder-480b-a35b-instruct", + name: "Qwen3 Coder 480B A35B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.3 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen/qwen3-vl-30b-a3b-instruct": { + id: "qwen/qwen3-vl-30b-a3b-instruct", + name: "qwen/qwen3-vl-30b-a3b-instruct", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-11", + last_updated: "2025-10-11", + modalities: { input: ["text", "video", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.7 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen/qwen3-coder-30b-a3b-instruct": { + id: "qwen/qwen3-coder-30b-a3b-instruct", + name: "Qwen3 Coder 30b A3B Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-09", + last_updated: "2025-10-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.27 }, + limit: { context: 160000, output: 32768 }, + }, + "qwen/qwen3-235b-a22b-fp8": { + id: "qwen/qwen3-235b-a22b-fp8", + name: "Qwen3 235B A22B", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-04-29", + last_updated: "2025-04-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.8 }, + limit: { context: 40960, output: 20000 }, + }, + "qwen/qwen3-8b-fp8": { + id: "qwen/qwen3-8b-fp8", + name: "Qwen3 8B", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-04-29", + last_updated: "2025-04-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.035, output: 0.138 }, + limit: { context: 128000, output: 20000 }, + }, + "qwen/qwen3-vl-235b-a22b-instruct": { + id: "qwen/qwen3-vl-235b-a22b-instruct", + name: "Qwen3 VL 235B A22B Instruct", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-24", + last_updated: "2025-09-24", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.5 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen/qwen-2.5-72b-instruct": { + id: "qwen/qwen-2.5-72b-instruct", + name: "Qwen 2.5 72B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-10-15", + last_updated: "2024-10-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.38, output: 0.4 }, + limit: { context: 32000, output: 8192 }, + }, + "qwen/qwen3.5-35b-a3b": { + id: "qwen/qwen3.5-35b-a3b", + name: "Qwen3.5-35B-A3B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-26", + last_updated: "2026-02-26", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 2 }, + limit: { context: 262144, output: 65536 }, + }, + "kwaipilot/kat-coder-pro": { + id: "kwaipilot/kat-coder-pro", + name: "Kat Coder Pro", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01-05", + last_updated: "2026-01-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.06 }, + limit: { context: 256000, output: 128000 }, + }, + "google/gemma-4-31b-it": { + id: "google/gemma-4-31b-it", + name: "Gemma 4 31B", + family: "gemma", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.4 }, + limit: { context: 262144, output: 131072 }, + }, + "google/gemma-3-12b-it": { + id: "google/gemma-3-12b-it", + name: "Gemma 3 12B", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2025-03-13", + last_updated: "2025-03-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.1 }, + limit: { context: 131072, output: 8192 }, + }, + "google/gemma-3-27b-it": { + id: "google/gemma-3-27b-it", + name: "Gemma 3 27B", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-03-25", + last_updated: "2025-03-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.119, output: 0.2 }, + limit: { context: 98304, output: 16384 }, + }, + "google/gemma-4-26b-a4b-it": { + id: "google/gemma-4-26b-a4b-it", + name: "Gemma 4 26B A4B", + family: "gemma", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.13, output: 0.4 }, + limit: { context: 262144, output: 131072 }, + }, + "moonshotai/kimi-k2.5": { + id: "moonshotai/kimi-k2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3, cache_read: 0.1 }, + limit: { context: 262144, output: 262144 }, + }, + "moonshotai/kimi-k2-instruct": { + id: "moonshotai/kimi-k2-instruct", + name: "Kimi K2 Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-11", + last_updated: "2025-07-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.57, output: 2.3 }, + limit: { context: 131072, output: 131072 }, + }, + "moonshotai/kimi-k2-0905": { + id: "moonshotai/kimi-k2-0905", + name: "Kimi K2 0905", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5 }, + limit: { context: 262144, output: 262144 }, + }, + "moonshotai/kimi-k2.6": { + id: "moonshotai/kimi-k2.6", + name: "Kimi K2.6", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4, cache_read: 0.16 }, + limit: { context: 262144, output: 262144 }, + }, + "moonshotai/kimi-k2-thinking": { + id: "moonshotai/kimi-k2-thinking", + name: "Kimi K2 Thinking", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-11-07", + last_updated: "2025-11-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5 }, + limit: { context: 262144, output: 262144 }, + }, + "deepseek/deepseek-v4-flash": { + id: "deepseek/deepseek-v4-flash", + name: "DeepSeek V4 Flash", + family: "deepseek-flash", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.28, cache_read: 0.028 }, + limit: { context: 1048576, output: 393216 }, + }, + "deepseek/deepseek-v4-pro": { + id: "deepseek/deepseek-v4-pro", + name: "DeepSeek V4 Pro", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.69, output: 3.38, cache_read: 0.13 }, + limit: { context: 1048576, output: 393216 }, + }, + }, + }, + "xiaomi-token-plan-cn": { + id: "xiaomi-token-plan-cn", + env: ["XIAOMI_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://token-plan-cn.xiaomimimo.com/v1", + name: "Xiaomi Token Plan (China)", + doc: "https://platform.xiaomimimo.com/#/docs", + models: { + "mimo-v2-tts": { + id: "mimo-v2-tts", + name: "MiMo-V2-TTS", + family: "mimo", + attachment: false, + reasoning: false, + tool_call: false, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["audio"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 8192, output: 16384 }, + }, + "mimo-v2.5-pro": { + id: "mimo-v2.5-pro", + name: "MiMo-V2.5-Pro", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, context_over_200k: { input: 0, output: 0, cache_read: 0 } }, + limit: { context: 1048576, output: 131072 }, + }, + "mimo-v2-omni": { + id: "mimo-v2-omni", + name: "MiMo-V2-Omni", + family: "mimo", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0 }, + limit: { context: 262144, output: 131072 }, + }, + "mimo-v2.5": { + id: "mimo-v2.5", + name: "MiMo-V2.5", + family: "mimo", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, context_over_200k: { input: 0, output: 0, cache_read: 0 } }, + limit: { context: 1048576, output: 131072 }, + }, + "mimo-v2-pro": { + id: "mimo-v2-pro", + name: "MiMo-V2-Pro", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, context_over_200k: { input: 2, output: 6, cache_read: 0.4 } }, + limit: { context: 1048576, output: 131072 }, + }, + "mimo-v2-flash": { + id: "mimo-v2-flash", + name: "MiMo-V2-Flash", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12-01", + release_date: "2025-12-16", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0 }, + limit: { context: 262144, output: 65536 }, + }, + }, + }, + wandb: { + id: "wandb", + env: ["WANDB_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.inference.wandb.ai/v1", + name: "Weights & Biases", + doc: "https://docs.wandb.ai/guides/integrations/inference/", + models: { + "Qwen/Qwen3-30B-A3B-Instruct-2507": { + id: "Qwen/Qwen3-30B-A3B-Instruct-2507", + name: "Qwen3 30B A3B Instruct 2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-29", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 262144, output: 262144 }, + }, + "Qwen/Qwen3-235B-A22B-Instruct-2507": { + id: "Qwen/Qwen3-235B-A22B-Instruct-2507", + name: "Qwen3 235B A22B Instruct 2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04-28", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 262144, output: 262144 }, + }, + "Qwen/Qwen3-235B-A22B-Thinking-2507": { + id: "Qwen/Qwen3-235B-A22B-Thinking-2507", + name: "Qwen3-235B-A22B-Thinking-2507", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-25", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 262144, output: 262144 }, + }, + "Qwen/Qwen3-Coder-480B-A35B-Instruct": { + id: "Qwen/Qwen3-Coder-480B-A35B-Instruct", + name: "Qwen3-Coder-480B-A35B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 1.5 }, + limit: { context: 262144, output: 262144 }, + }, + "zai-org/GLM-5-FP8": { + id: "zai-org/GLM-5-FP8", + name: "GLM 5", + family: "glm", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3.2 }, + limit: { context: 200000, output: 200000 }, + }, + "meta-llama/Llama-4-Scout-17B-16E-Instruct": { + id: "meta-llama/Llama-4-Scout-17B-16E-Instruct", + name: "Llama 4 Scout 17B 16E Instruct", + family: "llama", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-01-31", + last_updated: "2026-03-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.17, output: 0.66 }, + limit: { context: 64000, output: 64000 }, + }, + "meta-llama/Llama-3.3-70B-Instruct": { + id: "meta-llama/Llama-3.3-70B-Instruct", + name: "Llama-3.3-70B-Instruct", + family: "llama", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-12-06", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.71, output: 0.71 }, + limit: { context: 128000, output: 128000 }, + }, + "meta-llama/Llama-3.1-8B-Instruct": { + id: "meta-llama/Llama-3.1-8B-Instruct", + name: "Meta-Llama-3.1-8B-Instruct", + family: "llama", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-07-23", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.22, output: 0.22 }, + limit: { context: 128000, output: 128000 }, + }, + "meta-llama/Llama-3.1-70B-Instruct": { + id: "meta-llama/Llama-3.1-70B-Instruct", + name: "Llama 3.1 70B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-07-23", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.8, output: 0.8 }, + limit: { context: 128000, output: 128000 }, + }, + "OpenPipe/Qwen3-14B-Instruct": { + id: "OpenPipe/Qwen3-14B-Instruct", + name: "OpenPipe Qwen3 14B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-29", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.22 }, + limit: { context: 32768, output: 32768 }, + }, + "microsoft/Phi-4-mini-instruct": { + id: "microsoft/Phi-4-mini-instruct", + name: "Phi-4-mini-instruct", + family: "phi", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-12-11", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.08, output: 0.35 }, + limit: { context: 128000, output: 128000 }, + }, + "nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-FP8": { + id: "nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-FP8", + name: "NVIDIA Nemotron 3 Super 120B", + family: "nemotron", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-03-11", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.8 }, + limit: { context: 262144, output: 262144 }, + }, + "deepseek-ai/DeepSeek-V3.1": { + id: "deepseek-ai/DeepSeek-V3.1", + name: "DeepSeek V3.1", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-21", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 1.65 }, + limit: { context: 161000, output: 161000 }, + }, + "openai/gpt-oss-20b": { + id: "openai/gpt-oss-20b", + name: "gpt-oss-20b", + family: "gpt-oss", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.2 }, + limit: { context: 131072, output: 131072 }, + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "gpt-oss-120b", + family: "gpt-oss", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 131072, output: 131072 }, + }, + "moonshotai/Kimi-K2.5": { + id: "moonshotai/Kimi-K2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-01-27", + last_updated: "2026-03-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 2.85 }, + limit: { context: 262144, output: 262144 }, + }, + "MiniMaxAI/MiniMax-M2.5": { + id: "MiniMaxAI/MiniMax-M2.5", + name: "MiniMax M2.5", + family: "minimax", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 196608, output: 196608 }, + }, + "zai-org/GLM-5.1": { + id: "zai-org/GLM-5.1", + name: "GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-03-27", + last_updated: "2026-03-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.4, output: 4.4, cache_read: 0.26, cache_write: 0 }, + limit: { context: 200000, output: 131072 }, + }, + }, + }, + chutes: { + id: "chutes", + env: ["CHUTES_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://llm.chutes.ai/v1", + name: "Chutes", + doc: "https://llm.chutes.ai/v1/models", + models: { + "miromind-ai/MiroThinker-v1.5-235B": { + id: "miromind-ai/MiroThinker-v1.5-235B", + name: "MiroThinker V1.5 235B", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2026-01-10", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.15 }, + limit: { context: 262144, output: 8192 }, + }, + "OpenGVLab/InternVL3-78B-TEE": { + id: "OpenGVLab/InternVL3-78B-TEE", + name: "InternVL3 78B TEE", + family: "opengvlab", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-01-06", + last_updated: "2026-01-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.39 }, + limit: { context: 32768, output: 32768 }, + }, + "NousResearch/DeepHermes-3-Mistral-24B-Preview": { + id: "NousResearch/DeepHermes-3-Mistral-24B-Preview", + name: "DeepHermes 3 Mistral 24B Preview", + family: "nousresearch", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0.1 }, + limit: { context: 32768, output: 32768 }, + }, + "NousResearch/Hermes-4-405B-FP8-TEE": { + id: "NousResearch/Hermes-4-405B-FP8-TEE", + name: "Hermes 4 405B FP8 TEE", + family: "nousresearch", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 131072, output: 65536 }, + }, + "NousResearch/Hermes-4.3-36B": { + id: "NousResearch/Hermes-4.3-36B", + name: "Hermes 4.3 36B", + family: "nousresearch", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.39 }, + limit: { context: 32768, output: 8192 }, + }, + "NousResearch/Hermes-4-14B": { + id: "NousResearch/Hermes-4-14B", + name: "Hermes 4 14B", + family: "nousresearch", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.01, output: 0.05 }, + limit: { context: 40960, output: 40960 }, + }, + "NousResearch/Hermes-4-70B": { + id: "NousResearch/Hermes-4-70B", + name: "Hermes 4 70B", + family: "nousresearch", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.11, output: 0.38 }, + limit: { context: 131072, output: 131072 }, + }, + "Qwen/Qwen3-30B-A3B": { + id: "Qwen/Qwen3-30B-A3B", + name: "Qwen3 30B A3B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.06, output: 0.22 }, + limit: { context: 40960, output: 40960 }, + }, + "Qwen/Qwen3-Coder-Next": { + id: "Qwen/Qwen3-Coder-Next", + name: "Qwen3 Coder Next", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.3 }, + limit: { context: 262144, output: 65536 }, + }, + "Qwen/Qwen2.5-Coder-32B-Instruct": { + id: "Qwen/Qwen2.5-Coder-32B-Instruct", + name: "Qwen2.5 Coder 32B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.11 }, + limit: { context: 32768, output: 32768 }, + }, + "Qwen/Qwen3-235B-A22B": { + id: "Qwen/Qwen3-235B-A22B", + name: "Qwen3 235B A22B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 40960, output: 40960 }, + }, + "Qwen/Qwen2.5-VL-72B-Instruct-TEE": { + id: "Qwen/Qwen2.5-VL-72B-Instruct-TEE", + name: "Qwen2.5 VL 72B Instruct TEE", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 32768, output: 32768 }, + }, + "Qwen/Qwen3Guard-Gen-0.6B": { + id: "Qwen/Qwen3Guard-Gen-0.6B", + name: "Qwen3Guard Gen 0.6B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.01, output: 0.01, cache_read: 0.005 }, + limit: { context: 32768, output: 8192 }, + }, + "Qwen/Qwen3-Next-80B-A3B-Instruct": { + id: "Qwen/Qwen3-Next-80B-A3B-Instruct", + name: "Qwen3 Next 80B A3B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.8 }, + limit: { context: 262144, output: 262144 }, + }, + "Qwen/Qwen3-30B-A3B-Instruct-2507": { + id: "Qwen/Qwen3-30B-A3B-Instruct-2507", + name: "Qwen3 30B A3B Instruct 2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.08, output: 0.33 }, + limit: { context: 262144, output: 262144 }, + }, + "Qwen/Qwen3-32B": { + id: "Qwen/Qwen3-32B", + name: "Qwen3 32B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.08, output: 0.24, cache_read: 0.04 }, + limit: { context: 40960, output: 40960 }, + }, + "Qwen/Qwen3-14B": { + id: "Qwen/Qwen3-14B", + name: "Qwen3 14B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.22 }, + limit: { context: 40960, output: 40960 }, + }, + "Qwen/Qwen3-235B-A22B-Instruct-2507-TEE": { + id: "Qwen/Qwen3-235B-A22B-Instruct-2507-TEE", + name: "Qwen3 235B A22B Instruct 2507 TEE", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.08, output: 0.55, cache_read: 0.04 }, + limit: { context: 262144, output: 65536 }, + }, + "Qwen/Qwen3-235B-A22B-Thinking-2507": { + id: "Qwen/Qwen3-235B-A22B-Thinking-2507", + name: "Qwen3 235B A22B Thinking 2507", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.11, output: 0.6 }, + limit: { context: 262144, output: 262144 }, + }, + "Qwen/Qwen2.5-VL-32B-Instruct": { + id: "Qwen/Qwen2.5-VL-32B-Instruct", + name: "Qwen2.5 VL 32B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.22 }, + limit: { context: 16384, output: 16384 }, + }, + "Qwen/Qwen3.5-397B-A17B-TEE": { + id: "Qwen/Qwen3.5-397B-A17B-TEE", + name: "Qwen3.5 397B A17B TEE", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-02-18", + last_updated: "2026-02-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.39, output: 2.34, cache_read: 0.195 }, + limit: { context: 262144, output: 65536 }, + }, + "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8-TEE": { + id: "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8-TEE", + name: "Qwen3 Coder 480B A35B Instruct FP8 TEE", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.22, output: 0.95, cache_read: 0.11 }, + limit: { context: 262144, output: 262144 }, + }, + "Qwen/Qwen3-VL-235B-A22B-Instruct": { + id: "Qwen/Qwen3-VL-235B-A22B-Instruct", + name: "Qwen3 VL 235B A22B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 262144, output: 262144 }, + }, + "Qwen/Qwen2.5-72B-Instruct": { + id: "Qwen/Qwen2.5-72B-Instruct", + name: "Qwen2.5 72B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.13, output: 0.52 }, + limit: { context: 32768, output: 32768 }, + }, + "zai-org/GLM-5.1-TEE": { + id: "zai-org/GLM-5.1-TEE", + name: "GLM 5.1 TEE", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-04-08", + last_updated: "2026-04-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 3.15, cache_read: 0.475 }, + limit: { context: 202752, output: 65535 }, + }, + "zai-org/GLM-4.7-Flash": { + id: "zai-org/GLM-4.7-Flash", + name: "GLM 4.7 Flash", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.06, output: 0.35 }, + limit: { context: 202752, output: 65535 }, + }, + "zai-org/GLM-4.5-TEE": { + id: "zai-org/GLM-4.5-TEE", + name: "GLM 4.5 TEE", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.35, output: 1.55 }, + limit: { context: 131072, output: 65536 }, + }, + "zai-org/GLM-4.6-FP8": { + id: "zai-org/GLM-4.6-FP8", + name: "GLM 4.6 FP8", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 202752, output: 65535 }, + }, + "zai-org/GLM-4.5-Air": { + id: "zai-org/GLM-4.5-Air", + name: "GLM 4.5 Air", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.22 }, + limit: { context: 131072, output: 131072 }, + }, + "zai-org/GLM-4.7-FP8": { + id: "zai-org/GLM-4.7-FP8", + name: "GLM 4.7 FP8", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 202752, output: 65535 }, + }, + "zai-org/GLM-5-TEE": { + id: "zai-org/GLM-5-TEE", + name: "GLM 5 TEE", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-02-14", + last_updated: "2026-02-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 3.15, cache_read: 0.475 }, + limit: { context: 202752, output: 65535 }, + }, + "zai-org/GLM-4.5-FP8": { + id: "zai-org/GLM-4.5-FP8", + name: "GLM 4.5 FP8", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 131072, output: 65536 }, + }, + "zai-org/GLM-4.7-TEE": { + id: "zai-org/GLM-4.7-TEE", + name: "GLM 4.7 TEE", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 1.5 }, + limit: { context: 202752, output: 65535 }, + }, + "zai-org/GLM-4.6V": { + id: "zai-org/GLM-4.6V", + name: "GLM 4.6V", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.9, cache_read: 0.15 }, + limit: { context: 131072, output: 65536 }, + }, + "zai-org/GLM-5-Turbo": { + id: "zai-org/GLM-5-Turbo", + name: "GLM 5 Turbo", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-03-11", + last_updated: "2026-03-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.49, output: 1.96, cache_read: 0.245 }, + limit: { context: 202752, output: 65535 }, + }, + "zai-org/GLM-4.6-TEE": { + id: "zai-org/GLM-4.6-TEE", + name: "GLM 4.6 TEE", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 1.7, cache_read: 0.2 }, + limit: { context: 202752, output: 65536 }, + }, + "mistralai/Devstral-2-123B-Instruct-2512-TEE": { + id: "mistralai/Devstral-2-123B-Instruct-2512-TEE", + name: "Devstral 2 123B Instruct 2512 TEE", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01-10", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.22 }, + limit: { context: 262144, output: 65536 }, + }, + "XiaomiMiMo/MiMo-V2-Flash": { + id: "XiaomiMiMo/MiMo-V2-Flash", + name: "MiMo V2 Flash", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.09, output: 0.29 }, + limit: { context: 262144, output: 32000 }, + }, + "chutesai/Mistral-Small-3.2-24B-Instruct-2506": { + id: "chutesai/Mistral-Small-3.2-24B-Instruct-2506", + name: "Mistral Small 3.2 24B Instruct 2506", + family: "chutesai", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.06, output: 0.18 }, + limit: { context: 131072, output: 131072 }, + }, + "chutesai/Mistral-Small-3.1-24B-Instruct-2503": { + id: "chutesai/Mistral-Small-3.1-24B-Instruct-2503", + name: "Mistral Small 3.1 24B Instruct 2503", + family: "chutesai", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.11, cache_read: 0.015 }, + limit: { context: 131072, output: 131072 }, + }, + "nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16": { + id: "nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16", + name: "NVIDIA Nemotron 3 Nano 30B A3B BF16", + family: "nemotron", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.06, output: 0.24 }, + limit: { context: 262144, output: 262144 }, + }, + "deepseek-ai/DeepSeek-R1-TEE": { + id: "deepseek-ai/DeepSeek-R1-TEE", + name: "DeepSeek R1 TEE", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: false, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 163840, output: 163840 }, + }, + "deepseek-ai/DeepSeek-V3": { + id: "deepseek-ai/DeepSeek-V3", + name: "DeepSeek V3", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 163840, output: 163840 }, + }, + "deepseek-ai/DeepSeek-R1-Distill-Llama-70B": { + id: "deepseek-ai/DeepSeek-R1-Distill-Llama-70B", + name: "DeepSeek R1 Distill Llama 70B", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.11 }, + limit: { context: 131072, output: 131072 }, + }, + "deepseek-ai/DeepSeek-V3.1-TEE": { + id: "deepseek-ai/DeepSeek-V3.1-TEE", + name: "DeepSeek V3.1 TEE", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.8 }, + limit: { context: 163840, output: 65536 }, + }, + "deepseek-ai/DeepSeek-V3-0324-TEE": { + id: "deepseek-ai/DeepSeek-V3-0324-TEE", + name: "DeepSeek V3 0324 TEE", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.19, output: 0.87, cache_read: 0.095 }, + limit: { context: 163840, output: 65536 }, + }, + "deepseek-ai/DeepSeek-V3.2-Speciale-TEE": { + id: "deepseek-ai/DeepSeek-V3.2-Speciale-TEE", + name: "DeepSeek V3.2 Speciale TEE", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: false, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 0.41 }, + limit: { context: 163840, output: 65536 }, + }, + "deepseek-ai/DeepSeek-V3.1-Terminus-TEE": { + id: "deepseek-ai/DeepSeek-V3.1-Terminus-TEE", + name: "DeepSeek V3.1 Terminus TEE", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.23, output: 0.9 }, + limit: { context: 163840, output: 65536 }, + }, + "deepseek-ai/DeepSeek-R1-0528-TEE": { + id: "deepseek-ai/DeepSeek-R1-0528-TEE", + name: "DeepSeek R1 0528 TEE", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 1.75 }, + limit: { context: 163840, output: 65536 }, + }, + "deepseek-ai/DeepSeek-V3.2-TEE": { + id: "deepseek-ai/DeepSeek-V3.2-TEE", + name: "DeepSeek V3.2 TEE", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.28, output: 0.42, cache_read: 0.14 }, + limit: { context: 131072, output: 65536 }, + }, + "openai/gpt-oss-20b": { + id: "openai/gpt-oss-20b", + name: "gpt oss 20b", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0.1 }, + limit: { context: 131072, output: 131072 }, + }, + "openai/gpt-oss-120b-TEE": { + id: "openai/gpt-oss-120b-TEE", + name: "gpt oss 120b TEE", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.04, output: 0.18 }, + limit: { context: 131072, output: 65536 }, + }, + "unsloth/gemma-3-12b-it": { + id: "unsloth/gemma-3-12b-it", + name: "gemma 3 12b it", + family: "unsloth", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.1 }, + limit: { context: 131072, output: 131072 }, + }, + "unsloth/Llama-3.2-3B-Instruct": { + id: "unsloth/Llama-3.2-3B-Instruct", + name: "Llama 3.2 3B Instruct", + family: "unsloth", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-02-12", + last_updated: "2025-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.01, output: 0.01, cache_read: 0.005 }, + limit: { context: 16384, output: 16384 }, + }, + "unsloth/gemma-3-4b-it": { + id: "unsloth/gemma-3-4b-it", + name: "gemma 3 4b it", + family: "unsloth", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.01, output: 0.03 }, + limit: { context: 96000, output: 96000 }, + }, + "unsloth/Llama-3.2-1B-Instruct": { + id: "unsloth/Llama-3.2-1B-Instruct", + name: "Llama 3.2 1B Instruct", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.01, output: 0.01, cache_read: 0.005 }, + limit: { context: 32768, output: 8192 }, + }, + "unsloth/Mistral-Nemo-Instruct-2407": { + id: "unsloth/Mistral-Nemo-Instruct-2407", + name: "Mistral Nemo Instruct 2407", + family: "unsloth", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0.04, cache_read: 0.01 }, + limit: { context: 131072, output: 131072 }, + }, + "unsloth/Mistral-Small-24B-Instruct-2501": { + id: "unsloth/Mistral-Small-24B-Instruct-2501", + name: "Mistral Small 24B Instruct 2501", + family: "unsloth", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.11 }, + limit: { context: 32768, output: 32768 }, + }, + "unsloth/gemma-3-27b-it": { + id: "unsloth/gemma-3-27b-it", + name: "gemma 3 27b it", + family: "unsloth", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.04, output: 0.15, cache_read: 0.02 }, + limit: { context: 128000, output: 65536 }, + }, + "moonshotai/Kimi-K2-Thinking-TEE": { + id: "moonshotai/Kimi-K2-Thinking-TEE", + name: "Kimi K2 Thinking TEE", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 1.75 }, + limit: { context: 262144, output: 65535 }, + }, + "moonshotai/Kimi-K2-Instruct-0905": { + id: "moonshotai/Kimi-K2-Instruct-0905", + name: "Kimi K2 Instruct 0905", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.39, output: 1.9, cache_read: 0.195 }, + limit: { context: 262144, output: 262144 }, + }, + "moonshotai/Kimi-K2.6-TEE": { + id: "moonshotai/Kimi-K2.6-TEE", + name: "Kimi K2.6 TEE", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-12", + release_date: "2026-04-20", + last_updated: "2026-04-23", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.44, output: 2 }, + limit: { context: 262144, output: 262144 }, + }, + "moonshotai/Kimi-K2.5-TEE": { + id: "moonshotai/Kimi-K2.5-TEE", + name: "Kimi K2.5 TEE", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2024-10", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3 }, + limit: { context: 262144, output: 65535 }, + }, + "MiniMaxAI/MiniMax-M2.1-TEE": { + id: "MiniMaxAI/MiniMax-M2.1-TEE", + name: "MiniMax M2.1 TEE", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 1.12 }, + limit: { context: 196608, output: 65536 }, + }, + "MiniMaxAI/MiniMax-M2.5-TEE": { + id: "MiniMaxAI/MiniMax-M2.5-TEE", + name: "MiniMax M2.5 TEE", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-02-15", + last_updated: "2026-02-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.1, cache_read: 0.15 }, + limit: { context: 196608, output: 65536 }, + }, + "rednote-hilab/dots.ocr": { + id: "rednote-hilab/dots.ocr", + name: "dots.ocr", + family: "rednote", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.01, output: 0.01, cache_read: 0.005 }, + limit: { context: 131072, output: 131072 }, + }, + "tngtech/TNG-R1T-Chimera-Turbo": { + id: "tngtech/TNG-R1T-Chimera-Turbo", + name: "TNG R1T Chimera Turbo", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.22, output: 0.6 }, + limit: { context: 163840, output: 65536 }, + }, + "tngtech/DeepSeek-TNG-R1T2-Chimera": { + id: "tngtech/DeepSeek-TNG-R1T2-Chimera", + name: "DeepSeek TNG R1T2 Chimera", + family: "tngtech", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 0.85 }, + limit: { context: 163840, output: 163840 }, + }, + "tngtech/DeepSeek-R1T-Chimera": { + id: "tngtech/DeepSeek-R1T-Chimera", + name: "DeepSeek R1T Chimera", + family: "tngtech", + attachment: false, + reasoning: true, + tool_call: false, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 163840, output: 163840 }, + }, + "tngtech/TNG-R1T-Chimera-TEE": { + id: "tngtech/TNG-R1T-Chimera-TEE", + name: "TNG R1T Chimera TEE", + family: "tngtech", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 0.85 }, + limit: { context: 163840, output: 65536 }, + }, + }, + }, + dinference: { + id: "dinference", + env: ["DINFERENCE_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.dinference.com/v1", + name: "DInference", + doc: "https://dinference.com", + models: { + "gpt-oss-120b": { + id: "gpt-oss-120b", + name: "GPT OSS 120B", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-08", + last_updated: "2025-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.0675, output: 0.27 }, + limit: { context: 131072, output: 32768 }, + }, + "glm-4.7": { + id: "glm-4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.45, output: 1.65 }, + limit: { context: 200000, output: 128000 }, + }, + "glm-5": { + id: "glm-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.75, output: 2.4 }, + limit: { context: 200000, output: 128000 }, + }, + "glm-5.1": { + id: "glm-5.1", + name: "GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-03-27", + last_updated: "2026-03-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 3.89 }, + limit: { context: 200000, output: 128000 }, + }, + "minimax-m2.5": { + id: "minimax-m2.5", + name: "MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.22, output: 0.88 }, + limit: { context: 200000, output: 32000 }, + }, + }, + }, + vivgrid: { + id: "vivgrid", + env: ["VIVGRID_API_KEY"], + npm: "@ai-sdk/openai", + api: "https://api.vivgrid.com/v1", + name: "Vivgrid", + doc: "https://docs.vivgrid.com/models", + models: { + "gpt-5.1-codex-max": { + id: "gpt-5.1-codex-max", + name: "GPT-5.1 Codex Max", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "gemini-3.1-flash-lite-preview": { + id: "gemini-3.1-flash-lite-preview", + name: "Gemini 3.1 Flash Lite Preview", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-03-03", + last_updated: "2026-03-03", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.5, cache_read: 0.025, cache_write: 1 }, + limit: { context: 1048576, output: 65536 }, + provider: { npm: "@ai-sdk/openai-compatible" }, + }, + "gemini-3.1-pro-preview": { + id: "gemini-3.1-pro-preview", + name: "Gemini 3.1 Pro Preview", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-19", + last_updated: "2026-02-19", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2, context_over_200k: { input: 4, output: 18, cache_read: 0.4 } }, + limit: { context: 1048576, output: 65536 }, + provider: { npm: "@ai-sdk/openai-compatible" }, + }, + "gpt-5-mini": { + id: "gpt-5-mini", + name: "GPT-5 Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.03 }, + limit: { context: 272000, output: 128000 }, + provider: { npm: "@ai-sdk/openai-compatible" }, + }, + "gpt-5.3-codex": { + id: "gpt-5.3-codex", + name: "GPT-5.3 Codex", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-02-24", + last_updated: "2026-02-24", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, output: 128000 }, + }, + "gpt-5.4-mini": { + id: "gpt-5.4-mini", + name: "GPT-5.4 Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.75, output: 4.5, cache_read: 0.075 }, + limit: { context: 400000, input: 272000, output: 128000 }, + provider: { npm: "@ai-sdk/openai-compatible" }, + }, + "gpt-5.4-nano": { + id: "gpt-5.4-nano", + name: "GPT-5.4 Nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.25, cache_read: 0.02 }, + limit: { context: 400000, input: 272000, output: 128000 }, + provider: { npm: "@ai-sdk/openai-compatible" }, + }, + "gpt-5.2-codex": { + id: "gpt-5.2-codex", + name: "GPT-5.2 Codex", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-01-14", + last_updated: "2026-01-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, output: 128000 }, + }, + "gpt-5.4": { + id: "gpt-5.4", + name: "GPT-5.4", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 15, cache_read: 0.25 }, + limit: { context: 400000, input: 272000, output: 128000 }, + provider: { npm: "@ai-sdk/openai-compatible" }, + }, + "deepseek-v3.2": { + id: "deepseek-v3.2", + name: "DeepSeek-V3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.28, output: 0.42 }, + limit: { context: 128000, output: 128000 }, + provider: { npm: "@ai-sdk/openai-compatible" }, + }, + "gpt-5.1-codex": { + id: "gpt-5.1-codex", + name: "GPT-5.1 Codex", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "gpt-5.5": { + id: "gpt-5.5", + name: "GPT-5.5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-12-01", + release_date: "2026-04-23", + last_updated: "2026-04-23", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 30, cache_read: 0.5, context_over_200k: { input: 10, output: 45, cache_read: 1 } }, + limit: { context: 1050000, input: 922000, output: 128000 }, + provider: { npm: "@ai-sdk/openai-compatible" }, + }, + "deepseek-v4-pro": { + id: "deepseek-v4-pro", + name: "DeepSeek V4 Pro", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.74, output: 3.48, cache_read: 0.145 }, + limit: { context: 1000000, output: 384000 }, + provider: { npm: "@ai-sdk/openai-compatible" }, + }, + }, + }, + deepinfra: { + id: "deepinfra", + env: ["DEEPINFRA_API_KEY"], + npm: "@ai-sdk/deepinfra", + name: "Deep Infra", + doc: "https://deepinfra.com/models", + models: { + "Qwen/Qwen3.5-397B-A17B": { + id: "Qwen/Qwen3.5-397B-A17B", + name: "Qwen 3.5 397B A17B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-01", + last_updated: "2026-04-20", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.54, output: 3.4 }, + limit: { context: 262144, output: 81920 }, + }, + "Qwen/Qwen3.5-35B-A3B": { + id: "Qwen/Qwen3.5-35B-A3B", + name: "Qwen 3.5 35B A3B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-01", + last_updated: "2026-04-20", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.95 }, + limit: { context: 262144, output: 81920 }, + }, + "Qwen/Qwen3-Coder-480B-A35B-Instruct-Turbo": { + id: "Qwen/Qwen3-Coder-480B-A35B-Instruct-Turbo", + name: "Qwen3 Coder 480B A35B Instruct Turbo", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 262144, output: 66536 }, + }, + "Qwen/Qwen3-Coder-480B-A35B-Instruct": { + id: "Qwen/Qwen3-Coder-480B-A35B-Instruct", + name: "Qwen3 Coder 480B A35B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 1.6 }, + limit: { context: 262144, output: 66536 }, + }, + "Qwen/Qwen3.6-35B-A3B": { + id: "Qwen/Qwen3.6-35B-A3B", + name: "Qwen3.6 35B A3B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-01", + last_updated: "2026-04-01", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 1 }, + limit: { context: 262144, output: 81920 }, + }, + "zai-org/GLM-4.7-Flash": { + id: "zai-org/GLM-4.7-Flash", + name: "GLM-4.7-Flash", + family: "glm-flash", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2026-01-19", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.06, output: 0.4 }, + limit: { context: 202752, output: 16384 }, + }, + "zai-org/GLM-4.5": { + id: "zai-org/GLM-4.5", + name: "GLM-4.5", + family: "glm", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2 }, + limit: { context: 131072, output: 98304 }, + status: "deprecated", + }, + "zai-org/GLM-4.7": { + id: "zai-org/GLM-4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.43, output: 1.75, cache_read: 0.08 }, + limit: { context: 202752, output: 16384 }, + }, + "zai-org/GLM-5.1": { + id: "zai-org/GLM-5.1", + name: "GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-04-07", + last_updated: "2026-04-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.4, output: 4.4, cache_read: 0.26 }, + limit: { context: 202752, output: 16384 }, + }, + "zai-org/GLM-5": { + id: "zai-org/GLM-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-12", + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.8, output: 2.56, cache_read: 0.16 }, + limit: { context: 202752, output: 16384 }, + }, + "zai-org/GLM-4.6V": { + id: "zai-org/GLM-4.6V", + name: "GLM-4.6V", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.9 }, + limit: { context: 204800, output: 131072 }, + }, + "zai-org/GLM-4.6": { + id: "zai-org/GLM-4.6", + name: "GLM-4.6", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.43, output: 1.74, cache_read: 0.08 }, + limit: { context: 204800, output: 131072 }, + }, + "meta-llama/Llama-4-Scout-17B-16E-Instruct": { + id: "meta-llama/Llama-4-Scout-17B-16E-Instruct", + name: "Llama 4 Scout 17B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.08, output: 0.3 }, + limit: { context: 10000000, output: 16384 }, + }, + "meta-llama/Llama-3.1-8B-Instruct": { + id: "meta-llama/Llama-3.1-8B-Instruct", + name: "Llama 3.1 8B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0.05 }, + limit: { context: 131072, output: 16384 }, + }, + "meta-llama/Llama-3.1-70B-Instruct": { + id: "meta-llama/Llama-3.1-70B-Instruct", + name: "Llama 3.1 70B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 0.4 }, + limit: { context: 131072, output: 16384 }, + }, + "meta-llama/Llama-3.1-8B-Instruct-Turbo": { + id: "meta-llama/Llama-3.1-8B-Instruct-Turbo", + name: "Llama 3.1 8B Turbo", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0.03 }, + limit: { context: 131072, output: 16384 }, + }, + "meta-llama/Llama-3.3-70B-Instruct-Turbo": { + id: "meta-llama/Llama-3.3-70B-Instruct-Turbo", + name: "Llama 3.3 70B Turbo", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.32 }, + limit: { context: 131072, output: 16384 }, + }, + "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8": { + id: "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8", + name: "Llama 4 Maverick 17B FP8", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 1000000, output: 16384 }, + }, + "meta-llama/Llama-3.1-70B-Instruct-Turbo": { + id: "meta-llama/Llama-3.1-70B-Instruct-Turbo", + name: "Llama 3.1 70B Turbo", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 0.4 }, + limit: { context: 131072, output: 16384 }, + }, + "deepseek-ai/DeepSeek-R1-0528": { + id: "deepseek-ai/DeepSeek-R1-0528", + name: "DeepSeek-R1-0528", + attachment: false, + reasoning: true, + tool_call: false, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-07", + release_date: "2025-05-28", + last_updated: "2025-05-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 2.15, cache_read: 0.35 }, + limit: { context: 163840, output: 64000 }, + }, + "deepseek-ai/DeepSeek-V3.2": { + id: "deepseek-ai/DeepSeek-V3.2", + name: "DeepSeek-V3.2", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2025-12-02", + last_updated: "2025-12-02", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.26, output: 0.38, cache_read: 0.13 }, + limit: { context: 163840, output: 64000 }, + }, + "openai/gpt-oss-20b": { + id: "openai/gpt-oss-20b", + name: "GPT OSS 20B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.14 }, + limit: { context: 131072, output: 16384 }, + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "GPT OSS 120B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.24 }, + limit: { context: 131072, output: 16384 }, + }, + "moonshotai/Kimi-K2-Thinking": { + id: "moonshotai/Kimi-K2-Thinking", + name: "Kimi K2 Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-10", + release_date: "2025-11-06", + last_updated: "2025-11-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.47, output: 2 }, + limit: { context: 131072, output: 32768 }, + }, + "moonshotai/Kimi-K2.6": { + id: "moonshotai/Kimi-K2.6", + name: "Kimi K2.6", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.75, output: 3.5, cache_read: 0.15 }, + limit: { context: 262144, output: 16384 }, + }, + "moonshotai/Kimi-K2-Instruct": { + id: "moonshotai/Kimi-K2-Instruct", + name: "Kimi K2", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-07-11", + last_updated: "2025-07-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 2 }, + limit: { context: 131072, output: 32768 }, + }, + "moonshotai/Kimi-K2-Instruct-0905": { + id: "moonshotai/Kimi-K2-Instruct-0905", + name: "Kimi K2 0905", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 2, cache_read: 0.15 }, + limit: { context: 262144, output: 262144 }, + }, + "moonshotai/Kimi-K2.5": { + id: "moonshotai/Kimi-K2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 2.8 }, + limit: { context: 262144, output: 32768 }, + }, + "MiniMaxAI/MiniMax-M2": { + id: "MiniMaxAI/MiniMax-M2", + name: "MiniMax M2", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-10", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.254, output: 1.02 }, + limit: { context: 262144, output: 32768 }, + }, + "MiniMaxAI/MiniMax-M2.5": { + id: "MiniMaxAI/MiniMax-M2.5", + name: "MiniMax M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-06", + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 0.95, cache_read: 0.03, cache_write: 0.375 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMaxAI/MiniMax-M2.1": { + id: "MiniMaxAI/MiniMax-M2.1", + name: "MiniMax M2.1", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-06", + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.28, output: 1.2 }, + limit: { context: 196608, output: 196608 }, + }, + "anthropic/claude-3-7-sonnet-latest": { + id: "anthropic/claude-3-7-sonnet-latest", + name: "Claude Sonnet 3.7 (Latest)", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10-31", + release_date: "2025-03-13", + last_updated: "2025-03-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3.3, output: 16.5, cache_read: 0.33 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-4-opus": { + id: "anthropic/claude-4-opus", + name: "Claude Opus 4", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-06-12", + last_updated: "2025-06-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 16.5, output: 82.5 }, + limit: { context: 200000, output: 32000 }, + }, + "deepseek-ai/DeepSeek-V4-Flash": { + id: "deepseek-ai/DeepSeek-V4-Flash", + name: "DeepSeek V4 Flash", + family: "deepseek-flash", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.28, cache_read: 0.028 }, + limit: { context: 1000000, output: 384000 }, + }, + "deepseek-ai/DeepSeek-V4-Pro": { + id: "deepseek-ai/DeepSeek-V4-Pro", + name: "DeepSeek V4 Pro", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.74, output: 3.48, cache_read: 0.145 }, + limit: { context: 65536, output: 65536 }, + }, + "google/gemma-4-26B-A4B-it": { + id: "google/gemma-4-26B-A4B-it", + name: "Gemma 4 26B", + family: "gemma", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.34 }, + limit: { context: 256000, output: 8192 }, + }, + "google/gemma-4-31B-it": { + id: "google/gemma-4-31B-it", + name: "Gemma 4 31B", + family: "gemma", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.13, output: 0.38 }, + limit: { context: 256000, output: 8192 }, + }, + }, + }, + "qiniu-ai": { + id: "qiniu-ai", + env: ["QINIU_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.qnaigc.com/v1", + name: "Qiniu", + doc: "https://developer.qiniu.com/aitokenapi", + models: { + "qwen3-235b-a22b": { + id: "qwen3-235b-a22b", + name: "Qwen 3 235B A22B", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 32000 }, + }, + "doubao-seed-1.6-flash": { + id: "doubao-seed-1.6-flash", + name: "Doubao-Seed 1.6 Flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-15", + last_updated: "2025-08-15", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 256000, output: 32000 }, + }, + "qwen3-235b-a22b-instruct-2507": { + id: "qwen3-235b-a22b-instruct-2507", + name: "Qwen3 235b A22B Instruct 2507", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-12", + last_updated: "2025-08-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 262144, output: 64000 }, + }, + "doubao-seed-2.0-code": { + id: "doubao-seed-2.0-code", + name: "Doubao Seed 2.0 Code", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2026-02-14", + last_updated: "2026-02-14", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 256000, output: 128000 }, + }, + "deepseek-v3-0324": { + id: "deepseek-v3-0324", + name: "DeepSeek-V3-0324", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 16000 }, + }, + "doubao-1.5-thinking-pro": { + id: "doubao-1.5-thinking-pro", + name: "Doubao 1.5 Thinking Pro", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 16000 }, + }, + "claude-3.7-sonnet": { + id: "claude-3.7-sonnet", + name: "Claude 3.7 Sonnet", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + limit: { context: 200000, output: 128000 }, + }, + "qwen3.5-397b-a17b": { + id: "qwen3.5-397b-a17b", + name: "Qwen3.5 397B A17B", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2026-02-22", + last_updated: "2026-02-22", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + limit: { context: 256000, output: 64000 }, + }, + "qwen-vl-max-2025-01-25": { + id: "qwen-vl-max-2025-01-25", + name: "Qwen VL-MAX-2025-01-25", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 4096 }, + }, + "qwen3-32b": { + id: "qwen3-32b", + name: "Qwen3 32B", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 40000, output: 4096 }, + }, + "doubao-1.5-pro-32k": { + id: "doubao-1.5-pro-32k", + name: "Doubao 1.5 Pro 32k", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 12000 }, + }, + "qwen2.5-vl-72b-instruct": { + id: "qwen2.5-vl-72b-instruct", + name: "Qwen 2.5 VL 72B Instruct", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 8192 }, + }, + "gemini-2.0-flash": { + id: "gemini-2.0-flash", + name: "Gemini 2.0 Flash", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 1048576, output: 8192 }, + }, + "qwen3-vl-30b-a3b-thinking": { + id: "qwen3-vl-30b-a3b-thinking", + name: "Qwen3-Vl 30b A3b Thinking", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2026-02-09", + last_updated: "2026-02-09", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 32000 }, + }, + "gemini-3.0-pro-image-preview": { + id: "gemini-3.0-pro-image-preview", + name: "Gemini 3.0 Pro Image Preview", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-11-20", + last_updated: "2025-11-20", + modalities: { input: ["text", "image"], output: ["text", "image"] }, + open_weights: false, + limit: { context: 32768, output: 8192 }, + }, + "gemini-2.5-pro": { + id: "gemini-2.5-pro", + name: "Gemini 2.5 Pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: false, + limit: { context: 1048576, output: 65536 }, + }, + "claude-4.5-opus": { + id: "claude-4.5-opus", + name: "Claude 4.5 Opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-11-25", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + limit: { context: 200000, output: 200000 }, + }, + "deepseek-r1": { + id: "deepseek-r1", + name: "DeepSeek-R1", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 32000 }, + }, + "claude-4.0-opus": { + id: "claude-4.0-opus", + name: "Claude 4.0 Opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + limit: { context: 200000, output: 32000 }, + }, + "claude-4.5-haiku": { + id: "claude-4.5-haiku", + name: "Claude 4.5 Haiku", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-10-16", + last_updated: "2025-10-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + limit: { context: 200000, output: 64000 }, + }, + "qwen3-max": { + id: "qwen3-max", + name: "Qwen3 Max", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-09-24", + last_updated: "2025-09-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 262144, output: 65536 }, + }, + "gemini-3.0-flash-preview": { + id: "gemini-3.0-flash-preview", + name: "Gemini 3.0 Flash Preview", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-12-18", + last_updated: "2025-12-18", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + limit: { context: 1000000, output: 64000 }, + }, + "gemini-2.5-flash-image": { + id: "gemini-2.5-flash-image", + name: "Gemini 2.5 Flash Image", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-10-22", + last_updated: "2025-10-22", + modalities: { input: ["text", "image"], output: ["image"] }, + open_weights: false, + limit: { context: 32768, output: 8192 }, + }, + "glm-4.5": { + id: "glm-4.5", + name: "GLM 4.5", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 131072, output: 98304 }, + }, + "claude-3.5-sonnet": { + id: "claude-3.5-sonnet", + name: "Claude 3.5 Sonnet", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-09-09", + last_updated: "2025-09-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + limit: { context: 200000, output: 8200 }, + }, + "claude-4.0-sonnet": { + id: "claude-4.0-sonnet", + name: "Claude 4.0 Sonnet", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + limit: { context: 200000, output: 64000 }, + }, + "qwen3-30b-a3b-instruct-2507": { + id: "qwen3-30b-a3b-instruct-2507", + name: "Qwen3 30b A3b Instruct 2507", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2026-02-04", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 32000 }, + }, + "doubao-seed-1.6-thinking": { + id: "doubao-seed-1.6-thinking", + name: "Doubao-Seed 1.6 Thinking", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-15", + last_updated: "2025-08-15", + modalities: { input: ["image", "text", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 256000, output: 32000 }, + }, + "gemini-2.5-flash": { + id: "gemini-2.5-flash", + name: "Gemini 2.5 Flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 1048576, output: 64000 }, + }, + "qwen3-235b-a22b-thinking-2507": { + id: "qwen3-235b-a22b-thinking-2507", + name: "Qwen3 235B A22B Thinking 2507", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-12", + last_updated: "2025-08-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 262144, output: 4096 }, + }, + "qwen3-next-80b-a3b-thinking": { + id: "qwen3-next-80b-a3b-thinking", + name: "Qwen3 Next 80B A3B Thinking", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-09-12", + last_updated: "2025-09-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 131072, output: 32768 }, + }, + "qwen3-30b-a3b-thinking-2507": { + id: "qwen3-30b-a3b-thinking-2507", + name: "Qwen3 30b A3b Thinking 2507", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2026-02-04", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 126000, output: 32000 }, + }, + "glm-4.5-air": { + id: "glm-4.5-air", + name: "GLM 4.5 Air", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 131000, output: 4096 }, + }, + "deepseek-v3.1": { + id: "deepseek-v3.1", + name: "DeepSeek-V3.1", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-19", + last_updated: "2025-08-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 32000 }, + }, + "qwen3-30b-a3b": { + id: "qwen3-30b-a3b", + name: "Qwen3 30B A3B", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 40000, output: 4096 }, + }, + "claude-4.1-opus": { + id: "claude-4.1-opus", + name: "Claude 4.1 Opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-06", + last_updated: "2025-08-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + limit: { context: 200000, output: 32000 }, + }, + "doubao-seed-2.0-mini": { + id: "doubao-seed-2.0-mini", + name: "Doubao Seed 2.0 Mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2026-02-14", + last_updated: "2026-02-14", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 256000, output: 32000 }, + }, + "qwen3-next-80b-a3b-instruct": { + id: "qwen3-next-80b-a3b-instruct", + name: "Qwen3 Next 80B A3B Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-09-12", + last_updated: "2025-09-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 131072, output: 32768 }, + }, + "doubao-seed-1.6": { + id: "doubao-seed-1.6", + name: "Doubao-Seed 1.6", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-15", + last_updated: "2025-08-15", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 256000, output: 32000 }, + }, + "qwen2.5-vl-7b-instruct": { + id: "qwen2.5-vl-7b-instruct", + name: "Qwen 2.5 VL 7B Instruct", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 8192 }, + }, + "kling-v2-6": { + id: "kling-v2-6", + name: "Kling-V2 6", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2026-01-13", + last_updated: "2026-01-13", + modalities: { input: ["text", "image", "video"], output: ["video"] }, + open_weights: false, + limit: { context: 99999999, output: 99999999 }, + }, + "MiniMax-M1": { + id: "MiniMax-M1", + name: "MiniMax M1", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 1000000, output: 80000 }, + }, + "gemini-3.0-pro-preview": { + id: "gemini-3.0-pro-preview", + name: "Gemini 3.0 Pro Preview", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-11-19", + last_updated: "2025-11-19", + modalities: { input: ["text", "image", "video", "pdf", "audio"], output: ["text"] }, + open_weights: false, + limit: { context: 1000000, output: 64000 }, + }, + "doubao-seed-2.0-lite": { + id: "doubao-seed-2.0-lite", + name: "Doubao Seed 2.0 Lite", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2026-02-14", + last_updated: "2026-02-14", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 256000, output: 32000 }, + }, + "qwen3-coder-480b-a35b-instruct": { + id: "qwen3-coder-480b-a35b-instruct", + name: "Qwen3 Coder 480B A35B Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-14", + last_updated: "2025-08-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 262000, output: 4096 }, + }, + "claude-3.5-haiku": { + id: "claude-3.5-haiku", + name: "Claude 3.5 Haiku", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-26", + last_updated: "2025-08-26", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + limit: { context: 200000, output: 8192 }, + }, + "gpt-oss-20b": { + id: "gpt-oss-20b", + name: "gpt-oss-20b", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-06", + last_updated: "2025-08-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 4096 }, + }, + "qwen-turbo": { + id: "qwen-turbo", + name: "Qwen-Turbo", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 1000000, output: 4096 }, + }, + "kimi-k2": { + id: "kimi-k2", + name: "Kimi K2", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 128000 }, + }, + "gemini-2.5-flash-lite": { + id: "gemini-2.5-flash-lite", + name: "Gemini 2.5 Flash Lite", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 1048576, output: 64000 }, + }, + "qwen3-max-preview": { + id: "qwen3-max-preview", + name: "Qwen3 Max Preview", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-09-06", + last_updated: "2025-09-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 256000, output: 64000 }, + }, + "gpt-oss-120b": { + id: "gpt-oss-120b", + name: "gpt-oss-120b", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-06", + last_updated: "2025-08-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 4096 }, + }, + "doubao-1.5-vision-pro": { + id: "doubao-1.5-vision-pro", + name: "Doubao 1.5 Vision Pro", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 16000 }, + }, + "claude-4.5-sonnet": { + id: "claude-4.5-sonnet", + name: "Claude 4.5 Sonnet", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + limit: { context: 200000, output: 64000 }, + }, + "deepseek-v3": { + id: "deepseek-v3", + name: "DeepSeek-V3", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-08-13", + last_updated: "2025-08-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 16000 }, + }, + "deepseek-r1-0528": { + id: "deepseek-r1-0528", + name: "DeepSeek-R1-0528", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 32000 }, + }, + "gemini-2.0-flash-lite": { + id: "gemini-2.0-flash-lite", + name: "Gemini 2.0 Flash Lite", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 1048576, output: 8192 }, + }, + "qwen-max-2025-01-25": { + id: "qwen-max-2025-01-25", + name: "Qwen2.5-Max-2025-01-25", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 4096 }, + }, + "doubao-seed-2.0-pro": { + id: "doubao-seed-2.0-pro", + name: "Doubao Seed 2.0 Pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2026-02-14", + last_updated: "2026-02-14", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 256000, output: 128000 }, + }, + "deepseek/deepseek-v3.2-exp-thinking": { + id: "deepseek/deepseek-v3.2-exp-thinking", + name: "DeepSeek/DeepSeek-V3.2-Exp-Thinking", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 32000 }, + }, + "deepseek/deepseek-v3.1-terminus-thinking": { + id: "deepseek/deepseek-v3.1-terminus-thinking", + name: "DeepSeek/DeepSeek-V3.1-Terminus-Thinking", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-09-22", + last_updated: "2025-09-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 32000 }, + }, + "deepseek/deepseek-v3.2-exp": { + id: "deepseek/deepseek-v3.2-exp", + name: "DeepSeek/DeepSeek-V3.2-Exp", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 32000 }, + }, + "deepseek/deepseek-v3.2-251201": { + id: "deepseek/deepseek-v3.2-251201", + name: "Deepseek/DeepSeek-V3.2", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 32000 }, + }, + "deepseek/deepseek-math-v2": { + id: "deepseek/deepseek-math-v2", + name: "Deepseek/Deepseek-Math-V2", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-12-04", + last_updated: "2025-12-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 160000, output: 160000 }, + }, + "deepseek/deepseek-v3.1-terminus": { + id: "deepseek/deepseek-v3.1-terminus", + name: "DeepSeek/DeepSeek-V3.1-Terminus", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-09-22", + last_updated: "2025-09-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 32000 }, + }, + "stepfun-ai/gelab-zero-4b-preview": { + id: "stepfun-ai/gelab-zero-4b-preview", + name: "Stepfun-Ai/Gelab Zero 4b Preview", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + limit: { context: 8192, output: 4096 }, + }, + "stepfun/step-3.5-flash": { + id: "stepfun/step-3.5-flash", + name: "Stepfun/Step-3.5 Flash", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2026-02-02", + last_updated: "2026-02-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + limit: { context: 64000, output: 4096 }, + }, + "x-ai/grok-4-fast": { + id: "x-ai/grok-4-fast", + name: "x-AI/Grok-4-Fast", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-09-20", + last_updated: "2025-09-20", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 2000000, output: 2000000 }, + }, + "x-ai/grok-code-fast-1": { + id: "x-ai/grok-code-fast-1", + name: "x-AI/Grok-Code-Fast 1", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-09-02", + last_updated: "2025-09-02", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 256000, output: 10000 }, + }, + "x-ai/grok-4-fast-reasoning": { + id: "x-ai/grok-4-fast-reasoning", + name: "X-Ai/Grok-4-Fast-Reasoning", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-12-18", + last_updated: "2025-12-18", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 2000000, output: 2000000 }, + }, + "x-ai/grok-4.1-fast-non-reasoning": { + id: "x-ai/grok-4.1-fast-non-reasoning", + name: "X-Ai/Grok 4.1 Fast Non Reasoning", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-12-19", + last_updated: "2025-12-19", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 2000000, output: 2000000 }, + }, + "x-ai/grok-4.1-fast": { + id: "x-ai/grok-4.1-fast", + name: "x-AI/Grok-4.1-Fast", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-11-20", + last_updated: "2025-11-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 2000000, output: 2000000 }, + }, + "x-ai/grok-4-fast-non-reasoning": { + id: "x-ai/grok-4-fast-non-reasoning", + name: "X-Ai/Grok-4-Fast-Non-Reasoning", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-12-18", + last_updated: "2025-12-18", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 2000000, output: 2000000 }, + }, + "x-ai/grok-4.1-fast-reasoning": { + id: "x-ai/grok-4.1-fast-reasoning", + name: "X-Ai/Grok 4.1 Fast Reasoning", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-12-19", + last_updated: "2025-12-19", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 20000000, output: 2000000 }, + }, + "openai/gpt-5.2": { + id: "openai/gpt-5.2", + name: "OpenAI/GPT-5.2", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5": { + id: "openai/gpt-5", + name: "OpenAI/GPT-5", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-09-19", + last_updated: "2025-09-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 400000, output: 128000 }, + }, + "z-ai/glm-4.7": { + id: "z-ai/glm-4.7", + name: "Z-Ai/GLM 4.7", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 200000, output: 200000 }, + }, + "z-ai/glm-5": { + id: "z-ai/glm-5", + name: "Z-Ai/GLM 5", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 200000, output: 128000 }, + }, + "z-ai/autoglm-phone-9b": { + id: "z-ai/autoglm-phone-9b", + name: "Z-Ai/Autoglm Phone 9b", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + limit: { context: 12800, output: 4096 }, + }, + "z-ai/glm-4.6": { + id: "z-ai/glm-4.6", + name: "Z-AI/GLM 4.6", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-10-11", + last_updated: "2025-10-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 200000, output: 200000 }, + }, + "minimax/minimax-m2": { + id: "minimax/minimax-m2", + name: "Minimax/Minimax-M2", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-10-28", + last_updated: "2025-10-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 200000, output: 128000 }, + }, + "minimax/minimax-m2.1": { + id: "minimax/minimax-m2.1", + name: "Minimax/Minimax-M2.1", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 204800, output: 128000 }, + }, + "minimax/minimax-m2.5": { + id: "minimax/minimax-m2.5", + name: "Minimax/Minimax-M2.5", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 204800, output: 128000 }, + }, + "minimax/minimax-m2.5-highspeed": { + id: "minimax/minimax-m2.5-highspeed", + name: "Minimax/Minimax-M2.5 Highspeed", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2026-02-14", + last_updated: "2026-02-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 204800, output: 128000 }, + }, + "moonshotai/kimi-k2.5": { + id: "moonshotai/kimi-k2.5", + name: "Moonshotai/Kimi-K2.5", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2026-01-28", + last_updated: "2026-01-28", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + limit: { context: 256000, output: 256000 }, + }, + "moonshotai/kimi-k2-0905": { + id: "moonshotai/kimi-k2-0905", + name: "Kimi K2 0905", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-09-08", + last_updated: "2025-09-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 256000, output: 100000 }, + }, + "moonshotai/kimi-k2-thinking": { + id: "moonshotai/kimi-k2-thinking", + name: "Kimi K2 Thinking", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-11-07", + last_updated: "2025-11-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 256000, output: 100000 }, + }, + "meituan/longcat-flash-chat": { + id: "meituan/longcat-flash-chat", + name: "Meituan/Longcat-Flash-Chat", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-11-05", + last_updated: "2025-11-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 131072, output: 131072 }, + }, + "meituan/longcat-flash-lite": { + id: "meituan/longcat-flash-lite", + name: "Meituan/Longcat-Flash-Lite", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2026-02-06", + last_updated: "2026-02-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 256000, output: 320000 }, + }, + "mimo-v2-flash": { + id: "mimo-v2-flash", + name: "Mimo-V2-Flash", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-12-01", + release_date: "2025-12-16", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3, cache_read: 0.01 }, + limit: { context: 256000, output: 256000 }, + }, + "xiaomi/mimo-v2-flash": { + id: "xiaomi/mimo-v2-flash", + name: "Xiaomi/Mimo-V2-Flash", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-12-01", + release_date: "2025-12-16", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3, cache_read: 0.01 }, + limit: { context: 256000, output: 256000 }, + }, + }, + }, + kilo: { + id: "kilo", + env: ["KILO_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.kilo.ai/api/gateway", + name: "Kilo Gateway", + doc: "https://kilo.ai", + models: { + "rekaai/reka-edge": { + id: "rekaai/reka-edge", + name: "Reka Edge", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-03-20", + last_updated: "2026-04-11", + modalities: { input: ["image", "text", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 16384, output: 16384 }, + }, + "rekaai/reka-flash-3": { + id: "rekaai/reka-flash-3", + name: "Reka Flash 3", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-03-12", + last_updated: "2026-04-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.2 }, + limit: { context: 65536, output: 65536 }, + }, + "ai21/jamba-large-1.7": { + id: "ai21/jamba-large-1.7", + name: "AI21: Jamba Large 1.7", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-08-09", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8 }, + limit: { context: 256000, output: 4096 }, + }, + "alibaba/tongyi-deepresearch-30b-a3b": { + id: "alibaba/tongyi-deepresearch-30b-a3b", + name: "Tongyi DeepResearch 30B A3B", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-09-18", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.09, output: 0.45 }, + limit: { context: 131072, output: 131072 }, + }, + "inflection/inflection-3-pi": { + id: "inflection/inflection-3-pi", + name: "Inflection: Inflection 3 Pi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-10-11", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10 }, + limit: { context: 8000, output: 1024 }, + }, + "inflection/inflection-3-productivity": { + id: "inflection/inflection-3-productivity", + name: "Inflection: Inflection 3 Productivity", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-10-11", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10 }, + limit: { context: 8000, output: 1024 }, + }, + "liquid/lfm-2-24b-a2b": { + id: "liquid/lfm-2-24b-a2b", + name: "LiquidAI: LFM2-24B-A2B", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2026-02-26", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.12 }, + limit: { context: 32768, output: 32768 }, + }, + "writer/palmyra-x5": { + id: "writer/palmyra-x5", + name: "Writer: Palmyra X5", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-28", + last_updated: "2025-04-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 6 }, + limit: { context: 1040000, output: 8192 }, + }, + "ibm-granite/granite-4.1-8b": { + id: "ibm-granite/granite-4.1-8b", + name: "IBM: Granite 4.1 8B", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-04-30", + last_updated: "2026-05-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.1, cache_read: 0.05 }, + limit: { context: 131072, output: 131072 }, + }, + "ibm-granite/granite-4.0-h-micro": { + id: "ibm-granite/granite-4.0-h-micro", + name: "IBM: Granite 4.0 Micro", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-10-20", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.017, output: 0.11 }, + limit: { context: 131000, output: 32768 }, + }, + "essentialai/rnj-1-instruct": { + id: "essentialai/rnj-1-instruct", + name: "EssentialAI: Rnj 1 Instruct", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-12-05", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.15 }, + limit: { context: 32768, output: 6554 }, + }, + "perplexity/sonar-pro": { + id: "perplexity/sonar-pro", + name: "Perplexity: Sonar Pro", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-01-01", + last_updated: "2025-09-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 200000, output: 8000 }, + }, + "perplexity/sonar-deep-research": { + id: "perplexity/sonar-deep-research", + name: "Perplexity: Sonar Deep Research", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-01-27", + last_updated: "2025-01-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8 }, + limit: { context: 128000, output: 25600 }, + }, + "perplexity/sonar": { + id: "perplexity/sonar", + name: "Perplexity: Sonar", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-01-01", + last_updated: "2025-09-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 1 }, + limit: { context: 127072, output: 25415 }, + }, + "perplexity/sonar-pro-search": { + id: "perplexity/sonar-pro-search", + name: "Perplexity: Sonar Pro Search", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-10-31", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 200000, output: 8000 }, + }, + "perplexity/sonar-reasoning-pro": { + id: "perplexity/sonar-reasoning-pro", + name: "Perplexity: Sonar Reasoning Pro", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2024-01-01", + last_updated: "2025-09-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8 }, + limit: { context: 128000, output: 25600 }, + }, + "deepseek/deepseek-chat-v3.1": { + id: "deepseek/deepseek-chat-v3.1", + name: "DeepSeek: DeepSeek V3.1", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-21", + last_updated: "2025-08-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.75 }, + limit: { context: 32768, output: 7168 }, + }, + "deepseek/deepseek-chat": { + id: "deepseek/deepseek-chat", + name: "DeepSeek: DeepSeek V3", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-12-01", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.32, output: 0.89, cache_read: 0.15 }, + limit: { context: 163840, output: 163840 }, + }, + "deepseek/deepseek-r1-distill-llama-70b": { + id: "deepseek/deepseek-r1-distill-llama-70b", + name: "DeepSeek: R1 Distill Llama 70B", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-01-23", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.7, output: 0.8, cache_read: 0.015 }, + limit: { context: 131072, output: 16384 }, + }, + "deepseek/deepseek-r1": { + id: "deepseek/deepseek-r1", + name: "DeepSeek: R1", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.7, output: 2.5 }, + limit: { context: 64000, output: 16000 }, + }, + "deepseek/deepseek-v3.2-speciale": { + id: "deepseek/deepseek-v3.2-speciale", + name: "DeepSeek: DeepSeek V3.2 Speciale", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-12-01", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 1.2, cache_read: 0.135 }, + limit: { context: 163840, output: 163840 }, + }, + "deepseek/deepseek-r1-distill-qwen-32b": { + id: "deepseek/deepseek-r1-distill-qwen-32b", + name: "DeepSeek: R1 Distill Qwen 32B", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-01-01", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.29, output: 0.29 }, + limit: { context: 32768, output: 32768 }, + }, + "deepseek/deepseek-v3.2-exp": { + id: "deepseek/deepseek-v3.2-exp", + name: "DeepSeek: DeepSeek V3.2 Exp", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-01-01", + last_updated: "2025-09-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 0.41 }, + limit: { context: 163840, output: 65536 }, + }, + "deepseek/deepseek-v4-flash": { + id: "deepseek/deepseek-v4-flash", + name: "DeepSeek: DeepSeek V4 Flash", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-24", + last_updated: "2026-05-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.28, cache_read: 0.0028 }, + limit: { context: 1048576, output: 384000 }, + }, + "deepseek/deepseek-v4-pro": { + id: "deepseek/deepseek-v4-pro", + name: "DeepSeek: DeepSeek V4 Pro", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-24", + last_updated: "2026-05-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.435, output: 0.87, cache_read: 0.003625 }, + limit: { context: 1048576, output: 384000 }, + }, + "deepseek/deepseek-v3.2": { + id: "deepseek/deepseek-v3.2", + name: "DeepSeek: DeepSeek V3.2", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-12-01", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.26, output: 0.38, cache_read: 0.125 }, + limit: { context: 163840, output: 65536 }, + }, + "deepseek/deepseek-chat-v3-0324": { + id: "deepseek/deepseek-chat-v3-0324", + name: "DeepSeek: DeepSeek V3 0324", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-03-24", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.77, cache_read: 0.095 }, + limit: { context: 163840, output: 65536 }, + }, + "deepseek/deepseek-r1-0528": { + id: "deepseek/deepseek-r1-0528", + name: "DeepSeek: R1 0528", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-05-28", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.45, output: 2.15, cache_read: 0.2 }, + limit: { context: 163840, output: 65536 }, + }, + "deepseek/deepseek-v3.1-terminus": { + id: "deepseek/deepseek-v3.1-terminus", + name: "DeepSeek: DeepSeek V3.1 Terminus", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-09-22", + last_updated: "2025-09-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.21, output: 0.79, cache_read: 0.13 }, + limit: { context: 163840, output: 32768 }, + }, + "openrouter/auto": { + id: "openrouter/auto", + name: "Auto Router", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-15", + last_updated: "2026-03-15", + modalities: { input: ["audio", "image", "pdf", "text", "video"], output: ["image", "text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 2000000, output: 32768 }, + }, + "openrouter/bodybuilder": { + id: "openrouter/bodybuilder", + name: "Body Builder (beta)", + attachment: false, + reasoning: false, + tool_call: false, + release_date: "2026-03-15", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 32768 }, + status: "beta", + }, + "openrouter/owl-alpha": { + id: "openrouter/owl-alpha", + name: "Owl Alpha", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-28", + last_updated: "2026-04-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 1048756, output: 262144 }, + status: "alpha", + }, + "openrouter/pareto-code": { + id: "openrouter/pareto-code", + name: "Pareto Code Router", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2026-04-21", + last_updated: "2026-05-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 200000, output: 65536 }, + }, + "openrouter/free": { + id: "openrouter/free", + name: "Free Models Router", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-01", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 200000, output: 32768 }, + }, + "inclusionai/ling-2.6-1t:free": { + id: "inclusionai/ling-2.6-1t:free", + name: "inclusionAI: Ling-2.6-1T (free)", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-04-23", + last_updated: "2026-05-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 32768 }, + }, + "inclusionai/ling-2.6-flash": { + id: "inclusionai/ling-2.6-flash", + name: "inclusionAI: Ling-2.6 Flash", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-04-21", + last_updated: "2026-05-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.08, output: 0.24, cache_read: 0.016 }, + limit: { context: 262144, output: 32768 }, + }, + "arcee-ai/trinity-mini": { + id: "arcee-ai/trinity-mini", + name: "Arcee AI: Trinity Mini", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-12", + last_updated: "2026-01-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.045, output: 0.15 }, + limit: { context: 131072, output: 131072 }, + }, + "arcee-ai/virtuoso-large": { + id: "arcee-ai/virtuoso-large", + name: "Arcee AI: Virtuoso Large", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-05-06", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.75, output: 1.2 }, + limit: { context: 131072, output: 64000 }, + }, + "arcee-ai/trinity-large-thinking": { + id: "arcee-ai/trinity-large-thinking", + name: "Arcee AI: Trinity Large Thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-01", + last_updated: "2026-04-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.22, output: 0.85 }, + limit: { context: 262144, output: 262144 }, + }, + "arcee-ai/spotlight": { + id: "arcee-ai/spotlight", + name: "Arcee AI: Spotlight", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-05-06", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.18, output: 0.18 }, + limit: { context: 131072, output: 65537 }, + }, + "arcee-ai/maestro-reasoning": { + id: "arcee-ai/maestro-reasoning", + name: "Arcee AI: Maestro Reasoning", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-05-06", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.9, output: 3.3 }, + limit: { context: 131072, output: 32000 }, + }, + "arcee-ai/coder-large": { + id: "arcee-ai/coder-large", + name: "Arcee AI: Coder Large", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-05-06", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 0.8 }, + limit: { context: 32768, output: 32768 }, + }, + "arcee-ai/trinity-large-preview": { + id: "arcee-ai/trinity-large-preview", + name: "Arcee AI: Trinity Large Preview", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-01-28", + last_updated: "2026-05-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.45 }, + limit: { context: 131000, output: 32768 }, + }, + "deepcogito/cogito-v2.1-671b": { + id: "deepcogito/cogito-v2.1-671b", + name: "Deep Cogito: Cogito v2.1 671B", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-11-14", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.25, output: 1.25 }, + limit: { context: 128000, output: 32768 }, + }, + "upstage/solar-pro-3": { + id: "upstage/solar-pro-3", + name: "Upstage: Solar Pro 3", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-01-27", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 128000, output: 32768 }, + }, + "nex-agi/deepseek-v3.1-nex-n1": { + id: "nex-agi/deepseek-v3.1-nex-n1", + name: "Nex AGI: DeepSeek V3.1 Nex N1", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-01-01", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 1 }, + limit: { context: 131072, output: 163840 }, + }, + "bytedance-seed/seed-1.6": { + id: "bytedance-seed/seed-1.6", + name: "ByteDance Seed: Seed 1.6", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-09", + last_updated: "2025-09", + modalities: { input: ["image", "text", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2 }, + limit: { context: 262144, output: 32768 }, + }, + "bytedance-seed/seed-2.0-lite": { + id: "bytedance-seed/seed-2.0-lite", + name: "ByteDance Seed: Seed-2.0-Lite", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-10", + last_updated: "2026-03-15", + modalities: { input: ["image", "text", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 2 }, + limit: { context: 262144, output: 131072 }, + }, + "bytedance-seed/seed-1.6-flash": { + id: "bytedance-seed/seed-1.6-flash", + name: "ByteDance Seed: Seed 1.6 Flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-12-23", + last_updated: "2026-03-15", + modalities: { input: ["image", "text", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.075, output: 0.3 }, + limit: { context: 262144, output: 32768 }, + }, + "bytedance-seed/seed-2.0-mini": { + id: "bytedance-seed/seed-2.0-mini", + name: "ByteDance Seed: Seed-2.0-Mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-27", + last_updated: "2026-03-15", + modalities: { input: ["image", "text", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 262144, output: 131072 }, + }, + "mancer/weaver": { + id: "mancer/weaver", + name: "Mancer: Weaver (alpha)", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2023-08-02", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.75, output: 1 }, + limit: { context: 8000, output: 2000 }, + }, + "anthracite-org/magnum-v4-72b": { + id: "anthracite-org/magnum-v4-72b", + name: "Magnum v4 72B", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-10-22", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 3, output: 5 }, + limit: { context: 16384, output: 2048 }, + }, + "~google/gemini-pro-latest": { + id: "~google/gemini-pro-latest", + name: "Google: Gemini Pro Latest", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-27", + last_updated: "2026-05-01", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2, cache_write: 0.375 }, + limit: { context: 1048576, output: 65536 }, + }, + "~google/gemini-flash-latest": { + id: "~google/gemini-flash-latest", + name: "Google: Gemini Flash Latest", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-27", + last_updated: "2026-05-01", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 3, cache_read: 0.05, cache_write: 0.08333333333333334 }, + limit: { context: 1048576, output: 65536 }, + }, + "kilo-auto/balanced": { + id: "kilo-auto/balanced", + name: "Kilo Auto Balanced", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-15", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 3 }, + limit: { context: 204800, output: 131072 }, + }, + "kilo-auto/frontier": { + id: "kilo-auto/frontier", + name: "Kilo Auto Frontier", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-15", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25 }, + limit: { context: 1000000, output: 128000 }, + }, + "kilo-auto/small": { + id: "kilo-auto/small", + name: "Kilo Auto Small", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-15", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.4 }, + limit: { context: 400000, output: 128000 }, + }, + "kilo-auto/free": { + id: "kilo-auto/free", + name: "Kilo Auto Free", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-15", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "undi95/remm-slerp-l2-13b": { + id: "undi95/remm-slerp-l2-13b", + name: "ReMM SLERP 13B", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2023-07-22", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.45, output: 0.65 }, + limit: { context: 6144, output: 4096 }, + }, + "allenai/olmo-3-32b-think": { + id: "allenai/olmo-3-32b-think", + name: "AllenAI: Olmo 3 32B Think", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-11-22", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.5 }, + limit: { context: 65536, output: 65536 }, + }, + "nousresearch/hermes-2-pro-llama-3-8b": { + id: "nousresearch/hermes-2-pro-llama-3-8b", + name: "NousResearch: Hermes 2 Pro - Llama-3 8B", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-05-27", + last_updated: "2024-06-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.14 }, + limit: { context: 8192, output: 8192 }, + }, + "nousresearch/hermes-4-405b": { + id: "nousresearch/hermes-4-405b", + name: "Nous: Hermes 4 405B", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-08-25", + last_updated: "2025-08-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3 }, + limit: { context: 131072, output: 26215 }, + }, + "nousresearch/hermes-3-llama-3.1-70b": { + id: "nousresearch/hermes-3-llama-3.1-70b", + name: "Nous: Hermes 3 70B Instruct", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-08-18", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.3 }, + limit: { context: 131072, output: 32768 }, + }, + "nousresearch/hermes-4-70b": { + id: "nousresearch/hermes-4-70b", + name: "Nous: Hermes 4 70B", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-08-25", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.13, output: 0.4, cache_read: 0.055 }, + limit: { context: 131072, output: 131072 }, + }, + "nousresearch/hermes-3-llama-3.1-405b": { + id: "nousresearch/hermes-3-llama-3.1-405b", + name: "Nous: Hermes 3 405B Instruct", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-08-16", + last_updated: "2024-08-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 1 }, + limit: { context: 131072, output: 16384 }, + }, + "morph/morph-v3-fast": { + id: "morph/morph-v3-fast", + name: "Morph: Morph V3 Fast", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-08-15", + last_updated: "2024-08-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 1.2 }, + limit: { context: 81920, output: 38000 }, + }, + "morph/morph-v3-large": { + id: "morph/morph-v3-large", + name: "Morph: Morph V3 Large", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-08-15", + last_updated: "2024-08-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.9, output: 1.9 }, + limit: { context: 262144, output: 131072 }, + }, + "stepfun/step-3.5-flash:free": { + id: "stepfun/step-3.5-flash:free", + name: "StepFun: Step 3.5 Flash (free)", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-26", + last_updated: "2026-05-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 262144 }, + }, + "stepfun/step-3.5-flash": { + id: "stepfun/step-3.5-flash", + name: "StepFun: Step 3.5 Flash", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-01-29", + last_updated: "2026-01-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3, cache_read: 0.02 }, + limit: { context: 256000, output: 256000 }, + }, + "alpindale/goliath-120b": { + id: "alpindale/goliath-120b", + name: "Goliath 120B", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2023-11-10", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 3.75, output: 7.5 }, + limit: { context: 6144, output: 1024 }, + }, + "mistralai/mistral-nemo": { + id: "mistralai/mistral-nemo", + name: "Mistral: Mistral Nemo", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-07-01", + last_updated: "2024-07-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0.04 }, + limit: { context: 131072, output: 16384 }, + }, + "mistralai/mistral-saba": { + id: "mistralai/mistral-saba", + name: "Mistral: Saba", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-02-17", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.6 }, + limit: { context: 32768, output: 32768 }, + }, + "mistralai/mistral-large-2512": { + id: "mistralai/mistral-large-2512", + name: "Mistral: Mistral Large 3 2512", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-11-01", + last_updated: "2025-12-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 1.5 }, + limit: { context: 262144, output: 52429 }, + }, + "mistralai/devstral-medium": { + id: "mistralai/devstral-medium", + name: "Mistral: Devstral Medium", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-07-10", + last_updated: "2025-07-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 2 }, + limit: { context: 131072, output: 26215 }, + }, + "mistralai/mistral-small-3.1-24b-instruct": { + id: "mistralai/mistral-small-3.1-24b-instruct", + name: "Mistral: Mistral Small 3.1 24B", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-03-17", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.35, output: 0.56, cache_read: 0.015 }, + limit: { context: 128000, output: 131072 }, + }, + "mistralai/mistral-medium-3-5": { + id: "mistralai/mistral-medium-3-5", + name: "Mistral: Mistral Medium 3.5", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-30", + last_updated: "2026-05-07", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.5, output: 7.5 }, + limit: { context: 262144, output: 262144 }, + }, + "mistralai/pixtral-large-2411": { + id: "mistralai/pixtral-large-2411", + name: "Mistral: Pixtral Large 2411", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-11-19", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: true, + cost: { input: 2, output: 6 }, + limit: { context: 131072, output: 32768 }, + }, + "mistralai/devstral-2512": { + id: "mistralai/devstral-2512", + name: "Mistral: Devstral 2 2512", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-09-12", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 2, cache_read: 0.025 }, + limit: { context: 262144, output: 65536 }, + }, + "mistralai/codestral-2508": { + id: "mistralai/codestral-2508", + name: "Mistral: Codestral 2508", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-08-01", + last_updated: "2025-08-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.9 }, + limit: { context: 256000, output: 51200 }, + }, + "mistralai/mistral-small-24b-instruct-2501": { + id: "mistralai/mistral-small-24b-instruct-2501", + name: "Mistral: Mistral Small 3", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-12-29", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.08 }, + limit: { context: 32768, output: 16384 }, + }, + "mistralai/mistral-large-2411": { + id: "mistralai/mistral-large-2411", + name: "Mistral Large 2411", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-07-24", + last_updated: "2024-11-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2, output: 6 }, + limit: { context: 131072, output: 26215 }, + }, + "mistralai/mixtral-8x22b-instruct": { + id: "mistralai/mixtral-8x22b-instruct", + name: "Mistral: Mixtral 8x22B Instruct", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-04-17", + last_updated: "2024-04-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2, output: 6 }, + limit: { context: 65536, output: 13108 }, + }, + "mistralai/mistral-large-2407": { + id: "mistralai/mistral-large-2407", + name: "Mistral Large 2407", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-11-19", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2, output: 6 }, + limit: { context: 131072, output: 32768 }, + }, + "mistralai/ministral-8b-2512": { + id: "mistralai/ministral-8b-2512", + name: "Mistral: Ministral 3 8B 2512", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-12-02", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.15 }, + limit: { context: 262144, output: 32768 }, + }, + "mistralai/mistral-medium-3.1": { + id: "mistralai/mistral-medium-3.1", + name: "Mistral: Mistral Medium 3.1", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-08-12", + last_updated: "2025-08-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2 }, + limit: { context: 131072, output: 26215 }, + }, + "mistralai/mistral-small-2603": { + id: "mistralai/mistral-small-2603", + name: "Mistral: Mistral Small 4", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-16", + last_updated: "2026-04-11", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6, cache_read: 0.015 }, + limit: { context: 262144, output: 262144 }, + }, + "mistralai/ministral-3b-2512": { + id: "mistralai/ministral-3b-2512", + name: "Mistral: Ministral 3 3B 2512", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-12-02", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 131072, output: 32768 }, + }, + "mistralai/voxtral-small-24b-2507": { + id: "mistralai/voxtral-small-24b-2507", + name: "Mistral: Voxtral Small 24B 2507", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-07-01", + last_updated: "2025-07-01", + modalities: { input: ["text", "audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 32000, output: 6400 }, + }, + "mistralai/mixtral-8x7b-instruct": { + id: "mistralai/mixtral-8x7b-instruct", + name: "Mistral: Mixtral 8x7B Instruct", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2023-12-10", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.54, output: 0.54 }, + limit: { context: 32768, output: 16384 }, + }, + "mistralai/mistral-medium-3": { + id: "mistralai/mistral-medium-3", + name: "Mistral: Mistral Medium 3", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-05-07", + last_updated: "2025-05-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2 }, + limit: { context: 131072, output: 26215 }, + }, + "mistralai/mistral-small-3.2-24b-instruct": { + id: "mistralai/mistral-small-3.2-24b-instruct", + name: "Mistral: Mistral Small 3.2 24B", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-06-20", + last_updated: "2025-06-20", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.06, output: 0.18, cache_read: 0.03 }, + limit: { context: 131072, output: 131072 }, + }, + "mistralai/devstral-small": { + id: "mistralai/devstral-small", + name: "Mistral: Devstral Small 1.1", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-05-07", + last_updated: "2025-07-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 131072, output: 26215 }, + }, + "mistralai/mistral-large": { + id: "mistralai/mistral-large", + name: "Mistral Large", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-07-24", + last_updated: "2025-12-02", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2, output: 6 }, + limit: { context: 128000, output: 25600 }, + }, + "mistralai/mistral-7b-instruct-v0.1": { + id: "mistralai/mistral-7b-instruct-v0.1", + name: "Mistral: Mistral 7B Instruct v0.1", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.11, output: 0.19 }, + limit: { context: 2824, output: 565 }, + }, + "mistralai/ministral-14b-2512": { + id: "mistralai/ministral-14b-2512", + name: "Mistral: Ministral 3 14B 2512", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-12-16", + last_updated: "2025-12-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 262144, output: 52429 }, + }, + "~anthropic/claude-haiku-latest": { + id: "~anthropic/claude-haiku-latest", + name: "Anthropic: Claude Haiku Latest", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-27", + last_updated: "2026-05-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + }, + "~anthropic/claude-sonnet-latest": { + id: "~anthropic/claude-sonnet-latest", + name: "Anthropic: Claude Sonnet Latest", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-27", + last_updated: "2026-05-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 1000000, output: 128000 }, + }, + "~anthropic/claude-opus-latest": { + id: "~anthropic/claude-opus-latest", + name: "Anthropic: Claude Opus Latest", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-04-16", + last_updated: "2026-05-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "meta-llama/llama-3.3-70b-instruct": { + id: "meta-llama/llama-3.3-70b-instruct", + name: "Meta: Llama 3.3 70B Instruct", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-08-01", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.32 }, + limit: { context: 131072, output: 16384 }, + }, + "meta-llama/llama-4-scout": { + id: "meta-llama/llama-4-scout", + name: "Meta: Llama 4 Scout", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.08, output: 0.3 }, + limit: { context: 327680, output: 16384 }, + }, + "meta-llama/llama-guard-3-8b": { + id: "meta-llama/llama-guard-3-8b", + name: "Llama Guard 3 8B", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-04-18", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0.06 }, + limit: { context: 131072, output: 26215 }, + }, + "meta-llama/llama-4-maverick": { + id: "meta-llama/llama-4-maverick", + name: "Meta: Llama 4 Maverick", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-04-05", + last_updated: "2025-12-24", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 1048576, output: 16384 }, + }, + "meta-llama/llama-3.2-11b-vision-instruct": { + id: "meta-llama/llama-3.2-11b-vision-instruct", + name: "Meta: Llama 3.2 11B Vision Instruct", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-09-25", + last_updated: "2024-09-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.049, output: 0.049 }, + limit: { context: 131072, output: 16384 }, + }, + "meta-llama/llama-guard-4-12b": { + id: "meta-llama/llama-guard-4-12b", + name: "Meta: Llama Guard 4 12B", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.18, output: 0.18 }, + limit: { context: 163840, output: 32768 }, + }, + "meta-llama/llama-3.1-70b-instruct": { + id: "meta-llama/llama-3.1-70b-instruct", + name: "Meta: Llama 3.1 70B Instruct", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-07-16", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 0.4 }, + limit: { context: 131072, output: 26215 }, + }, + "meta-llama/llama-3.2-1b-instruct": { + id: "meta-llama/llama-3.2-1b-instruct", + name: "Meta: Llama 3.2 1B Instruct", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-09-18", + last_updated: "2026-01-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.027, output: 0.2 }, + limit: { context: 60000, output: 12000 }, + }, + "meta-llama/llama-3.2-3b-instruct": { + id: "meta-llama/llama-3.2-3b-instruct", + name: "Meta: Llama 3.2 3B Instruct", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-09-18", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.051, output: 0.34 }, + limit: { context: 80000, output: 16384 }, + }, + "meta-llama/llama-3-8b-instruct": { + id: "meta-llama/llama-3-8b-instruct", + name: "Meta: Llama 3 8B Instruct", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-04-25", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.04 }, + limit: { context: 8192, output: 16384 }, + }, + "meta-llama/llama-3.1-8b-instruct": { + id: "meta-llama/llama-3.1-8b-instruct", + name: "Meta: Llama 3.1 8B Instruct", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-07-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0.05 }, + limit: { context: 16384, output: 16384 }, + }, + "meta-llama/llama-3-70b-instruct": { + id: "meta-llama/llama-3-70b-instruct", + name: "Meta: Llama 3 70B Instruct", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.51, output: 0.74 }, + limit: { context: 8192, output: 8000 }, + }, + "x-ai/grok-4.20": { + id: "x-ai/grok-4.20", + name: "xAI: Grok 4.20", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-31", + last_updated: "2026-04-11", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6, cache_read: 0.2 }, + limit: { context: 2000000, output: 2000000 }, + }, + "x-ai/grok-code-fast-1:optimized:free": { + id: "x-ai/grok-code-fast-1:optimized:free", + name: "xAI: Grok Code Fast 1 Optimized (experimental, free)", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-27", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 256000, output: 10000 }, + }, + "x-ai/grok-4.3": { + id: "x-ai/grok-4.3", + name: "xAI: Grok 4.3", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-05-01", + last_updated: "2026-05-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 2.5, cache_read: 0.2 }, + limit: { context: 1000000, output: 4096 }, + }, + "x-ai/grok-4-fast": { + id: "x-ai/grok-4-fast", + name: "xAI: Grok 4 Fast", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-19", + last_updated: "2025-08-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + "x-ai/grok-code-fast-1": { + id: "x-ai/grok-code-fast-1", + name: "xAI: Grok Code Fast 1", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-26", + last_updated: "2025-08-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.5, cache_read: 0.02 }, + limit: { context: 256000, output: 10000 }, + }, + "x-ai/grok-3-beta": { + id: "x-ai/grok-3-beta", + name: "xAI: Grok 3 Beta", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.75 }, + limit: { context: 131072, output: 26215 }, + }, + "x-ai/grok-4": { + id: "x-ai/grok-4", + name: "xAI: Grok 4", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.75 }, + limit: { context: 256000, output: 51200 }, + }, + "x-ai/grok-3-mini": { + id: "x-ai/grok-3-mini", + name: "xAI: Grok 3 Mini", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.5, cache_read: 0.075 }, + limit: { context: 131072, output: 26215 }, + }, + "x-ai/grok-4.1-fast": { + id: "x-ai/grok-4.1-fast", + name: "xAI: Grok 4.1 Fast", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-11-19", + last_updated: "2025-11-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + "x-ai/grok-3-mini-beta": { + id: "x-ai/grok-3-mini-beta", + name: "xAI: Grok 3 Mini Beta", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.5, cache_read: 0.075 }, + limit: { context: 131072, output: 26215 }, + }, + "x-ai/grok-4.20-multi-agent": { + id: "x-ai/grok-4.20-multi-agent", + name: "xAI: Grok 4.20 Multi-Agent", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2026-03-31", + last_updated: "2026-04-11", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6, cache_read: 0.2 }, + limit: { context: 2000000, output: 2000000 }, + }, + "x-ai/grok-3": { + id: "x-ai/grok-3", + name: "xAI: Grok 3", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.75 }, + limit: { context: 131072, output: 26215 }, + }, + "tencent/hy3-preview:free": { + id: "tencent/hy3-preview:free", + name: "Tencent: Hy3 Preview (free)", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-22", + last_updated: "2026-05-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 262144 }, + }, + "tencent/hunyuan-a13b-instruct": { + id: "tencent/hunyuan-a13b-instruct", + name: "Tencent: Hunyuan A13B Instruct", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-06-30", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.57 }, + limit: { context: 131072, output: 131072 }, + }, + "gryphe/mythomax-l2-13b": { + id: "gryphe/mythomax-l2-13b", + name: "MythoMax 13B", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-04-25", + last_updated: "2024-04-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.06, output: 0.06 }, + limit: { context: 4096, output: 4096 }, + }, + "sao10k/l3-euryale-70b": { + id: "sao10k/l3-euryale-70b", + name: "Sao10k: Llama 3 Euryale 70B v2.1", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-06-18", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.48, output: 1.48 }, + limit: { context: 8192, output: 8192 }, + }, + "sao10k/l3-lunaris-8b": { + id: "sao10k/l3-lunaris-8b", + name: "Sao10K: Llama 3 8B Lunaris", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-08-13", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.04, output: 0.05 }, + limit: { context: 8192, output: 8192 }, + }, + "sao10k/l3.3-euryale-70b": { + id: "sao10k/l3.3-euryale-70b", + name: "Sao10K: Llama 3.3 Euryale 70B", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-12-18", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.65, output: 0.75 }, + limit: { context: 131072, output: 16384 }, + }, + "sao10k/l3.1-70b-hanami-x1": { + id: "sao10k/l3.1-70b-hanami-x1", + name: "Sao10K: Llama 3.1 70B Hanami x1", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-01-08", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 3, output: 3 }, + limit: { context: 16000, output: 16000 }, + }, + "sao10k/l3.1-euryale-70b": { + id: "sao10k/l3.1-euryale-70b", + name: "Sao10K: Llama 3.1 Euryale 70B v2.2", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-08-28", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.85, output: 0.85 }, + limit: { context: 131072, output: 16384 }, + }, + "microsoft/wizardlm-2-8x22b": { + id: "microsoft/wizardlm-2-8x22b", + name: "WizardLM-2 8x22B", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-04-24", + last_updated: "2024-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.62, output: 0.62 }, + limit: { context: 65535, output: 8000 }, + }, + "microsoft/phi-4-mini-instruct": { + id: "microsoft/phi-4-mini-instruct", + name: "Microsoft: Phi 4 Mini Instruct", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-10-17", + last_updated: "2026-05-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.08, output: 0.35, cache_read: 0.08 }, + limit: { context: 128000, output: 128000 }, + }, + "microsoft/phi-4": { + id: "microsoft/phi-4", + name: "Microsoft: Phi 4", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.06, output: 0.14 }, + limit: { context: 16384, output: 16384 }, + }, + "poolside/laguna-m.1:free": { + id: "poolside/laguna-m.1:free", + name: "Poolside: Laguna M.1 (free)", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-28", + last_updated: "2026-05-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 8192 }, + }, + "poolside/laguna-xs.2:free": { + id: "poolside/laguna-xs.2:free", + name: "Poolside: Laguna XS.2 (free)", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-28", + last_updated: "2026-05-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 8192 }, + }, + "cohere/command-r7b-12-2024": { + id: "cohere/command-r7b-12-2024", + name: "Cohere: Command R7B (12-2024)", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-02-27", + last_updated: "2024-02-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.0375, output: 0.15 }, + limit: { context: 128000, output: 4000 }, + }, + "cohere/command-a": { + id: "cohere/command-a", + name: "Cohere: Command A", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-03-13", + last_updated: "2025-03-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.5, output: 10 }, + limit: { context: 256000, output: 8192 }, + }, + "cohere/command-r-plus-08-2024": { + id: "cohere/command-r-plus-08-2024", + name: "Cohere: Command R+ (08-2024)", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-08-30", + last_updated: "2024-08-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.5, output: 10 }, + limit: { context: 128000, output: 4000 }, + }, + "cohere/command-r-08-2024": { + id: "cohere/command-r-08-2024", + name: "Cohere: Command R (08-2024)", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-08-30", + last_updated: "2024-08-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 128000, output: 4000 }, + }, + "prime-intellect/intellect-3": { + id: "prime-intellect/intellect-3", + name: "Prime Intellect: INTELLECT-3", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-11-26", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 1.1 }, + limit: { context: 131072, output: 131072 }, + }, + "nvidia/llama-3.3-nemotron-super-49b-v1.5": { + id: "nvidia/llama-3.3-nemotron-super-49b-v1.5", + name: "NVIDIA: Llama 3.3 Nemotron Super 49B V1.5", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-03-16", + last_updated: "2025-03-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 131072, output: 26215 }, + }, + "nvidia/nemotron-3-super-120b-a12b": { + id: "nvidia/nemotron-3-super-120b-a12b", + name: "NVIDIA: Nemotron 3 Super", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-11", + last_updated: "2026-04-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.5, cache_read: 0.1 }, + limit: { context: 262144, output: 262144 }, + }, + "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free": { + id: "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free", + name: "NVIDIA: Nemotron 3 Nano Omni (free)", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-28", + last_updated: "2026-05-01", + modalities: { input: ["text", "audio", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 256000, output: 65536 }, + }, + "nvidia/nemotron-3-nano-30b-a3b": { + id: "nvidia/nemotron-3-nano-30b-a3b", + name: "NVIDIA: Nemotron 3 Nano 30B A3B", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2024-12", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.2 }, + limit: { context: 262144, output: 52429 }, + }, + "nvidia/nemotron-3-super-120b-a12b:free": { + id: "nvidia/nemotron-3-super-120b-a12b:free", + name: "NVIDIA: Nemotron 3 Super (free)", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-12", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 262144 }, + }, + "nvidia/nemotron-nano-9b-v2": { + id: "nvidia/nemotron-nano-9b-v2", + name: "NVIDIA: Nemotron Nano 9B V2", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-18", + last_updated: "2025-08-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.04, output: 0.16 }, + limit: { context: 131072, output: 26215 }, + }, + "nvidia/llama-3.1-nemotron-70b-instruct": { + id: "nvidia/llama-3.1-nemotron-70b-instruct", + name: "NVIDIA: Llama 3.1 Nemotron 70B Instruct", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-10-12", + last_updated: "2024-10-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.2, output: 1.2 }, + limit: { context: 131072, output: 16384 }, + }, + "inception/mercury-2": { + id: "inception/mercury-2", + name: "Inception: Mercury 2", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-24", + last_updated: "2026-02-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 0.75, cache_read: 0.025 }, + limit: { context: 128000, output: 50000 }, + }, + "openai/gpt-5.1-codex-max": { + id: "openai/gpt-5.1-codex-max", + name: "OpenAI: GPT-5.1-Codex-Max", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.2-chat": { + id: "openai/gpt-5.2-chat", + name: "OpenAI: GPT-5.2 Chat", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-12-11", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-4o-mini-search-preview": { + id: "openai/gpt-4o-mini-search-preview", + name: "OpenAI: GPT-4o-mini Search Preview", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-01", + last_updated: "2025-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-5-chat": { + id: "openai/gpt-5-chat", + name: "OpenAI: GPT-5 Chat", + attachment: true, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-08-07", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-4o-2024-05-13": { + id: "openai/gpt-4o-2024-05-13", + name: "OpenAI: GPT-4o (2024-05-13)", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-05-13", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 15 }, + limit: { context: 128000, output: 4096 }, + }, + "openai/gpt-5.3-chat": { + id: "openai/gpt-5.3-chat", + name: "OpenAI: GPT-5.3 Chat", + attachment: true, + reasoning: false, + tool_call: true, + release_date: "2026-03-04", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-5.2-pro": { + id: "openai/gpt-5.2-pro", + name: "OpenAI: GPT-5.2 Pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-12-11", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 21, output: 168 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-4-1106-preview": { + id: "openai/gpt-4-1106-preview", + name: "OpenAI: GPT-4 Turbo (older v1106)", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2023-11-06", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 10, output: 30 }, + limit: { context: 128000, output: 4096 }, + }, + "openai/gpt-chat-latest": { + id: "openai/gpt-chat-latest", + name: "OpenAI: GPT Chat Latest", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + release_date: "2026-05-05", + last_updated: "2026-05-07", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 30, cache_read: 0.5 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-4o-audio-preview": { + id: "openai/gpt-4o-audio-preview", + name: "OpenAI: GPT-4o Audio", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-08-15", + last_updated: "2026-03-15", + modalities: { input: ["audio", "text"], output: ["audio", "text"] }, + open_weights: false, + cost: { input: 2.5, output: 10 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-5.5": { + id: "openai/gpt-5.5", + name: "OpenAI: GPT-5.5", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-04-24", + last_updated: "2026-05-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 30, cache_read: 0.5 }, + limit: { context: 1050000, output: 128000 }, + }, + "openai/gpt-5-mini": { + id: "openai/gpt-5-mini", + name: "OpenAI: GPT-5 Mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-08-07", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.025 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5-nano": { + id: "openai/gpt-5-nano", + name: "OpenAI: GPT-5 Nano", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-08-07", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.4, cache_read: 0.005 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.3-codex": { + id: "openai/gpt-5.3-codex", + name: "OpenAI: GPT-5.3-Codex", + attachment: true, + reasoning: true, + tool_call: true, + release_date: "2026-02-25", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-3.5-turbo-16k": { + id: "openai/gpt-3.5-turbo-16k", + name: "OpenAI: GPT-3.5 Turbo 16k", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2023-08-28", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 4 }, + limit: { context: 16385, output: 4096 }, + }, + "openai/gpt-4-turbo": { + id: "openai/gpt-4-turbo", + name: "OpenAI: GPT-4 Turbo", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2023-09-13", + last_updated: "2024-04-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 10, output: 30 }, + limit: { context: 128000, output: 4096 }, + }, + "openai/gpt-5.2": { + id: "openai/gpt-5.2", + name: "OpenAI: GPT-5.2", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-12-11", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/o3-pro": { + id: "openai/o3-pro", + name: "OpenAI: o3 Pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-04-16", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 20, output: 80 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/o3-mini-high": { + id: "openai/o3-mini-high", + name: "OpenAI: o3 Mini High", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-01-31", + last_updated: "2026-03-15", + modalities: { input: ["pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.55 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-4o-mini": { + id: "openai/gpt-4o-mini", + name: "OpenAI: GPT-4o-mini", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-07-18", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6, cache_read: 0.075 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/o4-mini-deep-research": { + id: "openai/o4-mini-deep-research", + name: "OpenAI: o4 Mini Deep Research", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2024-06-26", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-5.4-mini": { + id: "openai/gpt-5.4-mini", + name: "OpenAI: GPT-5.4 Mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-03-17", + last_updated: "2026-04-11", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.75, output: 4.5, cache_read: 0.075 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.1-chat": { + id: "openai/gpt-5.1-chat", + name: "OpenAI: GPT-5.1 Chat", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-11-13", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/o4-mini": { + id: "openai/o4-mini", + name: "OpenAI: o4 Mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-04-16", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.275 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-5.4-nano": { + id: "openai/gpt-5.4-nano", + name: "OpenAI: GPT-5.4 Nano", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-03-17", + last_updated: "2026-04-11", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.25, cache_read: 0.02 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.2-codex": { + id: "openai/gpt-5.2-codex", + name: "OpenAI: GPT-5.2-Codex", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-01-14", + last_updated: "2026-01-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-4o-mini-2024-07-18": { + id: "openai/gpt-4o-mini-2024-07-18", + name: "OpenAI: GPT-4o-mini (2024-07-18)", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-07-18", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-5.1-codex-mini": { + id: "openai/gpt-5.1-codex-mini", + name: "OpenAI: GPT-5.1-Codex-Mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.025 }, + limit: { context: 400000, output: 100000 }, + }, + "openai/gpt-4o-2024-08-06": { + id: "openai/gpt-4o-2024-08-06", + name: "OpenAI: GPT-4o (2024-08-06)", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-08-06", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10, cache_read: 1.25 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-5-image": { + id: "openai/gpt-5-image", + name: "OpenAI: GPT-5 Image", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-10-14", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["image", "text"] }, + open_weights: false, + cost: { input: 10, output: 10 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.1": { + id: "openai/gpt-5.1", + name: "OpenAI: GPT-5.1", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-11-13", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/o1": { + id: "openai/o1", + name: "OpenAI: o1", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-12-05", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 60, cache_read: 7.5 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-5.4-pro": { + id: "openai/gpt-5.4-pro", + name: "OpenAI: GPT-5.4 Pro", + attachment: true, + reasoning: true, + tool_call: true, + release_date: "2026-03-06", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 180 }, + limit: { context: 1050000, output: 128000 }, + }, + "openai/gpt-3.5-turbo": { + id: "openai/gpt-3.5-turbo", + name: "OpenAI: GPT-3.5 Turbo", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2023-03-01", + last_updated: "2023-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 1.5 }, + limit: { context: 16385, output: 4096 }, + }, + "openai/o3-deep-research": { + id: "openai/o3-deep-research", + name: "OpenAI: o3 Deep Research", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2024-06-26", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 10, output: 40, cache_read: 2.5 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/o3-mini": { + id: "openai/o3-mini", + name: "OpenAI: o3 Mini", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-12-20", + last_updated: "2026-03-15", + modalities: { input: ["pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.55 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-4-turbo-preview": { + id: "openai/gpt-4-turbo-preview", + name: "OpenAI: GPT-4 Turbo Preview", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-01-25", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 10, output: 30 }, + limit: { context: 128000, output: 4096 }, + }, + "openai/o1-pro": { + id: "openai/o1-pro", + name: "OpenAI: o1-pro", + attachment: true, + reasoning: true, + tool_call: false, + temperature: false, + release_date: "2025-03-19", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 150, output: 600 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-5.4-image-2": { + id: "openai/gpt-5.4-image-2", + name: "OpenAI: GPT-5.4 Image 2", + attachment: true, + reasoning: true, + tool_call: false, + temperature: false, + release_date: "2026-04-21", + last_updated: "2026-05-01", + modalities: { input: ["image", "text", "pdf"], output: ["image", "text"] }, + open_weights: false, + cost: { input: 8, output: 15, cache_read: 2 }, + limit: { context: 272000, output: 128000 }, + }, + "openai/gpt-4": { + id: "openai/gpt-4", + name: "OpenAI: GPT-4", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2023-03-14", + last_updated: "2024-04-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 60 }, + limit: { context: 8191, output: 4096 }, + }, + "openai/gpt-4-0314": { + id: "openai/gpt-4-0314", + name: "OpenAI: GPT-4 (older v0314)", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2023-05-28", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 60 }, + limit: { context: 8191, output: 4096 }, + }, + "openai/gpt-5-codex": { + id: "openai/gpt-5-codex", + name: "OpenAI: GPT-5 Codex", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-09-15", + last_updated: "2025-09-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.4": { + id: "openai/gpt-5.4", + name: "OpenAI: GPT-5.4", + attachment: true, + reasoning: true, + tool_call: true, + release_date: "2026-03-06", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 15 }, + limit: { context: 1050000, output: 128000 }, + }, + "openai/gpt-audio": { + id: "openai/gpt-audio", + name: "OpenAI: GPT Audio", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2026-01-20", + last_updated: "2026-03-15", + modalities: { input: ["audio", "text"], output: ["audio", "text"] }, + open_weights: false, + cost: { input: 2.5, output: 10 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-4o-search-preview": { + id: "openai/gpt-4o-search-preview", + name: "OpenAI: GPT-4o Search Preview", + attachment: false, + reasoning: false, + tool_call: false, + release_date: "2025-03-13", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-4.1-nano": { + id: "openai/gpt-4.1-nano", + name: "OpenAI: GPT-4.1 Nano", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-04-14", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.025 }, + limit: { context: 1047576, output: 32768 }, + }, + "openai/o4-mini-high": { + id: "openai/o4-mini-high", + name: "OpenAI: o4 Mini High", + attachment: true, + reasoning: true, + tool_call: true, + release_date: "2025-04-17", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/o3": { + id: "openai/o3", + name: "OpenAI: o3", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-04-16", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-oss-20b": { + id: "openai/gpt-oss-20b", + name: "OpenAI: gpt-oss-20b", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.14 }, + limit: { context: 131072, output: 26215 }, + }, + "openai/gpt-5-pro": { + id: "openai/gpt-5-pro", + name: "OpenAI: GPT-5 Pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-10-06", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 120 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-audio-mini": { + id: "openai/gpt-audio-mini", + name: "OpenAI: GPT Audio Mini", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2026-01-20", + last_updated: "2026-03-15", + modalities: { input: ["audio", "text"], output: ["audio", "text"] }, + open_weights: false, + cost: { input: 0.6, output: 2.4 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-4o": { + id: "openai/gpt-4o", + name: "OpenAI: GPT-4o", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-05-13", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10, cache_read: 1.25 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-3.5-turbo-0613": { + id: "openai/gpt-3.5-turbo-0613", + name: "OpenAI: GPT-3.5 Turbo (older v0613)", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2023-06-13", + last_updated: "2023-06-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 2 }, + limit: { context: 4095, output: 4096 }, + }, + "openai/gpt-5-image-mini": { + id: "openai/gpt-5-image-mini", + name: "OpenAI: GPT-5 Image Mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-10-16", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["image", "text"] }, + open_weights: false, + cost: { input: 2.5, output: 2 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5": { + id: "openai/gpt-5", + name: "OpenAI: GPT-5", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-08-07", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-oss-safeguard-20b": { + id: "openai/gpt-oss-safeguard-20b", + name: "OpenAI: gpt-oss-safeguard-20b", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-10-29", + last_updated: "2025-10-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.075, output: 0.3, cache_read: 0.037 }, + limit: { context: 131072, output: 65536 }, + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "OpenAI: gpt-oss-120b", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.039, output: 0.19 }, + limit: { context: 131072, output: 26215 }, + }, + "openai/gpt-5.5-pro": { + id: "openai/gpt-5.5-pro", + name: "OpenAI: GPT-5.5 Pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-04-24", + last_updated: "2026-05-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 180 }, + limit: { context: 1050000, output: 128000 }, + }, + "openai/gpt-3.5-turbo-instruct": { + id: "openai/gpt-3.5-turbo-instruct", + name: "OpenAI: GPT-3.5 Turbo Instruct", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2023-03-01", + last_updated: "2023-09-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.5, output: 2 }, + limit: { context: 4095, output: 4096 }, + }, + "openai/gpt-4.1": { + id: "openai/gpt-4.1", + name: "OpenAI: GPT-4.1", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-04-14", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 1047576, output: 32768 }, + }, + "openai/gpt-4.1-mini": { + id: "openai/gpt-4.1-mini", + name: "OpenAI: GPT-4.1 Mini", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-04-14", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.6, cache_read: 0.1 }, + limit: { context: 1047576, output: 32768 }, + }, + "openai/gpt-5.1-codex": { + id: "openai/gpt-5.1-codex", + name: "OpenAI: GPT-5.1-Codex", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-4o-2024-11-20": { + id: "openai/gpt-4o-2024-11-20", + name: "OpenAI: GPT-4o (2024-11-20)", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-11-20", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10, cache_read: 1.25 }, + limit: { context: 128000, output: 16384 }, + }, + "amazon/nova-lite-v1": { + id: "amazon/nova-lite-v1", + name: "Amazon: Nova Lite 1.0", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-12-06", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.06, output: 0.24 }, + limit: { context: 300000, output: 5120 }, + }, + "amazon/nova-pro-v1": { + id: "amazon/nova-pro-v1", + name: "Amazon: Nova Pro 1.0", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-12-03", + last_updated: "2024-12-03", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 3.2 }, + limit: { context: 300000, output: 5120 }, + }, + "amazon/nova-premier-v1": { + id: "amazon/nova-premier-v1", + name: "Amazon: Nova Premier 1.0", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-11-01", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 12.5 }, + limit: { context: 1000000, output: 32000 }, + }, + "amazon/nova-2-lite-v1": { + id: "amazon/nova-2-lite-v1", + name: "Amazon: Nova 2 Lite", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2024-12-01", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5 }, + limit: { context: 1000000, output: 65535 }, + }, + "amazon/nova-micro-v1": { + id: "amazon/nova-micro-v1", + name: "Amazon: Nova Micro 1.0", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-12-06", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.035, output: 0.14 }, + limit: { context: 128000, output: 5120 }, + }, + "z-ai/glm-5v-turbo": { + id: "z-ai/glm-5v-turbo", + name: "Z.ai: GLM 5V Turbo", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-01", + last_updated: "2026-04-11", + modalities: { input: ["image", "text", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 1.2, output: 4, cache_read: 0.24 }, + limit: { context: 202752, output: 131072 }, + }, + "z-ai/glm-4.7": { + id: "z-ai/glm-4.7", + name: "Z.ai: GLM 4.7", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-12-22", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.38, output: 1.98, cache_read: 0.2 }, + limit: { context: 202752, output: 65535 }, + }, + "z-ai/glm-5": { + id: "z-ai/glm-5", + name: "Z.ai: GLM 5", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.72, output: 2.3 }, + limit: { context: 202752, output: 131072 }, + }, + "z-ai/glm-4-32b": { + id: "z-ai/glm-4-32b", + name: "Z.ai: GLM 4 32B ", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-07-25", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 128000, output: 32768 }, + }, + "z-ai/glm-5.1": { + id: "z-ai/glm-5.1", + name: "Z.ai: GLM 5.1", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-27", + last_updated: "2026-03-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.26, output: 3.96 }, + limit: { context: 202752, output: 131072 }, + }, + "z-ai/glm-4.5": { + id: "z-ai/glm-4.5", + name: "Z.ai: GLM 4.5", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-07-28", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2, cache_read: 0.175 }, + limit: { context: 131072, output: 98304 }, + }, + "z-ai/glm-4.5-air": { + id: "z-ai/glm-4.5-air", + name: "Z.ai: GLM 4.5 Air", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.13, output: 0.85, cache_read: 0.025 }, + limit: { context: 131072, output: 98304 }, + }, + "z-ai/glm-5-turbo": { + id: "z-ai/glm-5-turbo", + name: "Z.ai: GLM 5 Turbo", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-15", + last_updated: "2026-04-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.2, output: 4, cache_read: 0.24 }, + limit: { context: 202752, output: 131072 }, + }, + "z-ai/glm-4.5v": { + id: "z-ai/glm-4.5v", + name: "Z.ai: GLM 4.5V", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-11", + last_updated: "2025-08-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 1.8, cache_read: 0.11 }, + limit: { context: 65536, output: 16384 }, + }, + "z-ai/glm-4.6": { + id: "z-ai/glm-4.6", + name: "Z.ai: GLM 4.6", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-09-30", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.39, output: 1.9, cache_read: 0.175 }, + limit: { context: 204800, output: 204800 }, + }, + "z-ai/glm-4.6v": { + id: "z-ai/glm-4.6v", + name: "Z.ai: GLM 4.6V", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-09-30", + last_updated: "2026-01-10", + modalities: { input: ["image", "text", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.9 }, + limit: { context: 131072, output: 131072 }, + }, + "z-ai/glm-4.7-flash": { + id: "z-ai/glm-4.7-flash", + name: "Z.ai: GLM 4.7 Flash", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-01-19", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.06, output: 0.4, cache_read: 0.01 }, + limit: { context: 202752, output: 40551 }, + }, + "baidu/ernie-4.5-vl-424b-a47b": { + id: "baidu/ernie-4.5-vl-424b-a47b", + name: "Baidu: ERNIE 4.5 VL 424B A47B ", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-06-30", + last_updated: "2026-01", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.42, output: 1.25 }, + limit: { context: 123000, output: 16000 }, + }, + "baidu/qianfan-ocr-fast:free": { + id: "baidu/qianfan-ocr-fast:free", + name: "Baidu: Qianfan-OCR-Fast (free)", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2026-04-20", + last_updated: "2026-05-01", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 65536, output: 28672 }, + }, + "baidu/cobuddy:free": { + id: "baidu/cobuddy:free", + name: "Baidu: CoBuddy (free)", + attachment: false, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-05-06", + last_updated: "2026-05-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 65536 }, + }, + "baidu/ernie-4.5-vl-28b-a3b": { + id: "baidu/ernie-4.5-vl-28b-a3b", + name: "Baidu: ERNIE 4.5 VL 28B A3B", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-06-30", + last_updated: "2025-06-30", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.56 }, + limit: { context: 30000, output: 8000 }, + }, + "baidu/ernie-4.5-21b-a3b": { + id: "baidu/ernie-4.5-21b-a3b", + name: "Baidu: ERNIE 4.5 21B A3B", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-06-30", + last_updated: "2025-06-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.28 }, + limit: { context: 120000, output: 8000 }, + }, + "baidu/ernie-4.5-300b-a47b": { + id: "baidu/ernie-4.5-300b-a47b", + name: "Baidu: ERNIE 4.5 300B A47B ", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-06-30", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.28, output: 1.1 }, + limit: { context: 123000, output: 12000 }, + }, + "baidu/ernie-4.5-21b-a3b-thinking": { + id: "baidu/ernie-4.5-21b-a3b-thinking", + name: "Baidu: ERNIE 4.5 21B A3B Thinking", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-09-19", + last_updated: "2025-09-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.28 }, + limit: { context: 131072, output: 65536 }, + }, + "relace/relace-apply-3": { + id: "relace/relace-apply-3", + name: "Relace: Relace Apply 3", + attachment: false, + reasoning: false, + tool_call: false, + release_date: "2025-09-26", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.85, output: 1.25 }, + limit: { context: 256000, output: 128000 }, + }, + "relace/relace-search": { + id: "relace/relace-search", + name: "Relace: Relace Search", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-12-09", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 3 }, + limit: { context: 256000, output: 128000 }, + }, + "minimax/minimax-m2.7": { + id: "minimax/minimax-m2.7", + name: "MiniMax: MiniMax M2.7", + family: "minimax-m2.7", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.06 }, + limit: { context: 204800, output: 131072 }, + }, + "minimax/minimax-m2": { + id: "minimax/minimax-m2", + name: "MiniMax: MiniMax M2", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-10-23", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.255, output: 1, cache_read: 0.03 }, + limit: { context: 196608, output: 196608 }, + }, + "minimax/minimax-01": { + id: "minimax/minimax-01", + name: "MiniMax: MiniMax-01", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-01-15", + last_updated: "2025-01-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 1.1 }, + limit: { context: 1000192, output: 1000192 }, + }, + "minimax/minimax-m2.1": { + id: "minimax/minimax-m2.1", + name: "MiniMax: MiniMax M2.1", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 0.95, cache_read: 0.03 }, + limit: { context: 196608, output: 39322 }, + }, + "minimax/minimax-m1": { + id: "minimax/minimax-m1", + name: "MiniMax: MiniMax M1", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 2.2 }, + limit: { context: 1000000, output: 40000 }, + }, + "minimax/minimax-m2-her": { + id: "minimax/minimax-m2-her", + name: "MiniMax: MiniMax M2-her", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2026-01-23", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 65536, output: 2048 }, + }, + "minimax/minimax-m2.5": { + id: "minimax/minimax-m2.5", + name: "MiniMax: MiniMax M2.5", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 1.2, cache_read: 0.029 }, + limit: { context: 196608, output: 196608 }, + }, + "~openai/gpt-latest": { + id: "~openai/gpt-latest", + name: "OpenAI: GPT Latest", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-04-27", + last_updated: "2026-05-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 30, cache_read: 0.5 }, + limit: { context: 1050000, output: 128000 }, + }, + "~openai/gpt-mini-latest": { + id: "~openai/gpt-mini-latest", + name: "OpenAI: GPT Mini Latest", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-04-27", + last_updated: "2026-05-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.75, output: 4.5, cache_read: 0.075 }, + limit: { context: 400000, output: 128000 }, + }, + "qwen/qwen3-235b-a22b": { + id: "qwen/qwen3-235b-a22b", + name: "Qwen: Qwen3 235B A22B", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2024-12-01", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.455, output: 1.82, cache_read: 0.15 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen/qwen3.5-122b-a10b": { + id: "qwen/qwen3.5-122b-a10b", + name: "Qwen: Qwen3.5-122B-A10B", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-26", + last_updated: "2026-03-15", + modalities: { input: ["image", "text", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.26, output: 2.08 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen/qwen3-coder-plus": { + id: "qwen/qwen3-coder-plus", + name: "Qwen: Qwen3 Coder Plus", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-07-01", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.65, output: 3.25, cache_read: 0.2 }, + limit: { context: 1000000, output: 65536 }, + }, + "qwen/qwen3.6-27b": { + id: "qwen/qwen3.6-27b", + name: "Qwen: Qwen3.6 27B", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-27", + last_updated: "2026-05-01", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.325, output: 3.25 }, + limit: { context: 256000, output: 65536 }, + }, + "qwen/qwen3.5-27b": { + id: "qwen/qwen3.5-27b", + name: "Qwen: Qwen3.5-27B", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-26", + last_updated: "2026-03-15", + modalities: { input: ["image", "text", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.195, output: 1.56 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen/qwen3-235b-a22b-2507": { + id: "qwen/qwen3-235b-a22b-2507", + name: "Qwen: Qwen3 235B A22B Instruct 2507", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-04", + last_updated: "2026-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.071, output: 0.1 }, + limit: { context: 262144, output: 52429 }, + }, + "qwen/qwen3-8b": { + id: "qwen/qwen3-8b", + name: "Qwen: Qwen3 8B", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-04", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.4, cache_read: 0.05 }, + limit: { context: 40960, output: 8192 }, + }, + "qwen/qwen3.5-397b-a17b": { + id: "qwen/qwen3.5-397b-a17b", + name: "Qwen: Qwen3.5 397B A17B", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-15", + last_updated: "2026-03-15", + modalities: { input: ["image", "text", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.39, output: 2.34 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen/qwen-vl-plus": { + id: "qwen/qwen-vl-plus", + name: "Qwen: Qwen VL Plus", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-01-25", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1365, output: 0.4095, cache_read: 0.042 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen/qwen3-32b": { + id: "qwen/qwen3-32b", + name: "Qwen: Qwen3 32B", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2024-12-01", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.08, output: 0.24, cache_read: 0.04 }, + limit: { context: 40960, output: 40960 }, + }, + "qwen/qwen2.5-vl-72b-instruct": { + id: "qwen/qwen2.5-vl-72b-instruct", + name: "Qwen: Qwen2.5 VL 72B Instruct", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-02-01", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.8, output: 0.8, cache_read: 0.075 }, + limit: { context: 32768, output: 32768 }, + }, + "qwen/qwen-max": { + id: "qwen/qwen-max", + name: "Qwen: Qwen-Max ", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-04-03", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.04, output: 4.16, cache_read: 0.32 }, + limit: { context: 32768, output: 8192 }, + }, + "qwen/qwen-plus": { + id: "qwen/qwen-plus", + name: "Qwen: Qwen-Plus", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-01-25", + last_updated: "2025-09-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.2, cache_read: 0.08 }, + limit: { context: 1000000, output: 32768 }, + }, + "qwen/qwen3.6-35b-a3b": { + id: "qwen/qwen3.6-35b-a3b", + name: "Qwen: Qwen3.6 35B A3B", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2026-04-27", + last_updated: "2026-05-01", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1612, output: 0.96525, cache_read: 0.1612 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen/qwen3-vl-235b-a22b-thinking": { + id: "qwen/qwen3-vl-235b-a22b-thinking", + name: "Qwen: Qwen3 VL 235B A22B Thinking", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-09-24", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.26, output: 2.6 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen/qwen3-vl-30b-a3b-thinking": { + id: "qwen/qwen3-vl-30b-a3b-thinking", + name: "Qwen: Qwen3 VL 30B A3B Thinking", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-10-11", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.13, output: 1.56 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen/qwen3-vl-8b-instruct": { + id: "qwen/qwen3-vl-8b-instruct", + name: "Qwen: Qwen3 VL 8B Instruct", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-10-15", + last_updated: "2025-11-25", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.08, output: 0.5 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen/qwen3.5-flash-02-23": { + id: "qwen/qwen3.5-flash-02-23", + name: "Qwen: Qwen3.5-Flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-26", + last_updated: "2026-03-15", + modalities: { input: ["image", "text", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 1000000, output: 65536 }, + }, + "qwen/qwen3.6-plus": { + id: "qwen/qwen3.6-plus", + name: "Qwen: Qwen3.6 Plus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-26", + last_updated: "2026-04-11", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.325, output: 1.95, cache_read: 0.0325, cache_write: 0.40625 }, + limit: { context: 1000000, output: 65536 }, + }, + "qwen/qwen3-max": { + id: "qwen/qwen3-max", + name: "Qwen: Qwen3 Max", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-09-05", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.2, output: 6, cache_read: 0.24 }, + limit: { context: 262144, output: 32768 }, + }, + "qwen/qwen-plus-2025-07-28": { + id: "qwen/qwen-plus-2025-07-28", + name: "Qwen: Qwen Plus 0728", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-09-09", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.26, output: 0.78 }, + limit: { context: 1000000, output: 32768 }, + }, + "qwen/qwen3-30b-a3b-instruct-2507": { + id: "qwen/qwen3-30b-a3b-instruct-2507", + name: "Qwen: Qwen3 30B A3B Instruct 2507", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-07-29", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.09, output: 0.3, cache_read: 0.04 }, + limit: { context: 262144, output: 262144 }, + }, + "qwen/qwen3-vl-32b-instruct": { + id: "qwen/qwen3-vl-32b-instruct", + name: "Qwen: Qwen3 VL 32B Instruct", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-10-21", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.104, output: 0.416 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen/qwen3-235b-a22b-thinking-2507": { + id: "qwen/qwen3-235b-a22b-thinking-2507", + name: "Qwen: Qwen3 235B A22B Thinking 2507", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-07-25", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.11, output: 0.6 }, + limit: { context: 262144, output: 262144 }, + }, + "qwen/qwen3-next-80b-a3b-thinking": { + id: "qwen/qwen3-next-80b-a3b-thinking", + name: "Qwen: Qwen3 Next 80B A3B Thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-09-11", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.0975, output: 0.78 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen/qwen3-30b-a3b-thinking-2507": { + id: "qwen/qwen3-30b-a3b-thinking-2507", + name: "Qwen: Qwen3 30B A3B Thinking 2507", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-07-29", + last_updated: "2025-07-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.051, output: 0.34 }, + limit: { context: 32768, output: 6554 }, + }, + "qwen/qwen-2.5-7b-instruct": { + id: "qwen/qwen-2.5-7b-instruct", + name: "Qwen: Qwen2.5 7B Instruct", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-09", + last_updated: "2025-04-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.04, output: 0.1 }, + limit: { context: 32768, output: 6554 }, + }, + "qwen/qwen-vl-max": { + id: "qwen/qwen-vl-max", + name: "Qwen: Qwen VL Max", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-04-08", + last_updated: "2025-08-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 3.2 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen/qwen3-coder-flash": { + id: "qwen/qwen3-coder-flash", + name: "Qwen: Qwen3 Coder Flash", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-07-23", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.195, output: 0.975, cache_read: 0.06 }, + limit: { context: 1000000, output: 65536 }, + }, + "qwen/qwen3-30b-a3b": { + id: "qwen/qwen3-30b-a3b", + name: "Qwen: Qwen3 30B A3B", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-04", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.08, output: 0.28, cache_read: 0.03 }, + limit: { context: 40960, output: 40960 }, + }, + "qwen/qwen3-next-80b-a3b-instruct": { + id: "qwen/qwen3-next-80b-a3b-instruct", + name: "Qwen: Qwen3 Next 80B A3B Instruct", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-09-11", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.09, output: 1.1 }, + limit: { context: 131072, output: 52429 }, + }, + "qwen/qwen3.5-plus-20260420": { + id: "qwen/qwen3.5-plus-20260420", + name: "Qwen: Qwen3.5 Plus 2026-04-20", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-27", + last_updated: "2026-05-01", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2.4 }, + limit: { context: 1000000, output: 65536 }, + }, + "qwen/qwen3-coder-next": { + id: "qwen/qwen3-coder-next", + name: "Qwen: Qwen3 Coder Next", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-02-02", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.12, output: 0.75, cache_read: 0.035 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen/qwen-2.5-coder-32b-instruct": { + id: "qwen/qwen-2.5-coder-32b-instruct", + name: "Qwen2.5 Coder 32B Instruct", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-11-11", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.2, cache_read: 0.015 }, + limit: { context: 32768, output: 8192 }, + }, + "qwen/qwen3-vl-30b-a3b-instruct": { + id: "qwen/qwen3-vl-30b-a3b-instruct", + name: "Qwen: Qwen3 VL 30B A3B Instruct", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-10-05", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.13, output: 0.52 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen/qwen3-coder-30b-a3b-instruct": { + id: "qwen/qwen3-coder-30b-a3b-instruct", + name: "Qwen: Qwen3 Coder 30B A3B Instruct", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-07-31", + last_updated: "2025-07-31", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.27 }, + limit: { context: 160000, output: 32768 }, + }, + "qwen/qwen3-max-thinking": { + id: "qwen/qwen3-max-thinking", + name: "Qwen: Qwen3 Max Thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-01-23", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.78, output: 3.9 }, + limit: { context: 262144, output: 32768 }, + }, + "qwen/qwen-turbo": { + id: "qwen/qwen-turbo", + name: "Qwen: Qwen-Turbo", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-11-01", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.0325, output: 0.13, cache_read: 0.01 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen/qwen3-vl-235b-a22b-instruct": { + id: "qwen/qwen3-vl-235b-a22b-instruct", + name: "Qwen: Qwen3 VL 235B A22B Instruct", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-09-23", + last_updated: "2026-01-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.88, cache_read: 0.11 }, + limit: { context: 262144, output: 52429 }, + }, + "qwen/qwen3-coder": { + id: "qwen/qwen3-coder", + name: "Qwen: Qwen3 Coder 480B A35B", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.22, output: 1, cache_read: 0.022 }, + limit: { context: 262144, output: 52429 }, + }, + "qwen/qwen3.5-9b": { + id: "qwen/qwen3.5-9b", + name: "Qwen: Qwen3.5-9B", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-10", + last_updated: "2026-03-15", + modalities: { input: ["image", "text", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.15 }, + limit: { context: 256000, output: 32768 }, + }, + "qwen/qwen3-vl-8b-thinking": { + id: "qwen/qwen3-vl-8b-thinking", + name: "Qwen: Qwen3 VL 8B Thinking", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-10-15", + last_updated: "2025-11-25", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.117, output: 1.365 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen/qwen3.6-max-preview": { + id: "qwen/qwen3.6-max-preview", + name: "Qwen: Qwen3.6 Max Preview", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-27", + last_updated: "2026-05-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.04, output: 6.24, cache_write: 1.3 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen/qwen-plus-2025-07-28:thinking": { + id: "qwen/qwen-plus-2025-07-28:thinking", + name: "Qwen: Qwen Plus 0728 (thinking)", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-09-09", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.26, output: 0.78 }, + limit: { context: 1000000, output: 32768 }, + }, + "qwen/qwen-2.5-72b-instruct": { + id: "qwen/qwen-2.5-72b-instruct", + name: "Qwen2.5 72B Instruct", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-09", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.12, output: 0.39 }, + limit: { context: 32768, output: 16384 }, + }, + "qwen/qwen3-14b": { + id: "qwen/qwen3-14b", + name: "Qwen: Qwen3 14B", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-04", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.06, output: 0.24, cache_read: 0.025 }, + limit: { context: 40960, output: 40960 }, + }, + "qwen/qwen3.5-35b-a3b": { + id: "qwen/qwen3.5-35b-a3b", + name: "Qwen: Qwen3.5-35B-A3B", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-26", + last_updated: "2026-03-15", + modalities: { input: ["image", "text", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1625, output: 1.3 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen/qwen3.5-plus-02-15": { + id: "qwen/qwen3.5-plus-02-15", + name: "Qwen: Qwen3.5 Plus 2026-02-15", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-15", + last_updated: "2026-03-15", + modalities: { input: ["image", "text", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.26, output: 1.56 }, + limit: { context: 1000000, output: 65536 }, + }, + "qwen/qwen3.6-flash": { + id: "qwen/qwen3.6-flash", + name: "Qwen: Qwen3.6 Flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-27", + last_updated: "2026-05-01", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.5, cache_write: 0.3125 }, + limit: { context: 1000000, output: 65536 }, + }, + "alfredpros/codellama-7b-instruct-solidity": { + id: "alfredpros/codellama-7b-instruct-solidity", + name: "AlfredPros: CodeLLaMa 7B Instruct Solidity", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-14", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.8, output: 1.2 }, + limit: { context: 4096, output: 4096 }, + }, + "kwaipilot/kat-coder-pro-v2": { + id: "kwaipilot/kat-coder-pro-v2", + name: "Kwaipilot: KAT-Coder-Pro V2", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-03-27", + last_updated: "2026-04-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.06 }, + limit: { context: 256000, output: 80000 }, + }, + "google/gemini-2.5-pro-preview-05-06": { + id: "google/gemini-2.5-pro-preview-05-06", + name: "Google: Gemini 2.5 Pro Preview 05-06", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-05-06", + last_updated: "2026-03-15", + modalities: { input: ["audio", "image", "pdf", "text", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, reasoning: 10, cache_read: 0.125, cache_write: 0.375 }, + limit: { context: 1048576, output: 65535 }, + }, + "google/lyria-3-clip-preview": { + id: "google/lyria-3-clip-preview", + name: "Google: Lyria 3 Clip Preview", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2026-03-30", + last_updated: "2026-04-11", + modalities: { input: ["image", "text"], output: ["audio", "text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-3.1-pro-preview-customtools": { + id: "google/gemini-3.1-pro-preview-customtools", + name: "Google: Gemini 3.1 Pro Preview Custom Tools", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-26", + last_updated: "2026-03-15", + modalities: { input: ["audio", "image", "pdf", "text", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, reasoning: 12 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-2.5-flash-lite-preview-09-2025": { + id: "google/gemini-2.5-flash-lite-preview-09-2025", + name: "Google: Gemini 2.5 Flash Lite Preview 09-2025", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-09-25", + last_updated: "2026-03-15", + modalities: { input: ["audio", "image", "pdf", "text", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, reasoning: 0.4, cache_read: 0.01, cache_write: 0.083333 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-2.0-flash-001": { + id: "google/gemini-2.0-flash-001", + name: "Google: Gemini 2.0 Flash", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-12-11", + last_updated: "2026-03-15", + modalities: { input: ["audio", "image", "pdf", "text", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.025, cache_write: 0.083333 }, + limit: { context: 1048576, output: 8192 }, + }, + "google/lyria-3-pro-preview": { + id: "google/lyria-3-pro-preview", + name: "Google: Lyria 3 Pro Preview", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2026-03-30", + last_updated: "2026-04-11", + modalities: { input: ["image", "text"], output: ["audio", "text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemma-3n-e4b-it": { + id: "google/gemma-3n-e4b-it", + name: "Google: Gemma 3n 4B", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-05-20", + last_updated: "2025-05-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0.04 }, + limit: { context: 32768, output: 6554 }, + }, + "google/gemini-3.1-flash-lite-preview": { + id: "google/gemini-3.1-flash-lite-preview", + name: "Google: Gemini 3.1 Flash Lite Preview", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-03", + last_updated: "2026-03-15", + modalities: { input: ["audio", "image", "pdf", "text", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.5, reasoning: 1.5 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-3.1-pro-preview": { + id: "google/gemini-3.1-pro-preview", + name: "Google: Gemini 3.1 Pro Preview", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-19", + last_updated: "2026-03-15", + modalities: { input: ["audio", "image", "pdf", "text", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, reasoning: 12 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-3-flash-preview": { + id: "google/gemini-3-flash-preview", + name: "Google: Gemini 3 Flash Preview", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-12-17", + last_updated: "2026-03-15", + modalities: { input: ["audio", "image", "pdf", "text", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 3, reasoning: 3, cache_read: 0.05, cache_write: 0.083333 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-2.5-pro": { + id: "google/gemini-2.5-pro", + name: "Google: Gemini 2.5 Pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-03-20", + last_updated: "2026-03-15", + modalities: { input: ["audio", "image", "pdf", "text", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, reasoning: 10, cache_read: 0.125, cache_write: 0.375 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-3-pro-image-preview": { + id: "google/gemini-3-pro-image-preview", + name: "Google: Nano Banana Pro (Gemini 3 Pro Image Preview)", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-11-20", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["image", "text"] }, + open_weights: false, + cost: { input: 2, output: 12, reasoning: 12 }, + limit: { context: 65536, output: 32768 }, + }, + "google/gemma-4-31b-it": { + id: "google/gemma-4-31b-it", + name: "Google: Gemma 4 31B", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-02", + last_updated: "2026-04-11", + modalities: { input: ["image", "text", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.4 }, + limit: { context: 262144, output: 131072 }, + }, + "google/gemini-2.5-flash-image": { + id: "google/gemini-2.5-flash-image", + name: "Google: Nano Banana (Gemini 2.5 Flash Image)", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-10-08", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["image", "text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5 }, + limit: { context: 32768, output: 32768 }, + }, + "google/gemma-3-12b-it": { + id: "google/gemma-3-12b-it", + name: "Google: Gemma 3 12B", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-03-13", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.04, output: 0.13, cache_read: 0.015 }, + limit: { context: 131072, output: 131072 }, + }, + "google/gemini-2.5-flash": { + id: "google/gemini-2.5-flash", + name: "Google: Gemini 2.5 Flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-07-17", + last_updated: "2026-03-15", + modalities: { input: ["audio", "image", "pdf", "text", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5, reasoning: 2.5, cache_read: 0.03, cache_write: 0.083333 }, + limit: { context: 1048576, output: 65535 }, + }, + "google/gemini-3.1-flash-image-preview": { + id: "google/gemini-3.1-flash-image-preview", + name: "Google: Nano Banana 2 (Gemini 3.1 Flash Image Preview)", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2026-02-26", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["image", "text"] }, + open_weights: false, + cost: { input: 0.5, output: 3 }, + limit: { context: 65536, output: 65536 }, + }, + "google/gemma-3-4b-it": { + id: "google/gemma-3-4b-it", + name: "Google: Gemma 3 4B", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-03-13", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.04, output: 0.08 }, + limit: { context: 131072, output: 19200 }, + }, + "google/gemini-2.5-pro-preview": { + id: "google/gemini-2.5-pro-preview", + name: "Google: Gemini 2.5 Pro Preview 06-05", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-06-05", + last_updated: "2026-03-15", + modalities: { input: ["audio", "image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, reasoning: 10, cache_read: 0.125, cache_write: 0.375 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemma-2-27b-it": { + id: "google/gemma-2-27b-it", + name: "Google: Gemma 2 27B", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-06-24", + last_updated: "2024-06-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.65, output: 0.65 }, + limit: { context: 8192, output: 2048 }, + }, + "google/gemma-3-27b-it": { + id: "google/gemma-3-27b-it", + name: "Google: Gemma 3 27B", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-03-12", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.11, cache_read: 0.02 }, + limit: { context: 128000, output: 65536 }, + }, + "google/gemma-4-26b-a4b-it": { + id: "google/gemma-4-26b-a4b-it", + name: "Google: Gemma 4 26B A4B", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-03", + last_updated: "2026-04-11", + modalities: { input: ["image", "text", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.12, output: 0.4 }, + limit: { context: 262144, output: 262144 }, + }, + "google/gemini-2.5-flash-lite": { + id: "google/gemini-2.5-flash-lite", + name: "Google: Gemini 2.5 Flash Lite", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-06-17", + last_updated: "2026-03-15", + modalities: { input: ["audio", "image", "pdf", "text", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, reasoning: 0.4, cache_read: 0.01, cache_write: 0.083333 }, + limit: { context: 1048576, output: 65535 }, + }, + "google/gemini-2.0-flash-lite-001": { + id: "google/gemini-2.0-flash-lite-001", + name: "Google: Gemini 2.0 Flash Lite", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-12-11", + last_updated: "2026-03-15", + modalities: { input: ["audio", "image", "pdf", "text", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.075, output: 0.3 }, + limit: { context: 1048576, output: 8192 }, + }, + "moonshotai/kimi-k2.5": { + id: "moonshotai/kimi-k2.5", + name: "MoonshotAI: Kimi K2.5", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-01-27", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.45, output: 2.2 }, + limit: { context: 262144, output: 65535 }, + }, + "moonshotai/kimi-k2-0905": { + id: "moonshotai/kimi-k2-0905", + name: "MoonshotAI: Kimi K2 0905", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 2, cache_read: 0.15 }, + limit: { context: 131072, output: 26215 }, + }, + "moonshotai/kimi-k2.6": { + id: "moonshotai/kimi-k2.6", + name: "MoonshotAI: Kimi K2.6", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_details" }, + structured_output: true, + temperature: true, + release_date: "2026-04-20", + last_updated: "2026-04-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4, cache_read: 0.16 }, + limit: { context: 262144, output: 262144 }, + }, + "moonshotai/kimi-k2": { + id: "moonshotai/kimi-k2", + name: "MoonshotAI: Kimi K2 0711", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-07-11", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 2.2 }, + limit: { context: 131000, output: 26215 }, + }, + "moonshotai/kimi-k2-thinking": { + id: "moonshotai/kimi-k2-thinking", + name: "MoonshotAI: Kimi K2 Thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-11-06", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.47, output: 2, cache_read: 0.2 }, + limit: { context: 131072, output: 65535 }, + }, + "aion-labs/aion-1.0": { + id: "aion-labs/aion-1.0", + name: "AionLabs: Aion-1.0", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-02-05", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 4, output: 8 }, + limit: { context: 131072, output: 32768 }, + }, + "aion-labs/aion-rp-llama-3.1-8b": { + id: "aion-labs/aion-rp-llama-3.1-8b", + name: "AionLabs: Aion-RP 1.0 (8B)", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-02-05", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 1.6 }, + limit: { context: 32768, output: 32768 }, + }, + "aion-labs/aion-2.0": { + id: "aion-labs/aion-2.0", + name: "AionLabs: Aion-2.0", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2026-02-24", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 1.6 }, + limit: { context: 131072, output: 32768 }, + }, + "aion-labs/aion-1.0-mini": { + id: "aion-labs/aion-1.0-mini", + name: "AionLabs: Aion-1.0-Mini", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-02-05", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.7, output: 1.4 }, + limit: { context: 131072, output: 32768 }, + }, + "~moonshotai/kimi-latest": { + id: "~moonshotai/kimi-latest", + name: "MoonshotAI: Kimi Latest", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-27", + last_updated: "2026-05-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.74, output: 3.49, cache_read: 0.14 }, + limit: { context: 262142, output: 262142 }, + }, + "thedrummer/unslopnemo-12b": { + id: "thedrummer/unslopnemo-12b", + name: "TheDrummer: UnslopNemo 12B", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-11-09", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 0.4 }, + limit: { context: 32768, output: 32768 }, + }, + "thedrummer/cydonia-24b-v4.1": { + id: "thedrummer/cydonia-24b-v4.1", + name: "TheDrummer: Cydonia 24B V4.1", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-09-27", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.5 }, + limit: { context: 131072, output: 131072 }, + }, + "thedrummer/skyfall-36b-v2": { + id: "thedrummer/skyfall-36b-v2", + name: "TheDrummer: Skyfall 36B V2", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-03-11", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 0.8 }, + limit: { context: 32768, output: 32768 }, + }, + "thedrummer/rocinante-12b": { + id: "thedrummer/rocinante-12b", + name: "TheDrummer: Rocinante 12B", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-09-30", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.17, output: 0.43 }, + limit: { context: 32768, output: 32768 }, + }, + "anthropic/claude-opus-4.1": { + id: "anthropic/claude-opus-4.1", + name: "Anthropic: Claude Opus 4.1", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "anthropic/claude-3.7-sonnet:thinking": { + id: "anthropic/claude-3.7-sonnet:thinking", + name: "Anthropic: Claude 3.7 Sonnet (thinking)", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-02-19", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-opus-4.6-fast": { + id: "anthropic/claude-opus-4.6-fast", + name: "Anthropic: Claude Opus 4.6 (Fast)", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-04-07", + last_updated: "2026-04-11", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 150, cache_read: 3, cache_write: 37.5 }, + limit: { context: 1000000, output: 128000 }, + }, + "anthropic/claude-3.7-sonnet": { + id: "anthropic/claude-3.7-sonnet", + name: "Anthropic: Claude 3.7 Sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-02-19", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-opus-4.6": { + id: "anthropic/claude-opus-4.6", + name: "Anthropic: Claude Opus 4.6", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "anthropic/claude-opus-4.7": { + id: "anthropic/claude-opus-4.7", + name: "Anthropic: Claude Opus 4.7", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-04-16", + last_updated: "2026-05-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "anthropic/claude-sonnet-4": { + id: "anthropic/claude-sonnet-4", + name: "Anthropic: Claude Sonnet 4", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-05-22", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-sonnet-4.5": { + id: "anthropic/claude-sonnet-4.5", + name: "Anthropic: Claude Sonnet 4.5", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-09-29", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 1000000, output: 64000 }, + }, + "anthropic/claude-opus-4.5": { + id: "anthropic/claude-opus-4.5", + name: "Anthropic: Claude Opus 4.5", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-11-24", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-3-haiku": { + id: "anthropic/claude-3-haiku", + name: "Anthropic: Claude 3 Haiku", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-03-07", + last_updated: "2024-03-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.25, cache_read: 0.03, cache_write: 0.3 }, + limit: { context: 200000, output: 4096 }, + }, + "anthropic/claude-opus-4": { + id: "anthropic/claude-opus-4", + name: "Anthropic: Claude Opus 4", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-05-22", + last_updated: "2026-03-15", + modalities: { input: ["image", "pdf", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "anthropic/claude-3.5-haiku": { + id: "anthropic/claude-3.5-haiku", + name: "Anthropic: Claude 3.5 Haiku", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 4, cache_read: 0.08, cache_write: 1 }, + limit: { context: 200000, output: 8192 }, + }, + "anthropic/claude-haiku-4.5": { + id: "anthropic/claude-haiku-4.5", + name: "Anthropic: Claude Haiku 4.5", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-sonnet-4.6": { + id: "anthropic/claude-sonnet-4.6", + name: "Anthropic: Claude Sonnet 4.6", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 1000000, output: 128000 }, + }, + "switchpoint/router": { + id: "switchpoint/router", + name: "Switchpoint Router", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-07-12", + last_updated: "2026-03-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.85, output: 3.4 }, + limit: { context: 131072, output: 32768 }, + }, + "bytedance/ui-tars-1.5-7b": { + id: "bytedance/ui-tars-1.5-7b", + name: "ByteDance: UI-TARS 7B ", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-07-23", + last_updated: "2026-03-15", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.2 }, + limit: { context: 128000, output: 2048 }, + }, + "tngtech/deepseek-r1t2-chimera": { + id: "tngtech/deepseek-r1t2-chimera", + name: "TNG: DeepSeek R1T2 Chimera", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-07-08", + last_updated: "2025-07-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 0.85, cache_read: 0.125 }, + limit: { context: 163840, output: 163840 }, + }, + "xiaomi/mimo-v2.5-pro": { + id: "xiaomi/mimo-v2.5-pro", + name: "Xiaomi: MiMo V2.5 Pro", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3, cache_read: 0.2, context_over_200k: { input: 2, output: 6, cache_read: 0.4 } }, + limit: { context: 1048576, output: 131072 }, + }, + "xiaomi/mimo-v2-omni": { + id: "xiaomi/mimo-v2-omni", + name: "Xiaomi: MiMo-V2-Omni", + family: "mimo", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2, cache_read: 0.08 }, + limit: { context: 262144, output: 65536 }, + }, + "xiaomi/mimo-v2.5": { + id: "xiaomi/mimo-v2.5", + name: "Xiaomi: MiMo-V2.5", + family: "mimo", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: true, + cost: { + input: 0.4, + output: 2, + cache_read: 0.08, + context_over_200k: { input: 0.8, output: 4, cache_read: 0.16 }, + }, + limit: { context: 1048576, output: 131072 }, + }, + "xiaomi/mimo-v2-pro": { + id: "xiaomi/mimo-v2-pro", + name: "Xiaomi: MiMo-V2-Pro", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 3, cache_read: 0.2, context_over_200k: { input: 2, output: 6, cache_read: 0.4 } }, + limit: { context: 1048576, output: 131072 }, + }, + "xiaomi/mimo-v2-flash": { + id: "xiaomi/mimo-v2-flash", + name: "Xiaomi: MiMo-V2-Flash", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-12-01", + release_date: "2025-12-16", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.09, output: 0.29, cache_read: 0.045 }, + limit: { context: 262144, output: 65536 }, + }, + }, + }, + "sap-ai-core": { + id: "sap-ai-core", + env: ["AICORE_SERVICE_KEY"], + npm: "@jerome-benoit/sap-ai-provider-v2", + name: "SAP AI Core", + doc: "https://help.sap.com/docs/sap-ai-core", + models: { + "anthropic--claude-4.6-opus": { + id: "anthropic--claude-4.6-opus", + name: "anthropic--claude-4.6-opus", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-02-05", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "anthropic--claude-3-haiku": { + id: "anthropic--claude-3-haiku", + name: "anthropic--claude-3-haiku", + family: "claude-haiku", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-08-31", + release_date: "2024-03-13", + last_updated: "2024-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.25, cache_read: 0.03, cache_write: 0.3 }, + limit: { context: 200000, output: 4096 }, + }, + "anthropic--claude-3-opus": { + id: "anthropic--claude-3-opus", + name: "anthropic--claude-3-opus", + family: "claude-opus", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-08-31", + release_date: "2024-02-29", + last_updated: "2024-02-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 4096 }, + }, + "gpt-5-mini": { + id: "gpt-5-mini", + name: "gpt-5-mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.025 }, + limit: { context: 400000, output: 128000 }, + }, + "gpt-5-nano": { + id: "gpt-5-nano", + name: "gpt-5-nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.4, cache_read: 0.005 }, + limit: { context: 400000, output: 128000 }, + }, + "gemini-2.5-pro": { + id: "gemini-2.5-pro", + name: "gemini-2.5-pro", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-25", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 1048576, output: 65536 }, + }, + "anthropic--claude-3.7-sonnet": { + id: "anthropic--claude-3.7-sonnet", + name: "anthropic--claude-3.7-sonnet", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10-31", + release_date: "2025-02-24", + last_updated: "2025-02-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "sonar-pro": { + id: "sonar-pro", + name: "sonar-pro", + family: "sonar-pro", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-09-01", + release_date: "2024-01-01", + last_updated: "2025-09-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 200000, output: 8192 }, + }, + "anthropic--claude-4.5-sonnet": { + id: "anthropic--claude-4.5-sonnet", + name: "anthropic--claude-4.5-sonnet", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic--claude-4.6-sonnet": { + id: "anthropic--claude-4.6-sonnet", + name: "anthropic--claude-4.6-sonnet", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08", + release_date: "2026-02-17", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 1000000, output: 64000 }, + }, + "sonar-deep-research": { + id: "sonar-deep-research", + name: "sonar-deep-research", + family: "sonar-deep-research", + attachment: false, + reasoning: true, + tool_call: false, + temperature: false, + knowledge: "2025-01", + release_date: "2025-02-01", + last_updated: "2025-09-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, reasoning: 3 }, + limit: { context: 128000, output: 32768 }, + }, + "gemini-2.5-flash": { + id: "gemini-2.5-flash", + name: "gemini-2.5-flash", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-25", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5, cache_read: 0.03, input_audio: 1 }, + limit: { context: 1048576, output: 65536 }, + }, + "anthropic--claude-4.5-opus": { + id: "anthropic--claude-4.5-opus", + name: "anthropic--claude-4.5-opus", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05", + release_date: "2025-11-24", + last_updated: "2025-11-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 64000 }, + }, + sonar: { + id: "sonar", + name: "sonar", + family: "sonar", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-09-01", + release_date: "2024-01-01", + last_updated: "2025-09-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 1 }, + limit: { context: 128000, output: 4096 }, + }, + "anthropic--claude-4-opus": { + id: "anthropic--claude-4-opus", + name: "anthropic--claude-4-opus", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "anthropic--claude-3-sonnet": { + id: "anthropic--claude-3-sonnet", + name: "anthropic--claude-3-sonnet", + family: "claude-sonnet", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-08-31", + release_date: "2024-03-04", + last_updated: "2024-03-04", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 4096 }, + }, + "anthropic--claude-4-sonnet": { + id: "anthropic--claude-4-sonnet", + name: "anthropic--claude-4-sonnet", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "gemini-2.5-flash-lite": { + id: "gemini-2.5-flash-lite", + name: "gemini-2.5-flash-lite", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.025 }, + limit: { context: 1048576, output: 65536 }, + }, + "anthropic--claude-4.5-haiku": { + id: "anthropic--claude-4.5-haiku", + name: "anthropic--claude-4.5-haiku", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + }, + "gpt-5": { + id: "gpt-5", + name: "gpt-5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "gpt-4.1": { + id: "gpt-4.1", + name: "gpt-4.1", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 1047576, output: 32768 }, + }, + "gpt-4.1-mini": { + id: "gpt-4.1-mini", + name: "gpt-4.1-mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.6, cache_read: 0.1 }, + limit: { context: 1047576, output: 32768 }, + }, + "anthropic--claude-3.5-sonnet": { + id: "anthropic--claude-3.5-sonnet", + name: "anthropic--claude-3.5-sonnet", + family: "claude-sonnet", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04-30", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 8192 }, + }, + }, + }, + morph: { + id: "morph", + env: ["MORPH_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.morphllm.com/v1", + name: "Morph", + doc: "https://docs.morphllm.com/api-reference/introduction", + models: { + auto: { + id: "auto", + name: "Auto", + family: "auto", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-06-01", + last_updated: "2024-06-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.85, output: 1.55 }, + limit: { context: 32000, output: 32000 }, + }, + "morph-v3-fast": { + id: "morph-v3-fast", + name: "Morph v3 Fast", + family: "morph", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-08-15", + last_updated: "2024-08-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 1.2 }, + limit: { context: 16000, output: 16000 }, + }, + "morph-v3-large": { + id: "morph-v3-large", + name: "Morph v3 Large", + family: "morph", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-08-15", + last_updated: "2024-08-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.9, output: 1.9 }, + limit: { context: 32000, output: 32000 }, + }, + }, + }, + "cloudflare-ai-gateway": { + id: "cloudflare-ai-gateway", + env: ["CLOUDFLARE_API_TOKEN", "CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_GATEWAY_ID"], + npm: "ai-gateway-provider", + name: "Cloudflare AI Gateway", + doc: "https://developers.cloudflare.com/ai-gateway/", + models: { + "workers-ai/@cf/myshell-ai/melotts": { + id: "workers-ai/@cf/myshell-ai/melotts", + name: "MyShell MeloTTS", + family: "melotts", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-11-14", + last_updated: "2025-11-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/ibm-granite/granite-4.0-h-micro": { + id: "workers-ai/@cf/ibm-granite/granite-4.0-h-micro", + name: "IBM Granite 4.0 H Micro", + family: "granite", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.017, output: 0.11 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/huggingface/distilbert-sst-2-int8": { + id: "workers-ai/@cf/huggingface/distilbert-sst-2-int8", + name: "DistilBERT SST-2 INT8", + family: "distilbert", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.026, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/zai-org/glm-4.7-flash": { + id: "workers-ai/@cf/zai-org/glm-4.7-flash", + name: "GLM-4.7-Flash", + family: "glm-flash", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-01-19", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.06, output: 0.4 }, + limit: { context: 131072, output: 131072 }, + }, + "workers-ai/@cf/pipecat-ai/smart-turn-v2": { + id: "workers-ai/@cf/pipecat-ai/smart-turn-v2", + name: "Pipecat Smart Turn v2", + family: "smart-turn", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-11-14", + last_updated: "2025-11-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/mistralai/mistral-small-3.1-24b-instruct": { + id: "workers-ai/@cf/mistralai/mistral-small-3.1-24b-instruct", + name: "Mistral Small 3.1 24B Instruct", + family: "mistral-small", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-11", + last_updated: "2025-04-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.35, output: 0.56 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/facebook/bart-large-cnn": { + id: "workers-ai/@cf/facebook/bart-large-cnn", + name: "BART Large CNN", + family: "bart", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-09", + last_updated: "2025-04-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/aisingapore/gemma-sea-lion-v4-27b-it": { + id: "workers-ai/@cf/aisingapore/gemma-sea-lion-v4-27b-it", + name: "Gemma SEA-LION v4 27B IT", + family: "gemma", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.35, output: 0.56 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/nvidia/nemotron-3-120b-a12b": { + id: "workers-ai/@cf/nvidia/nemotron-3-120b-a12b", + name: "Nemotron 3 Super 120B", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-03-11", + last_updated: "2026-03-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 1.5 }, + limit: { context: 256000, output: 256000 }, + }, + "workers-ai/@cf/deepseek-ai/deepseek-r1-distill-qwen-32b": { + id: "workers-ai/@cf/deepseek-ai/deepseek-r1-distill-qwen-32b", + name: "DeepSeek R1 Distill Qwen 32B", + family: "deepseek-thinking", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 4.88 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/openai/gpt-oss-20b": { + id: "workers-ai/@cf/openai/gpt-oss-20b", + name: "GPT OSS 20B", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.3 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/openai/gpt-oss-120b": { + id: "workers-ai/@cf/openai/gpt-oss-120b", + name: "GPT OSS 120B", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.35, output: 0.75 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/mistral/mistral-7b-instruct-v0.1": { + id: "workers-ai/@cf/mistral/mistral-7b-instruct-v0.1", + name: "Mistral 7B Instruct v0.1", + family: "mistral", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.11, output: 0.19 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/meta/llama-4-scout-17b-16e-instruct": { + id: "workers-ai/@cf/meta/llama-4-scout-17b-16e-instruct", + name: "Llama 4 Scout 17B 16E Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 0.85 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/meta/llama-3-8b-instruct-awq": { + id: "workers-ai/@cf/meta/llama-3-8b-instruct-awq", + name: "Llama 3 8B Instruct AWQ", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.12, output: 0.27 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/meta/llama-guard-3-8b": { + id: "workers-ai/@cf/meta/llama-guard-3-8b", + name: "Llama Guard 3 8B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.48, output: 0.03 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/meta/m2m100-1.2b": { + id: "workers-ai/@cf/meta/m2m100-1.2b", + name: "M2M100 1.2B", + family: "m2m", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.34, output: 0.34 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/meta/llama-2-7b-chat-fp16": { + id: "workers-ai/@cf/meta/llama-2-7b-chat-fp16", + name: "Llama 2 7B Chat FP16", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.56, output: 6.67 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/meta/llama-3.2-11b-vision-instruct": { + id: "workers-ai/@cf/meta/llama-3.2-11b-vision-instruct", + name: "Llama 3.2 11B Vision Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.049, output: 0.68 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast": { + id: "workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast", + name: "Llama 3.3 70B Instruct FP8 Fast", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.29, output: 2.25 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/meta/llama-3.2-1b-instruct": { + id: "workers-ai/@cf/meta/llama-3.2-1b-instruct", + name: "Llama 3.2 1B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.027, output: 0.2 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/meta/llama-3.1-8b-instruct-fp8": { + id: "workers-ai/@cf/meta/llama-3.1-8b-instruct-fp8", + name: "Llama 3.1 8B Instruct FP8", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.29 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/meta/llama-3.2-3b-instruct": { + id: "workers-ai/@cf/meta/llama-3.2-3b-instruct", + name: "Llama 3.2 3B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.051, output: 0.34 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/meta/llama-3.1-8b-instruct-awq": { + id: "workers-ai/@cf/meta/llama-3.1-8b-instruct-awq", + name: "Llama 3.1 8B Instruct AWQ", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.12, output: 0.27 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/meta/llama-3-8b-instruct": { + id: "workers-ai/@cf/meta/llama-3-8b-instruct", + name: "Llama 3 8B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.28, output: 0.83 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/meta/llama-3.1-8b-instruct": { + id: "workers-ai/@cf/meta/llama-3.1-8b-instruct", + name: "Llama 3.1 8B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.28, output: 0.8299999999999998 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/qwen/qwen2.5-coder-32b-instruct": { + id: "workers-ai/@cf/qwen/qwen2.5-coder-32b-instruct", + name: "Qwen 2.5 Coder 32B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-11", + last_updated: "2025-04-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.66, output: 1 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/qwen/qwen3-embedding-0.6b": { + id: "workers-ai/@cf/qwen/qwen3-embedding-0.6b", + name: "Qwen3 Embedding 0.6B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-11-14", + last_updated: "2025-11-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.012, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/qwen/qwq-32b": { + id: "workers-ai/@cf/qwen/qwq-32b", + name: "QwQ 32B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-11", + last_updated: "2025-04-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.66, output: 1 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/qwen/qwen3-30b-a3b-fp8": { + id: "workers-ai/@cf/qwen/qwen3-30b-a3b-fp8", + name: "Qwen3 30B A3B FP8", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-11-14", + last_updated: "2025-11-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.051, output: 0.34 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/google/gemma-3-12b-it": { + id: "workers-ai/@cf/google/gemma-3-12b-it", + name: "Gemma 3 12B IT", + family: "gemma", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-11", + last_updated: "2025-04-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.35, output: 0.56 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/moonshotai/kimi-k2.5": { + id: "workers-ai/@cf/moonshotai/kimi-k2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3, cache_read: 0.1 }, + limit: { context: 256000, output: 256000 }, + }, + "workers-ai/@cf/moonshotai/kimi-k2.6": { + id: "workers-ai/@cf/moonshotai/kimi-k2.6", + name: "Kimi K2.6", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-20", + last_updated: "2026-04-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4, cache_read: 0.16 }, + limit: { context: 256000, output: 256000 }, + }, + "workers-ai/@cf/ai4bharat/indictrans2-en-indic-1B": { + id: "workers-ai/@cf/ai4bharat/indictrans2-en-indic-1B", + name: "IndicTrans2 EN-Indic 1B", + family: "indictrans", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.34, output: 0.34 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/pfnet/plamo-embedding-1b": { + id: "workers-ai/@cf/pfnet/plamo-embedding-1b", + name: "PLaMo Embedding 1B", + family: "plamo", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.019, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/baai/bge-small-en-v1.5": { + id: "workers-ai/@cf/baai/bge-small-en-v1.5", + name: "BGE Small EN v1.5", + family: "bge", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.02, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/baai/bge-large-en-v1.5": { + id: "workers-ai/@cf/baai/bge-large-en-v1.5", + name: "BGE Large EN v1.5", + family: "bge", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/baai/bge-reranker-base": { + id: "workers-ai/@cf/baai/bge-reranker-base", + name: "BGE Reranker Base", + family: "bge", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-09", + last_updated: "2025-04-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.0031, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/baai/bge-base-en-v1.5": { + id: "workers-ai/@cf/baai/bge-base-en-v1.5", + name: "BGE Base EN v1.5", + family: "bge", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.067, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/baai/bge-m3": { + id: "workers-ai/@cf/baai/bge-m3", + name: "BGE M3", + family: "bge", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.012, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/deepgram/aura-2-en": { + id: "workers-ai/@cf/deepgram/aura-2-en", + name: "Deepgram Aura 2 (EN)", + family: "aura", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-11-14", + last_updated: "2025-11-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/deepgram/aura-2-es": { + id: "workers-ai/@cf/deepgram/aura-2-es", + name: "Deepgram Aura 2 (ES)", + family: "aura", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-11-14", + last_updated: "2025-11-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "workers-ai/@cf/deepgram/nova-3": { + id: "workers-ai/@cf/deepgram/nova-3", + name: "Deepgram Nova 3", + family: "nova", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-11-14", + last_updated: "2025-11-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-5.3-codex": { + id: "openai/gpt-5.3-codex", + name: "GPT-5.3 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, input: 272000, output: 128000 }, + provider: { npm: "ai-gateway-provider" }, + }, + "openai/gpt-4-turbo": { + id: "openai/gpt-4-turbo", + name: "GPT-4 Turbo", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + knowledge: "2023-12", + release_date: "2023-11-06", + last_updated: "2024-04-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 10, output: 30 }, + limit: { context: 128000, output: 4096 }, + }, + "openai/gpt-5.2": { + id: "openai/gpt-5.2", + name: "GPT-5.2", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/o3-pro": { + id: "openai/o3-pro", + name: "o3-pro", + family: "o-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05", + release_date: "2025-06-10", + last_updated: "2025-06-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 20, output: 80 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-4o-mini": { + id: "openai/gpt-4o-mini", + name: "GPT-4o mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-09", + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6, cache_read: 0.08 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/o4-mini": { + id: "openai/o4-mini", + name: "o4-mini", + family: "o-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05", + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.28 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-5.2-codex": { + id: "openai/gpt-5.2-codex", + name: "GPT-5.2 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, input: 272000, output: 128000 }, + provider: { npm: "ai-gateway-provider" }, + }, + "openai/gpt-5.1": { + id: "openai/gpt-5.1", + name: "GPT-5.1", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.13 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/o1": { + id: "openai/o1", + name: "o1", + family: "o", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2023-09", + release_date: "2024-12-05", + last_updated: "2024-12-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 60, cache_read: 7.5 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-3.5-turbo": { + id: "openai/gpt-3.5-turbo", + name: "GPT-3.5-turbo", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + knowledge: "2021-09-01", + release_date: "2023-03-01", + last_updated: "2023-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 1.5, cache_read: 1.25 }, + limit: { context: 16385, output: 4096 }, + }, + "openai/o3-mini": { + id: "openai/o3-mini", + name: "o3-mini", + family: "o-mini", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05", + release_date: "2024-12-20", + last_updated: "2025-01-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.55 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-4": { + id: "openai/gpt-4", + name: "GPT-4", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + knowledge: "2023-11", + release_date: "2023-11-06", + last_updated: "2024-04-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 60 }, + limit: { context: 8192, output: 8192 }, + }, + "openai/gpt-5.4": { + id: "openai/gpt-5.4", + name: "GPT-5.4", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 15, cache_read: 0.25 }, + limit: { context: 1050000, input: 922000, output: 128000 }, + provider: { npm: "ai-gateway-provider" }, + }, + "openai/o3": { + id: "openai/o3", + name: "o3", + family: "o", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05", + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-4o": { + id: "openai/gpt-4o", + name: "GPT-4o", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-09", + release_date: "2024-05-13", + last_updated: "2024-08-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10, cache_read: 1.25 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-5.1-codex": { + id: "openai/gpt-5.1-codex", + name: "GPT-5.1 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "anthropic/claude-haiku-4-5": { + id: "anthropic/claude-haiku-4-5", + name: "Claude Haiku 4.5 (latest)", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-sonnet-4-6": { + id: "anthropic/claude-sonnet-4-6", + name: "Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-02-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 3, + output: 15, + cache_read: 0.3, + cache_write: 3.75, + context_over_200k: { input: 6, output: 22.5, cache_read: 0.6, cache_write: 7.5 }, + }, + limit: { context: 1000000, output: 64000 }, + provider: { npm: "ai-gateway-provider" }, + }, + "anthropic/claude-opus-4-7": { + id: "anthropic/claude-opus-4-7", + name: "Claude Opus 4.7", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2026-01", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + provider: { npm: "@ai-sdk/anthropic" }, + }, + "anthropic/claude-opus-4-1": { + id: "anthropic/claude-opus-4-1", + name: "Claude Opus 4.1 (latest)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "anthropic/claude-3-5-haiku": { + id: "anthropic/claude-3-5-haiku", + name: "Claude Haiku 3.5 (latest)", + family: "claude-haiku", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07-31", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 4, cache_read: 0.08, cache_write: 1 }, + limit: { context: 200000, output: 8192 }, + }, + "anthropic/claude-3.5-sonnet": { + id: "anthropic/claude-3.5-sonnet", + name: "Claude Sonnet 3.5 v2", + family: "claude-sonnet", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04-30", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 8192 }, + }, + "anthropic/claude-sonnet-4": { + id: "anthropic/claude-sonnet-4", + name: "Claude Sonnet 4 (latest)", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-opus-4-5": { + id: "anthropic/claude-opus-4-5", + name: "Claude Opus 4.5 (latest)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-24", + last_updated: "2025-11-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-3-haiku": { + id: "anthropic/claude-3-haiku", + name: "Claude Haiku 3", + family: "claude-haiku", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-08-31", + release_date: "2024-03-13", + last_updated: "2024-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.25, cache_read: 0.03, cache_write: 0.3 }, + limit: { context: 200000, output: 4096 }, + }, + "anthropic/claude-opus-4": { + id: "anthropic/claude-opus-4", + name: "Claude Opus 4 (latest)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "anthropic/claude-opus-4-6": { + id: "anthropic/claude-opus-4-6", + name: "Claude Opus 4.6 (latest)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 5, + output: 25, + cache_read: 0.5, + cache_write: 6.25, + context_over_200k: { input: 10, output: 37.5, cache_read: 1, cache_write: 12.5 }, + }, + limit: { context: 1000000, output: 128000 }, + }, + "anthropic/claude-3.5-haiku": { + id: "anthropic/claude-3.5-haiku", + name: "Claude Haiku 3.5 (latest)", + family: "claude-haiku", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07-31", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 4, cache_read: 0.08, cache_write: 1 }, + limit: { context: 200000, output: 8192 }, + }, + "anthropic/claude-sonnet-4-5": { + id: "anthropic/claude-sonnet-4-5", + name: "Claude Sonnet 4.5 (latest)", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-3-sonnet": { + id: "anthropic/claude-3-sonnet", + name: "Claude Sonnet 3", + family: "claude-sonnet", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-08-31", + release_date: "2024-03-04", + last_updated: "2024-03-04", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 0.3 }, + limit: { context: 200000, output: 4096 }, + }, + "anthropic/claude-3-opus": { + id: "anthropic/claude-3-opus", + name: "Claude Opus 3", + family: "claude-opus", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-08-31", + release_date: "2024-02-29", + last_updated: "2024-02-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 4096 }, + }, + "openai/gpt-5.5": { + id: "openai/gpt-5.5", + name: "GPT-5.5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-12-01", + release_date: "2026-04-23", + last_updated: "2026-04-23", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 30, cache_read: 0.5, context_over_200k: { input: 10, output: 45, cache_read: 1 } }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + }, + }, + "github-copilot": { + id: "github-copilot", + env: ["GITHUB_TOKEN"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.githubcopilot.com", + name: "GitHub Copilot", + doc: "https://docs.github.com/en/copilot", + models: { + "gpt-5.1-codex-max": { + id: "gpt-5.1-codex-max", + name: "GPT-5.1-Codex-max", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-12-04", + last_updated: "2025-12-04", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 400000, input: 128000, output: 128000 }, + status: "deprecated", + }, + "claude-opus-4.6": { + id: "claude-opus-4.6", + name: "Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 144000, input: 128000, output: 64000 }, + }, + "gemini-3.1-pro-preview": { + id: "gemini-3.1-pro-preview", + name: "Gemini 3.1 Pro Preview", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-19", + last_updated: "2026-02-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, input: 128000, output: 64000 }, + }, + "gemini-3-flash-preview": { + id: "gemini-3-flash-preview", + name: "Gemini 3 Flash", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, input: 128000, output: 64000 }, + }, + "gpt-5.5": { + id: "gpt-5.5", + name: "GPT-5.5", + family: "gpt", + attachment: false, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gpt-5-mini": { + id: "gpt-5-mini", + name: "GPT-5-mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-06", + release_date: "2025-08-13", + last_updated: "2025-08-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 264000, input: 128000, output: 64000 }, + }, + "gemini-3-pro-preview": { + id: "gemini-3-pro-preview", + name: "Gemini 3 Pro Preview", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-11-18", + last_updated: "2025-11-18", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, input: 128000, output: 64000 }, + status: "deprecated", + }, + "gpt-5.3-codex": { + id: "gpt-5.3-codex", + name: "GPT-5.3-Codex", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-02-24", + last_updated: "2026-02-24", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gemini-2.5-pro": { + id: "gemini-2.5-pro", + name: "Gemini 2.5 Pro", + family: "gemini-pro", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-20", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, input: 128000, output: 64000 }, + }, + "gpt-5.2": { + id: "gpt-5.2", + name: "GPT-5.2", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 264000, input: 128000, output: 64000 }, + }, + "gpt-5.4-mini": { + id: "gpt-5.4-mini", + name: "GPT-5.4 Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "claude-opus-4.7": { + id: "claude-opus-4.7", + name: "Claude Opus 4.7", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 144000, input: 128000, output: 64000 }, + }, + "gpt-5.2-codex": { + id: "gpt-5.2-codex", + name: "GPT-5.2-Codex", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gpt-5.1-codex-mini": { + id: "gpt-5.1-codex-mini", + name: "GPT-5.1-Codex-mini", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 400000, input: 128000, output: 128000 }, + status: "deprecated", + }, + "claude-sonnet-4": { + id: "claude-sonnet-4", + name: "Claude Sonnet 4", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 216000, input: 128000, output: 16000 }, + status: "deprecated", + }, + "grok-code-fast-1": { + id: "grok-code-fast-1", + name: "Grok Code Fast 1", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08", + release_date: "2025-08-27", + last_updated: "2025-08-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, input: 128000, output: 64000 }, + }, + "gpt-5.1": { + id: "gpt-5.1", + name: "GPT-5.1", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 264000, input: 128000, output: 64000 }, + status: "deprecated", + }, + "claude-sonnet-4.5": { + id: "claude-sonnet-4.5", + name: "Claude Sonnet 4.5", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 144000, input: 128000, output: 32000 }, + }, + "claude-opus-41": { + id: "claude-opus-41", + name: "Claude Opus 4.1", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 80000, output: 16000 }, + status: "deprecated", + }, + "claude-opus-4.5": { + id: "claude-opus-4.5", + name: "Claude Opus 4.5", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-24", + last_updated: "2025-08-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 160000, input: 128000, output: 32000 }, + }, + "gpt-5.4": { + id: "gpt-5.4", + name: "GPT-5.4", + family: "gpt", + attachment: false, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gpt-4o": { + id: "gpt-4o", + name: "GPT-4o", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-09", + release_date: "2024-05-13", + last_updated: "2024-05-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, input: 64000, output: 4096 }, + }, + "gpt-5": { + id: "gpt-5", + name: "GPT-5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 128000 }, + status: "deprecated", + }, + "claude-haiku-4.5": { + id: "claude-haiku-4.5", + name: "Claude Haiku 4.5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 144000, input: 128000, output: 32000 }, + }, + "gpt-4.1": { + id: "gpt-4.1", + name: "GPT-4.1", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, input: 64000, output: 16384 }, + }, + "claude-sonnet-4.6": { + id: "claude-sonnet-4.6", + name: "Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-02-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 200000, input: 128000, output: 32000 }, + }, + "gpt-5.1-codex": { + id: "gpt-5.1-codex", + name: "GPT-5.1-Codex", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 400000, input: 128000, output: 128000 }, + status: "deprecated", + }, + }, + }, + mixlayer: { + id: "mixlayer", + env: ["MIXLAYER_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://models.mixlayer.ai/v1", + name: "Mixlayer", + doc: "https://docs.mixlayer.com", + models: { + "qwen/qwen3.5-122b-a10b": { + id: "qwen/qwen3.5-122b-a10b", + name: "Qwen3.5 122B A10B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 3.2 }, + limit: { context: 262144, output: 262144 }, + }, + "qwen/qwen3.5-27b": { + id: "qwen/qwen3.5-27b", + name: "Qwen3.5 27B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 2.4 }, + limit: { context: 262144, output: 262144 }, + }, + "qwen/qwen3.5-397b-a17b": { + id: "qwen/qwen3.5-397b-a17b", + name: "Qwen3.5 397B A17B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3.6 }, + limit: { context: 262144, output: 262144 }, + }, + "qwen/qwen3.5-9b": { + id: "qwen/qwen3.5-9b", + name: "Qwen3.5 9B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 262144, output: 262144 }, + }, + "qwen/qwen3.5-35b-a3b": { + id: "qwen/qwen3.5-35b-a3b", + name: "Qwen3.5 35B A3B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 1.3 }, + limit: { context: 262144, output: 262144 }, + }, + }, + }, + "xiaomi-token-plan-sgp": { + id: "xiaomi-token-plan-sgp", + env: ["XIAOMI_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://token-plan-sgp.xiaomimimo.com/v1", + name: "Xiaomi Token Plan (Singapore)", + doc: "https://platform.xiaomimimo.com/#/docs", + models: { + "mimo-v2-tts": { + id: "mimo-v2-tts", + name: "MiMo-V2-TTS", + family: "mimo", + attachment: false, + reasoning: false, + tool_call: false, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["audio"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 8192, output: 16384 }, + }, + "mimo-v2-flash": { + id: "mimo-v2-flash", + name: "MiMo-V2-Flash", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12-01", + release_date: "2025-12-16", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0 }, + limit: { context: 262144, output: 65536 }, + }, + "mimo-v2-pro": { + id: "mimo-v2-pro", + name: "MiMo-V2-Pro", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, context_over_200k: { input: 2, output: 6, cache_read: 0.4 } }, + limit: { context: 1048576, output: 131072 }, + }, + "mimo-v2.5": { + id: "mimo-v2.5", + name: "MiMo-V2.5", + family: "mimo", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, context_over_200k: { input: 0, output: 0, cache_read: 0 } }, + limit: { context: 1048576, output: 131072 }, + }, + "mimo-v2-omni": { + id: "mimo-v2-omni", + name: "MiMo-V2-Omni", + family: "mimo", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0 }, + limit: { context: 262144, output: 131072 }, + }, + "mimo-v2.5-pro": { + id: "mimo-v2.5-pro", + name: "MiMo-V2.5-Pro", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, context_over_200k: { input: 0, output: 0, cache_read: 0 } }, + limit: { context: 1048576, output: 131072 }, + }, + }, + }, + zai: { + id: "zai", + env: ["ZHIPU_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.z.ai/api/paas/v4", + name: "Z.AI", + doc: "https://docs.z.ai/guides/overview/pricing", + models: { + "glm-5v-turbo": { + id: "glm-5v-turbo", + name: "GLM-5V-Turbo", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-04-01", + last_updated: "2026-04-01", + modalities: { input: ["text", "image", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.2, output: 4, cache_read: 0.24, cache_write: 0 }, + limit: { context: 200000, output: 131072 }, + }, + "glm-4.7": { + id: "glm-4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2, cache_read: 0.11, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "glm-5": { + id: "glm-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3.2, cache_read: 0.2, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "glm-4.7-flashx": { + id: "glm-4.7-flashx", + name: "GLM-4.7-FlashX", + family: "glm-flash", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-01-19", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.4, cache_read: 0.01, cache_write: 0 }, + limit: { context: 200000, output: 131072 }, + }, + "glm-5.1": { + id: "glm-5.1", + name: "GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-03-27", + last_updated: "2026-03-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.4, output: 4.4, cache_read: 0.26, cache_write: 0 }, + limit: { context: 200000, output: 131072 }, + }, + "glm-4.5": { + id: "glm-4.5", + name: "GLM-4.5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2, cache_read: 0.11, cache_write: 0 }, + limit: { context: 131072, output: 98304 }, + }, + "glm-4.5-air": { + id: "glm-4.5-air", + name: "GLM-4.5-Air", + family: "glm-air", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 1.1, cache_read: 0.03, cache_write: 0 }, + limit: { context: 131072, output: 98304 }, + }, + "glm-5-turbo": { + id: "glm-5-turbo", + name: "GLM-5-Turbo", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-03-16", + last_updated: "2026-03-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.2, output: 4, cache_read: 0.24, cache_write: 0 }, + limit: { context: 200000, output: 131072 }, + }, + "glm-4.5v": { + id: "glm-4.5v", + name: "GLM-4.5V", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-08-11", + last_updated: "2025-08-11", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 1.8 }, + limit: { context: 64000, output: 16384 }, + }, + "glm-4.6": { + id: "glm-4.6", + name: "GLM-4.6", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2, cache_read: 0.11, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "glm-4.6v": { + id: "glm-4.6v", + name: "GLM-4.6V", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-08", + last_updated: "2025-12-08", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.9 }, + limit: { context: 128000, output: 32768 }, + }, + "glm-4.5-flash": { + id: "glm-4.5-flash", + name: "GLM-4.5-Flash", + family: "glm-flash", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 131072, output: 98304 }, + }, + "glm-4.7-flash": { + id: "glm-4.7-flash", + name: "GLM-4.7-Flash", + family: "glm-flash", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-01-19", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 200000, output: 131072 }, + }, + }, + }, + opencode: { + id: "opencode", + env: ["OPENCODE_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://opencode.ai/zen/v1", + name: "OpenCode Zen", + doc: "https://opencode.ai/docs/zen", + models: { + "minimax-m2.7": { + id: "minimax-m2.7", + name: "MiniMax M2.7", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-01", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.06 }, + limit: { context: 204800, output: 131072 }, + }, + "gpt-5.1-codex-max": { + id: "gpt-5.1-codex-max", + name: "GPT-5.1 Codex Max", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, input: 272000, output: 128000 }, + provider: { npm: "@ai-sdk/openai" }, + }, + "claude-haiku-4-5": { + id: "claude-haiku-4-5", + name: "Claude Haiku 4.5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + provider: { npm: "@ai-sdk/anthropic" }, + }, + "kimi-k2.5": { + id: "kimi-k2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-10", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3, cache_read: 0.08 }, + limit: { context: 262144, output: 65536 }, + }, + "glm-4.7": { + id: "glm-4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2, cache_read: 0.1 }, + limit: { context: 204800, output: 131072 }, + status: "deprecated", + }, + "glm-5": { + id: "glm-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3.2, cache_read: 0.2 }, + limit: { context: 204800, output: 131072 }, + }, + "glm-4.7-free": { + id: "glm-4.7-free", + name: "GLM-4.7 Free", + family: "glm-free", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0 }, + limit: { context: 204800, output: 131072 }, + status: "deprecated", + }, + "gemini-3.1-pro": { + id: "gemini-3.1-pro", + name: "Gemini 3.1 Pro Preview", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-19", + last_updated: "2026-02-19", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2, context_over_200k: { input: 4, output: 18, cache_read: 0.4 } }, + limit: { context: 1048576, output: 65536 }, + provider: { npm: "@ai-sdk/google" }, + }, + "claude-sonnet-4-6": { + id: "claude-sonnet-4-6", + name: "Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-02-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 1000000, output: 64000 }, + provider: { npm: "@ai-sdk/anthropic" }, + }, + "kimi-k2.5-free": { + id: "kimi-k2.5-free", + name: "Kimi K2.5 Free", + family: "kimi-free", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-10", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0 }, + limit: { context: 262144, output: 262144 }, + status: "deprecated", + }, + "claude-opus-4-7": { + id: "claude-opus-4-7", + name: "Claude Opus 4.7", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + provider: { npm: "@ai-sdk/anthropic" }, + }, + "gpt-5-nano": { + id: "gpt-5-nano", + name: "GPT-5 Nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.4, cache_read: 0.005 }, + limit: { context: 400000, input: 272000, output: 128000 }, + provider: { npm: "@ai-sdk/openai" }, + }, + "gpt-5.3-codex": { + id: "gpt-5.3-codex", + name: "GPT-5.3 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-02-24", + last_updated: "2026-02-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, input: 272000, output: 128000 }, + provider: { npm: "@ai-sdk/openai" }, + }, + "minimax-m2.5-free": { + id: "minimax-m2.5-free", + name: "MiniMax M2.5 Free", + family: "minimax-free", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0 }, + limit: { context: 204800, output: 131072 }, + provider: { npm: "@ai-sdk/anthropic" }, + }, + "ring-2.6-1t-free": { + id: "ring-2.6-1t-free", + name: "Ring 2.6 1T Free", + family: "ring-1t-free", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-06", + release_date: "2026-05-08", + last_updated: "2026-05-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262000, output: 66000 }, + }, + "gpt-5.2": { + id: "gpt-5.2", + name: "GPT-5.2", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, input: 272000, output: 128000 }, + provider: { npm: "@ai-sdk/openai" }, + }, + "big-pickle": { + id: "big-pickle", + name: "Big Pickle", + family: "big-pickle", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-10-17", + last_updated: "2025-10-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 200000, output: 128000 }, + }, + "claude-opus-4-1": { + id: "claude-opus-4-1", + name: "Claude Opus 4.1", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + provider: { npm: "@ai-sdk/anthropic" }, + }, + "qwen3.6-plus": { + id: "qwen3.6-plus", + name: "Qwen3.6 Plus", + family: "qwen3.6", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 3, cache_read: 0.05, cache_write: 0.625 }, + limit: { context: 262144, output: 65536 }, + provider: { npm: "@ai-sdk/anthropic" }, + }, + "gpt-5.4-mini": { + id: "gpt-5.4-mini", + name: "GPT-5.4 Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.75, output: 4.5, cache_read: 0.075 }, + limit: { context: 400000, input: 272000, output: 128000 }, + provider: { npm: "@ai-sdk/openai" }, + }, + "claude-3-5-haiku": { + id: "claude-3-5-haiku", + name: "Claude Haiku 3.5", + family: "claude-haiku", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07-31", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 4, cache_read: 0.08, cache_write: 1 }, + limit: { context: 200000, output: 8192 }, + status: "deprecated", + provider: { npm: "@ai-sdk/anthropic" }, + }, + "minimax-m2.1": { + id: "minimax-m2.1", + name: "MiniMax M2.1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-01", + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.1 }, + limit: { context: 204800, output: 131072 }, + status: "deprecated", + }, + "glm-5.1": { + id: "glm-5.1", + name: "GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2026-04-07", + last_updated: "2026-04-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.4, output: 4.4, cache_read: 0.26 }, + limit: { context: 204800, output: 131072 }, + }, + "gpt-5.4-nano": { + id: "gpt-5.4-nano", + name: "GPT-5.4 Nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.25, cache_read: 0.02 }, + limit: { context: 400000, input: 272000, output: 128000 }, + provider: { npm: "@ai-sdk/openai" }, + }, + "gpt-5.2-codex": { + id: "gpt-5.2-codex", + name: "GPT-5.2 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-01-14", + last_updated: "2026-01-14", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, input: 272000, output: 128000 }, + provider: { npm: "@ai-sdk/openai" }, + }, + "gpt-5.1-codex-mini": { + id: "gpt-5.1-codex-mini", + name: "GPT-5.1 Codex Mini", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.025 }, + limit: { context: 400000, input: 272000, output: 128000 }, + provider: { npm: "@ai-sdk/openai" }, + }, + "claude-sonnet-4": { + id: "claude-sonnet-4", + name: "Claude Sonnet 4", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 3, + output: 15, + cache_read: 0.3, + cache_write: 3.75, + context_over_200k: { input: 6, output: 22.5, cache_read: 0.6, cache_write: 7.5 }, + }, + limit: { context: 1000000, output: 64000 }, + provider: { npm: "@ai-sdk/anthropic" }, + }, + "gemini-3-flash": { + id: "gemini-3-flash", + name: "Gemini 3 Flash", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 3, cache_read: 0.05 }, + limit: { context: 1048576, output: 65536 }, + provider: { npm: "@ai-sdk/google" }, + }, + "trinity-large-preview-free": { + id: "trinity-large-preview-free", + name: "Trinity Large Preview", + family: "trinity", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2026-01-28", + last_updated: "2026-01-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 131072 }, + status: "deprecated", + }, + "gpt-5.1": { + id: "gpt-5.1", + name: "GPT-5.1", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.07, output: 8.5, cache_read: 0.107 }, + limit: { context: 400000, input: 272000, output: 128000 }, + provider: { npm: "@ai-sdk/openai" }, + }, + "gpt-5.4-pro": { + id: "gpt-5.4-pro", + name: "GPT-5.4 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 180, cache_read: 30 }, + limit: { context: 1050000, input: 922000, output: 128000 }, + provider: { npm: "@ai-sdk/openai" }, + }, + "glm-5-free": { + id: "glm-5-free", + name: "GLM-5 Free", + family: "glm-free", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0 }, + limit: { context: 204800, output: 131072 }, + status: "deprecated", + }, + "claude-opus-4-5": { + id: "claude-opus-4-5", + name: "Claude Opus 4.5", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-24", + last_updated: "2025-11-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 64000 }, + provider: { npm: "@ai-sdk/anthropic" }, + }, + "minimax-m2.1-free": { + id: "minimax-m2.1-free", + name: "MiniMax M2.1 Free", + family: "minimax-free", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0 }, + limit: { context: 204800, output: 131072 }, + status: "deprecated", + provider: { npm: "@ai-sdk/anthropic" }, + }, + "qwen3.6-plus-free": { + id: "qwen3.6-plus-free", + name: "Qwen3.6 Plus Free", + family: "qwen-free", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-30", + last_updated: "2026-03-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0 }, + limit: { context: 1048576, output: 64000 }, + status: "deprecated", + }, + "glm-4.6": { + id: "glm-4.6", + name: "GLM-4.6", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2, cache_read: 0.1 }, + limit: { context: 204800, output: 131072 }, + status: "deprecated", + }, + "ling-2.6-flash-free": { + id: "ling-2.6-flash-free", + name: "Ling 2.6 Flash Free", + family: "ling-flash-free", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262100, output: 32800 }, + status: "deprecated", + }, + "gemini-3-pro": { + id: "gemini-3-pro", + name: "Gemini 3 Pro", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-11-18", + last_updated: "2025-11-18", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2, context_over_200k: { input: 4, output: 18, cache_read: 0.4 } }, + limit: { context: 1048576, output: 65536 }, + status: "deprecated", + provider: { npm: "@ai-sdk/google" }, + }, + "kimi-k2.6": { + id: "kimi-k2.6", + name: "Kimi K2.6", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-10", + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4, cache_read: 0.16 }, + limit: { context: 262144, output: 65536 }, + }, + "gpt-5-codex": { + id: "gpt-5-codex", + name: "GPT-5 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-09-15", + last_updated: "2025-09-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.07, output: 8.5, cache_read: 0.107 }, + limit: { context: 400000, input: 272000, output: 128000 }, + provider: { npm: "@ai-sdk/openai" }, + }, + "grok-code": { + id: "grok-code", + name: "Grok Code Fast 1", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-20", + last_updated: "2025-08-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 256000, output: 256000 }, + status: "deprecated", + }, + "mimo-v2-flash-free": { + id: "mimo-v2-flash-free", + name: "MiMo V2 Flash Free", + family: "mimo-flash-free", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2025-12-16", + last_updated: "2025-12-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0 }, + limit: { context: 262144, output: 65536 }, + status: "deprecated", + }, + "gpt-5.3-codex-spark": { + id: "gpt-5.3-codex-spark", + name: "GPT-5.3 Codex Spark", + family: "gpt-codex-spark", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 128000, input: 128000, output: 128000 }, + provider: { npm: "@ai-sdk/openai" }, + }, + "hy3-preview-free": { + id: "hy3-preview-free", + name: "Hy3 preview Free", + family: "hy3-free", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2026-04-20", + last_updated: "2026-04-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0 }, + limit: { context: 256000, output: 64000 }, + status: "deprecated", + }, + "minimax-m2.5": { + id: "minimax-m2.5", + name: "MiniMax M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.06 }, + limit: { context: 204800, output: 131072 }, + }, + "kimi-k2": { + id: "kimi-k2", + name: "Kimi K2", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 2.5, cache_read: 0.4 }, + limit: { context: 262144, output: 262144 }, + status: "deprecated", + }, + "claude-sonnet-4-5": { + id: "claude-sonnet-4-5", + name: "Claude Sonnet 4.5", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 3, + output: 15, + cache_read: 0.3, + cache_write: 3.75, + context_over_200k: { input: 6, output: 22.5, cache_read: 0.6, cache_write: 7.5 }, + }, + limit: { context: 1000000, output: 64000 }, + provider: { npm: "@ai-sdk/anthropic" }, + }, + "qwen3-coder": { + id: "qwen3-coder", + name: "Qwen3 Coder", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.45, output: 1.8 }, + limit: { context: 262144, output: 65536 }, + status: "deprecated", + }, + "gpt-5": { + id: "gpt-5", + name: "GPT-5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.07, output: 8.5, cache_read: 0.107 }, + limit: { context: 400000, input: 272000, output: 128000 }, + provider: { npm: "@ai-sdk/openai" }, + }, + "qwen3.5-plus": { + id: "qwen3.5-plus", + name: "Qwen3.5 Plus", + family: "qwen3.5", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-16", + last_updated: "2026-02-16", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.2, cache_read: 0.02, cache_write: 0.25 }, + limit: { context: 262144, output: 65536 }, + provider: { npm: "@ai-sdk/anthropic" }, + }, + "mimo-v2-pro-free": { + id: "mimo-v2-pro-free", + name: "MiMo V2 Pro Free", + family: "mimo-pro-free", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0 }, + limit: { context: 1048576, output: 64000 }, + status: "deprecated", + }, + "nemotron-3-super-free": { + id: "nemotron-3-super-free", + name: "Nemotron 3 Super Free", + family: "nemotron-free", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2026-02", + release_date: "2026-03-11", + last_updated: "2026-03-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0 }, + limit: { context: 204800, output: 128000 }, + }, + "gpt-5.5-pro": { + id: "gpt-5.5-pro", + name: "GPT-5.5 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: false, + knowledge: "2025-12-01", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 180, cache_read: 30 }, + limit: { context: 1050000, input: 922000, output: 128000 }, + provider: { npm: "@ai-sdk/openai" }, + }, + "kimi-k2-thinking": { + id: "kimi-k2-thinking", + name: "Kimi K2 Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-10", + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 2.5, cache_read: 0.4 }, + limit: { context: 262144, output: 262144 }, + status: "deprecated", + }, + "gpt-5.1-codex": { + id: "gpt-5.1-codex", + name: "GPT-5.1 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.07, output: 8.5, cache_read: 0.107 }, + limit: { context: 400000, input: 272000, output: 128000 }, + provider: { npm: "@ai-sdk/openai" }, + }, + "mimo-v2-omni-free": { + id: "mimo-v2-omni-free", + name: "MiMo V2 Omni Free", + family: "mimo-omni-free", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text", "image", "audio", "pdf"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0 }, + limit: { context: 262144, output: 64000 }, + status: "deprecated", + }, + "gpt-5.5": { + id: "gpt-5.5", + name: "GPT-5.5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-12-01", + release_date: "2026-04-23", + last_updated: "2026-04-23", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 30, cache_read: 0.5, context_over_200k: { input: 10, output: 45, cache_read: 1 } }, + limit: { context: 1050000, input: 922000, output: 128000 }, + provider: { npm: "@ai-sdk/openai" }, + }, + "gpt-5.4": { + id: "gpt-5.4", + name: "GPT-5.4", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 2.5, + output: 15, + cache_read: 0.25, + context_over_200k: { input: 5, output: 22.5, cache_read: 0.5 }, + }, + limit: { context: 1050000, input: 922000, output: 128000 }, + provider: { npm: "@ai-sdk/openai" }, + }, + "claude-opus-4-6": { + id: "claude-opus-4-6", + name: "Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + provider: { npm: "@ai-sdk/anthropic" }, + }, + }, + }, + stepfun: { + id: "stepfun", + env: ["STEPFUN_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.stepfun.com/v1", + name: "StepFun", + doc: "https://platform.stepfun.com/docs/zh/overview/concept", + models: { + "step-3.5-flash-2603": { + id: "step-3.5-flash-2603", + name: "Step 3.5 Flash 2603", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3, cache_read: 0.02 }, + limit: { context: 256000, input: 256000, output: 256000 }, + }, + "step-1-32k": { + id: "step-1-32k", + name: "Step 1 (32K)", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-06", + release_date: "2025-01-01", + last_updated: "2026-02-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.05, output: 9.59, cache_read: 0.41 }, + limit: { context: 32768, input: 32768, output: 32768 }, + }, + "step-3.5-flash": { + id: "step-3.5-flash", + name: "Step 3.5 Flash", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-01-29", + last_updated: "2026-02-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.096, output: 0.288, cache_read: 0.019 }, + limit: { context: 256000, input: 256000, output: 256000 }, + }, + "step-2-16k": { + id: "step-2-16k", + name: "Step 2 (16K)", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-06", + release_date: "2025-01-01", + last_updated: "2026-02-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 5.21, output: 16.44, cache_read: 1.04 }, + limit: { context: 16384, input: 16384, output: 8192 }, + }, + }, + }, + nebius: { + id: "nebius", + env: ["NEBIUS_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.tokenfactory.nebius.com/v1", + name: "Nebius Token Factory", + doc: "https://docs.tokenfactory.nebius.com/", + models: { + "NousResearch/Hermes-4-70B": { + id: "NousResearch/Hermes-4-70B", + name: "Hermes-4-70B", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-11", + release_date: "2026-01-30", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.13, output: 0.4, reasoning: 0.4, cache_read: 0.013, cache_write: 0.16 }, + limit: { context: 128000, input: 120000, output: 8192 }, + }, + "NousResearch/Hermes-4-405B": { + id: "NousResearch/Hermes-4-405B", + name: "Hermes-4-405B", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-11", + release_date: "2026-01-30", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3, reasoning: 3, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 128000, input: 120000, output: 8192 }, + }, + "Qwen/Qwen2.5-VL-72B-Instruct": { + id: "Qwen/Qwen2.5-VL-72B-Instruct", + name: "Qwen2.5-VL-72B-Instruct", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-01-20", + last_updated: "2026-02-04", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 0.75, cache_read: 0.025, cache_write: 0.31 }, + limit: { context: 128000, input: 120000, output: 8192 }, + }, + "Qwen/Qwen3.5-397B-A17B": { + id: "Qwen/Qwen3.5-397B-A17B", + name: "Qwen3.5-397B-A17B", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-07-15", + last_updated: "2026-05-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3.6, cache_read: 0.06, cache_write: 0.75 }, + limit: { context: 262144, input: 250000, output: 8192 }, + }, + "Qwen/Qwen3-Embedding-8B": { + id: "Qwen/Qwen3-Embedding-8B", + name: "Qwen3-Embedding-8B", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: false, + knowledge: "2025-10", + release_date: "2026-01-10", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.01, output: 0 }, + limit: { context: 32768, input: 32768, output: 0 }, + }, + "Qwen/Qwen3-30B-A3B-Instruct-2507": { + id: "Qwen/Qwen3-30B-A3B-Instruct-2507", + name: "Qwen3-30B-A3B-Instruct-2507", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-12", + release_date: "2026-01-28", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3, cache_read: 0.01, cache_write: 0.125 }, + limit: { context: 128000, input: 120000, output: 8192 }, + }, + "Qwen/Qwen3-235B-A22B-Instruct-2507": { + id: "Qwen/Qwen3-235B-A22B-Instruct-2507", + name: "Qwen3 235B A22B Instruct 2507", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-07-25", + last_updated: "2025-10-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.6 }, + limit: { context: 262144, output: 8192 }, + }, + "Qwen/Qwen3-32B": { + id: "Qwen/Qwen3-32B", + name: "Qwen3-32B", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-12", + release_date: "2026-01-28", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3, cache_read: 0.01, cache_write: 0.125 }, + limit: { context: 128000, input: 120000, output: 8192 }, + }, + "Qwen/Qwen3-235B-A22B-Thinking-2507-fast": { + id: "Qwen/Qwen3-235B-A22B-Thinking-2507-fast", + name: "Qwen3-235B-A22B-Thinking-2507-fast", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-07-25", + last_updated: "2026-05-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 2, cache_read: 0.05, cache_write: 0.625 }, + limit: { context: 8000, input: 7000, output: 8192 }, + }, + "Qwen/Qwen3.5-397B-A17B-fast": { + id: "Qwen/Qwen3.5-397B-A17B-fast", + name: "Qwen3.5-397B-A17B-fast", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-07-15", + last_updated: "2026-05-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3.6, cache_read: 0.06, cache_write: 0.75 }, + limit: { context: 8000, input: 7000, output: 8192 }, + }, + "Qwen/Qwen3-Next-80B-A3B-Thinking-fast": { + id: "Qwen/Qwen3-Next-80B-A3B-Thinking-fast", + name: "Qwen3-Next-80B-A3B-Thinking-fast", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-07-25", + last_updated: "2026-05-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 1.2, cache_read: 0.015, cache_write: 0.1875 }, + limit: { context: 8000, input: 7000, output: 8192 }, + }, + "Qwen/Qwen3-Next-80B-A3B-Thinking": { + id: "Qwen/Qwen3-Next-80B-A3B-Thinking", + name: "Qwen3-Next-80B-A3B-Thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-12", + release_date: "2026-01-28", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 1.2, reasoning: 1.2, cache_read: 0.015, cache_write: 0.18 }, + limit: { context: 128000, input: 120000, output: 16384 }, + }, + "PrimeIntellect/INTELLECT-3": { + id: "PrimeIntellect/INTELLECT-3", + name: "INTELLECT-3", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-10", + release_date: "2026-01-25", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 1.1, cache_read: 0.02, cache_write: 0.25 }, + limit: { context: 128000, input: 120000, output: 8192 }, + }, + "zai-org/GLM-5": { + id: "zai-org/GLM-5", + name: "GLM-5", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2026-01", + release_date: "2026-03-01", + last_updated: "2026-03-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 3.2, cache_read: 0.1, cache_write: 1 }, + limit: { context: 200000, input: 200000, output: 16384 }, + }, + "meta-llama/Llama-3.3-70B-Instruct": { + id: "meta-llama/Llama-3.3-70B-Instruct", + name: "Llama-3.3-70B-Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-08", + release_date: "2025-12-05", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.13, output: 0.4, cache_read: 0.013, cache_write: 0.16 }, + limit: { context: 128000, input: 120000, output: 8192 }, + }, + "meta-llama/Meta-Llama-3.1-8B-Instruct": { + id: "meta-llama/Meta-Llama-3.1-8B-Instruct", + name: "Meta-Llama-3.1-8B-Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-12", + release_date: "2024-07-23", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0.06, cache_read: 0.002, cache_write: 0.025 }, + limit: { context: 128000, input: 120000, output: 4096 }, + }, + "nvidia/nemotron-3-super-120b-a12b": { + id: "nvidia/nemotron-3-super-120b-a12b", + name: "Nemotron-3-Super-120B-A12B", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2026-02", + release_date: "2026-03-11", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.9 }, + limit: { context: 256000, input: 256000, output: 32768 }, + }, + "nvidia/Llama-3_1-Nemotron-Ultra-253B-v1": { + id: "nvidia/Llama-3_1-Nemotron-Ultra-253B-v1", + name: "Llama-3.1-Nemotron-Ultra-253B-v1", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-01-15", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 1.8, cache_read: 0.06, cache_write: 0.75 }, + limit: { context: 128000, input: 120000, output: 4096 }, + }, + "nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B": { + id: "nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B", + name: "Nemotron-3-Nano-30B-A3B", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2025-08-10", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.06, output: 0.24, cache_read: 0.006, cache_write: 0.075 }, + limit: { context: 32000, input: 30000, output: 4096 }, + }, + "nvidia/Nemotron-3-Nano-Omni": { + id: "nvidia/Nemotron-3-Nano-Omni", + name: "Nemotron-3-Nano-Omni", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-01-20", + last_updated: "2026-05-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.06, output: 0.24, cache_read: 0.006, cache_write: 0.075 }, + limit: { context: 65536, input: 60000, output: 8192 }, + }, + "deepseek-ai/DeepSeek-V3.2-fast": { + id: "deepseek-ai/DeepSeek-V3.2-fast", + name: "DeepSeek-V3.2-fast", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-01-27", + last_updated: "2026-05-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 2, cache_read: 0.04, cache_write: 0.5 }, + limit: { context: 8000, input: 7000, output: 8192 }, + }, + "deepseek-ai/DeepSeek-V3.2": { + id: "deepseek-ai/DeepSeek-V3.2", + name: "DeepSeek-V3.2", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-11", + release_date: "2026-01-20", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.45, reasoning: 0.45, cache_read: 0.03, cache_write: 0.375 }, + limit: { context: 163000, input: 160000, output: 16384 }, + }, + "openai/gpt-oss-120b-fast": { + id: "openai/gpt-oss-120b-fast", + name: "gpt-oss-120b-fast", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-06-10", + last_updated: "2026-05-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.5, cache_read: 0.01, cache_write: 0.125 }, + limit: { context: 8000, input: 7000, output: 8192 }, + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "gpt-oss-120b", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-09", + release_date: "2026-01-10", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6, reasoning: 0.6, cache_read: 0.015, cache_write: 0.18 }, + limit: { context: 128000, input: 124000, output: 8192 }, + }, + "google/gemma-2-2b-it": { + id: "google/gemma-2-2b-it", + name: "Gemma-2-2b-it", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + knowledge: "2024-06", + release_date: "2024-07-31", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0.06, cache_read: 0.002, cache_write: 0.025 }, + limit: { context: 8192, input: 8000, output: 4096 }, + }, + "google/gemma-3-27b-it": { + id: "google/gemma-3-27b-it", + name: "Gemma-3-27b-it", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-10", + release_date: "2026-01-20", + last_updated: "2026-02-04", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3, cache_read: 0.01, cache_write: 0.125 }, + limit: { context: 110000, input: 100000, output: 8192 }, + }, + "moonshotai/Kimi-K2.5-fast": { + id: "moonshotai/Kimi-K2.5-fast", + name: "Kimi-K2.5-fast", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-12-15", + last_updated: "2026-02-04", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 2.5, cache_read: 0.05, cache_write: 0.625 }, + limit: { context: 256000, input: 256000, output: 8192 }, + }, + "moonshotai/Kimi-K2.5": { + id: "moonshotai/Kimi-K2.5", + name: "Kimi-K2.5", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-12-15", + last_updated: "2026-02-04", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 2.5, reasoning: 2.5, cache_read: 0.05, cache_write: 0.625 }, + limit: { context: 256000, input: 256000, output: 8192 }, + }, + "MiniMaxAI/MiniMax-M2.5": { + id: "MiniMaxAI/MiniMax-M2.5", + name: "MiniMax-M2.5", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-01-20", + last_updated: "2026-05-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.03, cache_write: 0.375 }, + limit: { context: 196608, input: 190000, output: 8192 }, + }, + "MiniMaxAI/MiniMax-M2.5-fast": { + id: "MiniMaxAI/MiniMax-M2.5-fast", + name: "MiniMax-M2.5-fast", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-01-20", + last_updated: "2026-05-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.03, cache_write: 0.375 }, + limit: { context: 8000, input: 7000, output: 8192 }, + }, + "deepseek-ai/DeepSeek-V4-Pro": { + id: "deepseek-ai/DeepSeek-V4-Pro", + name: "DeepSeek V4 Pro", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.75, output: 3.5, cache_read: 0.15 }, + limit: { context: 1000000, output: 384000 }, + }, + }, + }, + poe: { + id: "poe", + env: ["POE_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.poe.com/v1", + name: "Poe", + doc: "https://creator.poe.com/docs/external-applications/openai-compatible-api", + models: { + "topazlabs-co/topazlabs": { + id: "topazlabs-co/topazlabs", + name: "TopazLabs", + family: "topazlabs", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-12-03", + last_updated: "2024-12-03", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 204, output: 0 }, + }, + "novita/kimi-k2.5": { + id: "novita/kimi-k2.5", + name: "Kimi-K2.5", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 3, cache_read: 0.1 }, + limit: { context: 128000, output: 262144 }, + }, + "novita/glm-4.7": { + id: "novita/glm-4.7", + name: "glm-4.7", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 205000, output: 131072 }, + status: "deprecated", + }, + "novita/glm-5": { + id: "novita/glm-5", + name: "GLM-5", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-15", + last_updated: "2026-02-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 3.2, cache_read: 0.2 }, + limit: { context: 205000, output: 131072 }, + }, + "novita/minimax-m2.1": { + id: "novita/minimax-m2.1", + name: "minimax-m2.1", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-12-26", + last_updated: "2025-12-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 205000, output: 131072 }, + }, + "novita/glm-4.6": { + id: "novita/glm-4.6", + name: "GLM-4.6", + family: "glm", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 0, output: 0 }, + }, + "novita/kimi-k2.6": { + id: "novita/kimi-k2.6", + name: "Kimi-K2.6", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-04-20", + last_updated: "2026-05-02", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.96, output: 4.04, cache_read: 0.16 }, + limit: { context: 262144, input: 262144, output: 262144 }, + }, + "novita/glm-4.6v": { + id: "novita/glm-4.6v", + name: "glm-4.6v", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-12-09", + last_updated: "2025-12-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + limit: { context: 131000, output: 32768 }, + }, + "novita/deepseek-v3.2": { + id: "novita/deepseek-v3.2", + name: "DeepSeek-V3.2", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 0.4, cache_read: 0.13 }, + limit: { context: 128000, output: 0 }, + }, + "novita/glm-4.7-flash": { + id: "novita/glm-4.7-flash", + name: "glm-4.7-flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-01-19", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 200000, output: 65500 }, + }, + "novita/glm-4.7-n": { + id: "novita/glm-4.7-n", + name: "glm-4.7-n", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 205000, output: 131072 }, + }, + "novita/kimi-k2-thinking": { + id: "novita/kimi-k2-thinking", + name: "kimi-k2-thinking", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-11-07", + last_updated: "2025-11-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 256000, output: 0 }, + }, + "fireworks-ai/kimi-k2.5-fw": { + id: "fireworks-ai/kimi-k2.5-fw", + name: "Kimi-K2.5-FW", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 262144, input: 245760, output: 16384 }, + }, + "empiriolabs/deepseek-v4-pro-el": { + id: "empiriolabs/deepseek-v4-pro-el", + name: "DeepSeek-V4-Pro-EL", + attachment: true, + reasoning: true, + tool_call: true, + release_date: "2026-04-24", + last_updated: "2026-05-02", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.67, output: 3.33 }, + limit: { context: 1000000, input: 1000000, output: 384000 }, + }, + "empiriolabs/deepseek-v4-flash-el": { + id: "empiriolabs/deepseek-v4-flash-el", + name: "DeepSeek-V4-Flash-EL", + attachment: true, + reasoning: true, + tool_call: true, + release_date: "2026-04-24", + last_updated: "2026-05-02", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.28 }, + limit: { context: 1000000, input: 1000000, output: 384000 }, + }, + "elevenlabs/elevenlabs-v2.5-turbo": { + id: "elevenlabs/elevenlabs-v2.5-turbo", + name: "ElevenLabs-v2.5-Turbo", + family: "elevenlabs", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-10-28", + last_updated: "2024-10-28", + modalities: { input: ["text"], output: ["audio"] }, + open_weights: false, + limit: { context: 128000, output: 0 }, + }, + "elevenlabs/elevenlabs-v3": { + id: "elevenlabs/elevenlabs-v3", + name: "ElevenLabs-v3", + family: "elevenlabs", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-06-05", + last_updated: "2025-06-05", + modalities: { input: ["text"], output: ["audio"] }, + open_weights: false, + limit: { context: 128000, output: 0 }, + }, + "elevenlabs/elevenlabs-music": { + id: "elevenlabs/elevenlabs-music", + name: "ElevenLabs-Music", + family: "elevenlabs", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-08-29", + last_updated: "2025-08-29", + modalities: { input: ["text"], output: ["audio"] }, + open_weights: false, + limit: { context: 2000, output: 0 }, + }, + "cerebras/gpt-oss-120b-cs": { + id: "cerebras/gpt-oss-120b-cs", + name: "GPT-OSS-120B-CS", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-08-06", + last_updated: "2025-08-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.35, output: 0.75 }, + limit: { context: 128000, output: 0 }, + }, + "cerebras/llama-3.1-8b-cs": { + id: "cerebras/llama-3.1-8b-cs", + name: "Llama-3.1-8B-CS", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-05-13", + last_updated: "2025-05-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 128000, output: 0 }, + }, + "cerebras/qwen3-32b-cs": { + id: "cerebras/qwen3-32b-cs", + name: "qwen3-32b-cs", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-05-15", + last_updated: "2025-05-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 0, output: 0 }, + status: "deprecated", + }, + "cerebras/qwen3-235b-2507-cs": { + id: "cerebras/qwen3-235b-2507-cs", + name: "qwen3-235b-2507-cs", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-08-06", + last_updated: "2025-08-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 0, output: 0 }, + status: "deprecated", + }, + "cerebras/llama-3.3-70b-cs": { + id: "cerebras/llama-3.3-70b-cs", + name: "llama-3.3-70b-cs", + attachment: true, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-05-13", + last_updated: "2025-05-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 0, output: 0 }, + status: "deprecated", + }, + "stabilityai/stablediffusionxl": { + id: "stabilityai/stablediffusionxl", + name: "StableDiffusionXL", + family: "stable-diffusion", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2023-07-09", + last_updated: "2023-07-09", + modalities: { input: ["text", "image"], output: ["image"] }, + open_weights: false, + limit: { context: 200, output: 0 }, + }, + "xai/grok-code-fast-1": { + id: "xai/grok-code-fast-1", + name: "Grok Code Fast 1", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-08-22", + last_updated: "2025-08-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.5, cache_read: 0.02 }, + limit: { context: 256000, output: 128000 }, + }, + "xai/grok-4-fast-reasoning": { + id: "xai/grok-4-fast-reasoning", + name: "Grok-4-Fast-Reasoning", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-09-16", + last_updated: "2025-09-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 128000 }, + }, + "xai/grok-4.1-fast-non-reasoning": { + id: "xai/grok-4.1-fast-non-reasoning", + name: "Grok-4.1-Fast-Non-Reasoning", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-11-19", + last_updated: "2025-11-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + limit: { context: 2000000, output: 30000 }, + }, + "xai/grok-4": { + id: "xai/grok-4", + name: "Grok-4", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-07-10", + last_updated: "2025-07-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.75 }, + limit: { context: 256000, output: 128000 }, + }, + "xai/grok-3-mini": { + id: "xai/grok-3-mini", + name: "Grok 3 Mini", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-04-11", + last_updated: "2025-04-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.5, cache_read: 0.075 }, + limit: { context: 131072, output: 8192 }, + }, + "xai/grok-4.20-multi-agent": { + id: "xai/grok-4.20-multi-agent", + name: "Grok-4.20-Multi-Agent", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2026-03-13", + last_updated: "2026-03-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6, cache_read: 0.2 }, + limit: { context: 128000, output: 0 }, + }, + "xai/grok-3": { + id: "xai/grok-3", + name: "Grok 3", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-04-11", + last_updated: "2025-04-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.75 }, + limit: { context: 131072, output: 8192 }, + }, + "xai/grok-4-fast-non-reasoning": { + id: "xai/grok-4-fast-non-reasoning", + name: "Grok-4-Fast-Non-Reasoning", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-09-16", + last_updated: "2025-09-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 128000 }, + }, + "xai/grok-4.1-fast-reasoning": { + id: "xai/grok-4.1-fast-reasoning", + name: "Grok-4.1-Fast-Reasoning", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-11-19", + last_updated: "2025-11-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + limit: { context: 2000000, output: 30000 }, + }, + "runwayml/runway": { + id: "runwayml/runway", + name: "Runway", + family: "runway", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-10-11", + last_updated: "2024-10-11", + modalities: { input: ["text", "image"], output: ["video"] }, + open_weights: false, + limit: { context: 256, output: 0 }, + }, + "runwayml/runway-gen-4-turbo": { + id: "runwayml/runway-gen-4-turbo", + name: "Runway-Gen-4-Turbo", + family: "runway", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-05-09", + last_updated: "2025-05-09", + modalities: { input: ["text", "image"], output: ["video"] }, + open_weights: false, + limit: { context: 256, output: 0 }, + }, + "openai/gpt-5.1-codex-max": { + id: "openai/gpt-5.1-codex-max", + name: "GPT-5.1-Codex-Max", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-12-08", + last_updated: "2025-12-08", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 9, cache_read: 0.11 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/sora-2-pro": { + id: "openai/sora-2-pro", + name: "Sora-2-Pro", + family: "sora", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-10-06", + last_updated: "2025-10-06", + modalities: { input: ["text", "image"], output: ["video"] }, + open_weights: false, + limit: { context: 0, output: 0 }, + }, + "openai/chatgpt-4o-latest": { + id: "openai/chatgpt-4o-latest", + name: "ChatGPT-4o-Latest", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-08-14", + last_updated: "2024-08-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 4.5, output: 14 }, + limit: { context: 128000, output: 8192 }, + status: "deprecated", + }, + "openai/gpt-5-chat": { + id: "openai/gpt-5-chat", + name: "GPT-5-Chat", + family: "gpt-codex", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 9, cache_read: 0.11 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-5.2-pro": { + id: "openai/gpt-5.2-pro", + name: "GPT-5.2-Pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 19, output: 150 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-4o-aug": { + id: "openai/gpt-4o-aug", + name: "GPT-4o-Aug", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-11-21", + last_updated: "2024-11-21", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.2, output: 9, cache_read: 1.1 }, + limit: { context: 128000, output: 8192 }, + }, + "openai/gpt-image-2": { + id: "openai/gpt-image-2", + name: "GPT-Image-2", + attachment: true, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text", "image"], output: ["image"] }, + open_weights: false, + cost: { input: 5.0505, output: 32.3232, cache_read: 1.2626 }, + limit: { context: 0, output: 0 }, + }, + "openai/gpt-4-classic-0314": { + id: "openai/gpt-4-classic-0314", + name: "GPT-4-Classic-0314", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-08-26", + last_updated: "2024-08-26", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 27, output: 54 }, + limit: { context: 8192, output: 4096 }, + status: "deprecated", + }, + "openai/gpt-5-mini": { + id: "openai/gpt-5-mini", + name: "GPT-5-mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-06-25", + last_updated: "2025-06-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.22, output: 1.8, cache_read: 0.022 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5-nano": { + id: "openai/gpt-5-nano", + name: "GPT-5-nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.045, output: 0.36, cache_read: 0.0045 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.3-codex": { + id: "openai/gpt-5.3-codex", + name: "GPT-5.3-Codex", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-02-10", + last_updated: "2026-02-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.6, output: 13, cache_read: 0.16 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-4-turbo": { + id: "openai/gpt-4-turbo", + name: "GPT-4-Turbo", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2023-09-13", + last_updated: "2023-09-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 9, output: 27 }, + limit: { context: 128000, output: 4096 }, + }, + "openai/gpt-5.2": { + id: "openai/gpt-5.2", + name: "GPT-5.2", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-12-08", + last_updated: "2025-12-08", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.6, output: 13, cache_read: 0.16 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/o3-pro": { + id: "openai/o3-pro", + name: "o3-pro", + family: "o-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-06-10", + last_updated: "2025-06-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 18, output: 72 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/o3-mini-high": { + id: "openai/o3-mini-high", + name: "o3-mini-high", + family: "o-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-01-31", + last_updated: "2025-01-31", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.99, output: 4 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-4o-mini": { + id: "openai/gpt-4o-mini", + name: "GPT-4o-mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.54, cache_read: 0.068 }, + limit: { context: 124096, output: 4096 }, + }, + "openai/o4-mini-deep-research": { + id: "openai/o4-mini-deep-research", + name: "o4-mini-deep-research", + family: "o-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-06-27", + last_updated: "2025-06-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.8, output: 7.2, cache_read: 0.45 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-5.4-mini": { + id: "openai/gpt-5.4-mini", + name: "GPT-5.4-Mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-03-12", + last_updated: "2026-03-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.68, output: 4, cache_read: 0.068 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "openai/dall-e-3": { + id: "openai/dall-e-3", + name: "DALL-E-3", + family: "dall-e", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2023-11-06", + last_updated: "2023-11-06", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 800, output: 0 }, + }, + "openai/o4-mini": { + id: "openai/o4-mini", + name: "o4-mini", + family: "o-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.99, output: 4, cache_read: 0.25 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-5.4-nano": { + id: "openai/gpt-5.4-nano", + name: "GPT-5.4-Nano", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-03-11", + last_updated: "2026-03-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.18, output: 1.1, cache_read: 0.018 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "openai/gpt-image-1": { + id: "openai/gpt-image-1", + name: "GPT-Image-1", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-03-31", + last_updated: "2025-03-31", + modalities: { input: ["text", "image"], output: ["image"] }, + open_weights: false, + limit: { context: 128000, output: 0 }, + }, + "openai/gpt-5.2-codex": { + id: "openai/gpt-5.2-codex", + name: "GPT-5.2-Codex", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-01-14", + last_updated: "2026-01-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.6, output: 13, cache_read: 0.16 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.1-codex-mini": { + id: "openai/gpt-5.1-codex-mini", + name: "GPT-5.1-Codex-Mini", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-11-12", + last_updated: "2025-11-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.22, output: 1.8, cache_read: 0.022 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.1": { + id: "openai/gpt-5.1", + name: "GPT-5.1", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-11-12", + last_updated: "2025-11-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 9, cache_read: 0.11 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-image-1-mini": { + id: "openai/gpt-image-1-mini", + name: "GPT-Image-1-Mini", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-08-26", + last_updated: "2025-08-26", + modalities: { input: ["text", "image"], output: ["image"] }, + open_weights: false, + limit: { context: 0, output: 0 }, + }, + "openai/o1": { + id: "openai/o1", + name: "o1", + family: "o", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2024-12-18", + last_updated: "2024-12-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 14, output: 54 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-5.4-pro": { + id: "openai/gpt-5.4-pro", + name: "GPT-5.4-Pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image"], output: ["image"] }, + open_weights: false, + cost: { input: 27, output: 160 }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "openai/gpt-3.5-turbo": { + id: "openai/gpt-3.5-turbo", + name: "GPT-3.5-Turbo", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2023-09-13", + last_updated: "2023-09-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.45, output: 1.4 }, + limit: { context: 16384, output: 2048 }, + }, + "openai/o3-deep-research": { + id: "openai/o3-deep-research", + name: "o3-deep-research", + family: "o", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-06-27", + last_updated: "2025-06-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 9, output: 36, cache_read: 2.2 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/o3-mini": { + id: "openai/o3-mini", + name: "o3-mini", + family: "o-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-01-31", + last_updated: "2025-01-31", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.99, output: 4 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/o1-pro": { + id: "openai/o1-pro", + name: "o1-pro", + family: "o-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-03-19", + last_updated: "2025-03-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 140, output: 540 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-4o-search": { + id: "openai/gpt-4o-search", + name: "GPT-4o-Search", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-03-11", + last_updated: "2025-03-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.2, output: 9 }, + limit: { context: 128000, output: 8192 }, + }, + "openai/gpt-5-codex": { + id: "openai/gpt-5-codex", + name: "GPT-5-Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-09-23", + last_updated: "2025-09-23", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 9 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.4": { + id: "openai/gpt-5.4", + name: "GPT-5.4", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-02-26", + last_updated: "2026-02-26", + modalities: { input: ["text", "image", "pdf"], output: ["image"] }, + open_weights: false, + cost: { input: 2.2, output: 14, cache_read: 0.22 }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "openai/gpt-5.3-codex-spark": { + id: "openai/gpt-5.3-codex-spark", + name: "GPT-5.3-Codex-Spark", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-03-04", + last_updated: "2026-03-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-3.5-turbo-raw": { + id: "openai/gpt-3.5-turbo-raw", + name: "GPT-3.5-Turbo-Raw", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2023-09-27", + last_updated: "2023-09-27", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.45, output: 1.4 }, + limit: { context: 4524, output: 2048 }, + }, + "openai/gpt-4.1-nano": { + id: "openai/gpt-4.1-nano", + name: "GPT-4.1-nano", + family: "gpt-nano", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-04-15", + last_updated: "2025-04-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.09, output: 0.36, cache_read: 0.022 }, + limit: { context: 1047576, output: 32768 }, + }, + "openai/o3": { + id: "openai/o3", + name: "o3", + family: "o", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.8, output: 7.2, cache_read: 0.45 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-5-pro": { + id: "openai/gpt-5-pro", + name: "GPT-5-Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-10-06", + last_updated: "2025-10-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 14, output: 110 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/sora-2": { + id: "openai/sora-2", + name: "Sora-2", + family: "sora", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-10-06", + last_updated: "2025-10-06", + modalities: { input: ["text", "image"], output: ["video"] }, + open_weights: false, + limit: { context: 0, output: 0 }, + }, + "openai/gpt-4o": { + id: "openai/gpt-4o", + name: "GPT-4o", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-05-13", + last_updated: "2024-05-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 8192 }, + }, + "openai/gpt-5": { + id: "openai/gpt-5", + name: "GPT-5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 9, cache_read: 0.11 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.2-instant": { + id: "openai/gpt-5.2-instant", + name: "GPT-5.2-Instant", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.6, output: 13, cache_read: 0.16 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-4o-mini-search": { + id: "openai/gpt-4o-mini-search", + name: "GPT-4o-mini-Search", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-03-11", + last_updated: "2025-03-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.54 }, + limit: { context: 128000, output: 8192 }, + }, + "openai/gpt-image-1.5": { + id: "openai/gpt-image-1.5", + name: "gpt-image-1.5", + attachment: true, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-12-16", + last_updated: "2025-12-16", + modalities: { input: ["text", "image"], output: ["image"] }, + open_weights: false, + limit: { context: 128000, output: 0 }, + }, + "openai/gpt-3.5-turbo-instruct": { + id: "openai/gpt-3.5-turbo-instruct", + name: "GPT-3.5-Turbo-Instruct", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2023-09-20", + last_updated: "2023-09-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.4, output: 1.8 }, + limit: { context: 3500, output: 1024 }, + }, + "openai/gpt-4.1": { + id: "openai/gpt-4.1", + name: "GPT-4.1", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.8, output: 7.2, cache_read: 0.45 }, + limit: { context: 1047576, output: 32768 }, + }, + "openai/gpt-5.1-instant": { + id: "openai/gpt-5.1-instant", + name: "GPT-5.1-Instant", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-11-12", + last_updated: "2025-11-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 9, cache_read: 0.11 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-4.1-mini": { + id: "openai/gpt-4.1-mini", + name: "GPT-4.1-mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-04-15", + last_updated: "2025-04-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.36, output: 1.4, cache_read: 0.09 }, + limit: { context: 1047576, output: 32768 }, + }, + "openai/gpt-4-classic": { + id: "openai/gpt-4-classic", + name: "GPT-4-Classic", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-03-25", + last_updated: "2024-03-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 27, output: 54 }, + limit: { context: 8192, output: 4096 }, + status: "deprecated", + }, + "openai/gpt-5.1-codex": { + id: "openai/gpt-5.1-codex", + name: "GPT-5.1-Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-11-12", + last_updated: "2025-11-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 9, cache_read: 0.11 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.3-instant": { + id: "openai/gpt-5.3-instant", + name: "GPT-5.3-Instant", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2026-03-03", + last_updated: "2026-03-03", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.6, output: 13, cache_read: 0.16 }, + limit: { context: 128000, input: 111616, output: 16384 }, + }, + "google/veo-3-fast": { + id: "google/veo-3-fast", + name: "Veo-3-Fast", + family: "veo", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-10-13", + last_updated: "2025-10-13", + modalities: { input: ["text"], output: ["video"] }, + open_weights: false, + limit: { context: 480, output: 0 }, + }, + "google/veo-3.1-fast": { + id: "google/veo-3.1-fast", + name: "Veo-3.1-Fast", + family: "veo", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image"], output: ["video"] }, + open_weights: false, + limit: { context: 480, output: 0 }, + }, + "google/gemini-3.1-pro": { + id: "google/gemini-3.1-pro", + name: "Gemini-3.1-Pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-02-19", + last_updated: "2026-02-19", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/imagen-3-fast": { + id: "google/imagen-3-fast", + name: "Imagen-3-Fast", + family: "imagen", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-10-17", + last_updated: "2024-10-17", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 480, output: 0 }, + }, + "google/gemini-2.0-flash": { + id: "google/gemini-2.0-flash", + name: "Gemini-2.0-Flash", + family: "gemini-flash", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.42 }, + limit: { context: 990000, output: 8192 }, + }, + "google/gemini-deep-research": { + id: "google/gemini-deep-research", + name: "gemini-deep-research", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 1.6, output: 9.6 }, + limit: { context: 1048576, output: 0 }, + status: "deprecated", + }, + "google/gemini-2.5-pro": { + id: "google/gemini-2.5-pro", + name: "Gemini-2.5-Pro", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-02-05", + last_updated: "2025-02-05", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.87, output: 7, cache_read: 0.087 }, + limit: { context: 1065535, output: 65535 }, + }, + "google/imagen-3": { + id: "google/imagen-3", + name: "Imagen-3", + family: "imagen", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-10-15", + last_updated: "2024-10-15", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 480, output: 0 }, + }, + "google/nano-banana": { + id: "google/nano-banana", + name: "Nano-Banana", + family: "nano-banana", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-08-21", + last_updated: "2025-08-21", + modalities: { input: ["text", "image"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 0.21, output: 1.8, cache_read: 0.021 }, + limit: { context: 65536, output: 0 }, + }, + "google/gemini-2.5-flash": { + id: "google/gemini-2.5-flash", + name: "Gemini-2.5-Flash", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-04-26", + last_updated: "2025-04-26", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.21, output: 1.8, cache_read: 0.021 }, + limit: { context: 1065535, output: 65535 }, + }, + "google/gemini-3.1-flash-lite": { + id: "google/gemini-3.1-flash-lite", + name: "Gemini-3.1-Flash-Lite", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-02-18", + last_updated: "2026-02-18", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.5 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-3-flash": { + id: "google/gemini-3-flash", + name: "Gemini-3-Flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-10-07", + last_updated: "2025-10-07", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2.4, cache_read: 0.04 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/veo-3.1": { + id: "google/veo-3.1", + name: "Veo-3.1", + family: "veo", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text"], output: ["video"] }, + open_weights: false, + limit: { context: 480, output: 0 }, + }, + "google/lyria": { + id: "google/lyria", + name: "Lyria", + family: "lyria", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-06-04", + last_updated: "2025-06-04", + modalities: { input: ["text"], output: ["audio"] }, + open_weights: false, + limit: { context: 0, output: 0 }, + }, + "google/imagen-4-ultra": { + id: "google/imagen-4-ultra", + name: "Imagen-4-Ultra", + family: "imagen", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-05-24", + last_updated: "2025-05-24", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 480, output: 0 }, + }, + "google/nano-banana-pro": { + id: "google/nano-banana-pro", + name: "Nano-Banana-Pro", + family: "nano-banana", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-11-19", + last_updated: "2025-11-19", + modalities: { input: ["text", "image"], output: ["image"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2 }, + limit: { context: 65536, output: 0 }, + }, + "google/gemini-3-pro": { + id: "google/gemini-3-pro", + name: "Gemini-3-Pro", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-10-22", + last_updated: "2025-10-22", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 1.6, output: 9.6, cache_read: 0.16 }, + limit: { context: 1048576, output: 65536 }, + status: "deprecated", + }, + "google/imagen-4-fast": { + id: "google/imagen-4-fast", + name: "Imagen-4-Fast", + family: "imagen", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-06-25", + last_updated: "2025-06-25", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 480, output: 0 }, + }, + "google/veo-3": { + id: "google/veo-3", + name: "Veo-3", + family: "veo", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-05-21", + last_updated: "2025-05-21", + modalities: { input: ["text"], output: ["video"] }, + open_weights: false, + limit: { context: 480, output: 0 }, + }, + "google/gemini-2.5-flash-lite": { + id: "google/gemini-2.5-flash-lite", + name: "Gemini-2.5-Flash-Lite", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-06-19", + last_updated: "2025-06-19", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.07, output: 0.28 }, + limit: { context: 1024000, output: 64000 }, + }, + "google/imagen-4": { + id: "google/imagen-4", + name: "Imagen-4", + family: "imagen", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 480, output: 0 }, + }, + "google/gemma-4-31b": { + id: "google/gemma-4-31b", + name: "Gemma-4-31B", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 8192 }, + }, + "google/gemini-2.0-flash-lite": { + id: "google/gemini-2.0-flash-lite", + name: "Gemini-2.0-Flash-Lite", + family: "gemini-flash-lite", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-02-05", + last_updated: "2025-02-05", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.052, output: 0.21 }, + limit: { context: 990000, output: 8192 }, + }, + "google/veo-2": { + id: "google/veo-2", + name: "Veo-2", + family: "veo", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-12-02", + last_updated: "2024-12-02", + modalities: { input: ["text"], output: ["video"] }, + open_weights: false, + limit: { context: 480, output: 0 }, + }, + "lumalabs/ray2": { + id: "lumalabs/ray2", + name: "Ray2", + family: "ray", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-02-20", + last_updated: "2025-02-20", + modalities: { input: ["text", "image"], output: ["video"] }, + open_weights: false, + limit: { context: 5000, output: 0 }, + }, + "anthropic/claude-opus-4.1": { + id: "anthropic/claude-opus-4.1", + name: "Claude-Opus-4.1", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 13, output: 64, cache_read: 1.3, cache_write: 16 }, + limit: { context: 196608, output: 32000 }, + }, + "anthropic/claude-sonnet-3.5": { + id: "anthropic/claude-sonnet-3.5", + name: "Claude-Sonnet-3.5", + family: "claude-sonnet", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-06-05", + last_updated: "2024-06-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.6, output: 13, cache_read: 0.26, cache_write: 3.2 }, + limit: { context: 189096, output: 8192 }, + status: "deprecated", + }, + "anthropic/claude-haiku-3": { + id: "anthropic/claude-haiku-3", + name: "Claude-Haiku-3", + family: "claude-haiku", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-03-09", + last_updated: "2024-03-09", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.21, output: 1.1, cache_read: 0.021, cache_write: 0.26 }, + limit: { context: 189096, output: 8192 }, + }, + "anthropic/claude-opus-4.6": { + id: "anthropic/claude-opus-4.6", + name: "Claude-Opus-4.6", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-02-04", + last_updated: "2026-02-04", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 4.3, output: 21, cache_read: 0.43, cache_write: 5.3 }, + limit: { context: 983040, output: 128000 }, + }, + "anthropic/claude-opus-4.7": { + id: "anthropic/claude-opus-4.7", + name: "Claude-Opus-4.7", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-04-15", + last_updated: "2026-04-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 4.3, output: 21, cache_read: 0.43, cache_write: 5.4 }, + limit: { context: 1048576, output: 128000 }, + }, + "anthropic/claude-sonnet-4": { + id: "anthropic/claude-sonnet-4", + name: "Claude-Sonnet-4", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-05-21", + last_updated: "2025-05-21", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.6, output: 13, cache_read: 0.26, cache_write: 3.2 }, + limit: { context: 983040, output: 64000 }, + }, + "anthropic/claude-sonnet-4.5": { + id: "anthropic/claude-sonnet-4.5", + name: "Claude-Sonnet-4.5", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-09-26", + last_updated: "2025-09-26", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.6, output: 13, cache_read: 0.26, cache_write: 3.2 }, + limit: { context: 983040, output: 32768 }, + }, + "anthropic/claude-opus-4.5": { + id: "anthropic/claude-opus-4.5", + name: "Claude-Opus-4.5", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-11-21", + last_updated: "2025-11-21", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 4.3, output: 21, cache_read: 0.43, cache_write: 5.3 }, + limit: { context: 196608, output: 64000 }, + }, + "anthropic/claude-sonnet-3.7": { + id: "anthropic/claude-sonnet-3.7", + name: "Claude-Sonnet-3.7", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-02-19", + last_updated: "2025-02-19", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.6, output: 13, cache_read: 0.26, cache_write: 3.2 }, + limit: { context: 196608, output: 128000 }, + }, + "anthropic/claude-opus-4": { + id: "anthropic/claude-opus-4", + name: "Claude-Opus-4", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-05-21", + last_updated: "2025-05-21", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 13, output: 64, cache_read: 1.3, cache_write: 16 }, + limit: { context: 192512, output: 28672 }, + }, + "anthropic/claude-haiku-3.5": { + id: "anthropic/claude-haiku-3.5", + name: "Claude-Haiku-3.5", + family: "claude-haiku", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-10-01", + last_updated: "2024-10-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.68, output: 3.4, cache_read: 0.068, cache_write: 0.85 }, + limit: { context: 189096, output: 8192 }, + }, + "anthropic/claude-haiku-4.5": { + id: "anthropic/claude-haiku-4.5", + name: "Claude-Haiku-4.5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.85, output: 4.3, cache_read: 0.085, cache_write: 1.1 }, + limit: { context: 192000, output: 64000 }, + }, + "anthropic/claude-sonnet-3.5-june": { + id: "anthropic/claude-sonnet-3.5-june", + name: "Claude-Sonnet-3.5-June", + family: "claude-sonnet", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-11-18", + last_updated: "2024-11-18", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.6, output: 13, cache_read: 0.26, cache_write: 3.2 }, + limit: { context: 189096, output: 8192 }, + status: "deprecated", + }, + "anthropic/claude-sonnet-4.6": { + id: "anthropic/claude-sonnet-4.6", + name: "Claude-Sonnet-4.6", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.6, output: 13, cache_read: 0.26, cache_write: 3.2 }, + limit: { context: 983040, output: 128000 }, + }, + "ideogramai/ideogram": { + id: "ideogramai/ideogram", + name: "Ideogram", + family: "ideogram", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-04-03", + last_updated: "2024-04-03", + modalities: { input: ["text", "image"], output: ["image"] }, + open_weights: false, + limit: { context: 150, output: 0 }, + }, + "ideogramai/ideogram-v2": { + id: "ideogramai/ideogram-v2", + name: "Ideogram-v2", + family: "ideogram", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-08-21", + last_updated: "2024-08-21", + modalities: { input: ["text", "image"], output: ["image"] }, + open_weights: false, + limit: { context: 150, output: 0 }, + }, + "ideogramai/ideogram-v2a-turbo": { + id: "ideogramai/ideogram-v2a-turbo", + name: "Ideogram-v2a-Turbo", + family: "ideogram", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-02-27", + last_updated: "2025-02-27", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 150, output: 0 }, + }, + "ideogramai/ideogram-v2a": { + id: "ideogramai/ideogram-v2a", + name: "Ideogram-v2a", + family: "ideogram", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2025-02-27", + last_updated: "2025-02-27", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 150, output: 0 }, + }, + "trytako/tako": { + id: "trytako/tako", + name: "Tako", + family: "tako", + attachment: true, + reasoning: false, + tool_call: true, + temperature: false, + release_date: "2024-08-15", + last_updated: "2024-08-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 2048, output: 0 }, + }, + "poetools/claude-code": { + id: "poetools/claude-code", + name: "claude-code", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2025-11-27", + last_updated: "2025-11-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 0, output: 0 }, + }, + "openai/gpt-5.5": { + id: "openai/gpt-5.5", + name: "GPT-5.5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-12-01", + release_date: "2026-04-08", + last_updated: "2026-04-08", + modalities: { input: ["text", "image"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 4.5455, output: 27.2727, cache_read: 0.4545 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.5-pro": { + id: "openai/gpt-5.5-pro", + name: "GPT-5.5-Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: false, + knowledge: "2025-12-01", + release_date: "2026-04-08", + last_updated: "2026-04-08", + modalities: { input: ["text", "image"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 27.2727, output: 163.6364 }, + limit: { context: 400000, output: 128000 }, + }, + }, + }, + helicone: { + id: "helicone", + env: ["HELICONE_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://ai-gateway.helicone.ai/v1", + name: "Helicone", + doc: "https://helicone.ai/models", + models: { + "mistral-nemo": { + id: "mistral-nemo", + name: "Mistral Nemo", + family: "mistral-nemo", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-07", + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 20, output: 40 }, + limit: { context: 128000, output: 16400 }, + }, + "grok-4-1-fast-reasoning": { + id: "grok-4-1-fast-reasoning", + name: "xAI Grok 4.1 Fast Reasoning", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-11", + release_date: "2025-11-17", + last_updated: "2025-11-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.19999999999999998, output: 0.5, cache_read: 0.049999999999999996 }, + limit: { context: 2000000, output: 2000000 }, + }, + "gemma2-9b-it": { + id: "gemma2-9b-it", + name: "Google Gemma 2", + family: "gemma", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-06", + release_date: "2024-06-25", + last_updated: "2024-06-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.01, output: 0.03 }, + limit: { context: 8192, output: 8192 }, + }, + "llama-3.3-70b-instruct": { + id: "llama-3.3-70b-instruct", + name: "Meta Llama 3.3 70B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.13, output: 0.39 }, + limit: { context: 128000, output: 16400 }, + }, + "llama-4-scout": { + id: "llama-4-scout", + name: "Meta Llama 4 Scout 17B 16E", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.08, output: 0.3 }, + limit: { context: 131072, output: 8192 }, + }, + "chatgpt-4o-latest": { + id: "chatgpt-4o-latest", + name: "OpenAI ChatGPT-4o", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2024-08-14", + last_updated: "2024-08-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 20, cache_read: 2.5 }, + limit: { context: 128000, output: 16384 }, + }, + "claude-3.5-sonnet-v2": { + id: "claude-3.5-sonnet-v2", + name: "Anthropic: Claude 3.5 Sonnet v2", + family: "claude-sonnet", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.30000000000000004, cache_write: 3.75 }, + limit: { context: 200000, output: 8192 }, + }, + "hermes-2-pro-llama-3-8b": { + id: "hermes-2-pro-llama-3-8b", + name: "Hermes 2 Pro Llama 3 8B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-05", + release_date: "2024-05-27", + last_updated: "2024-05-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.14 }, + limit: { context: 131072, output: 131072 }, + }, + "claude-3.7-sonnet": { + id: "claude-3.7-sonnet", + name: "Anthropic: Claude 3.7 Sonnet", + family: "claude-sonnet", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-02", + release_date: "2025-02-19", + last_updated: "2025-02-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.30000000000000004, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "llama-prompt-guard-2-22m": { + id: "llama-prompt-guard-2-22m", + name: "Meta Llama Prompt Guard 2 22M", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2024-10-01", + last_updated: "2024-10-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.01, output: 0.01 }, + limit: { context: 512, output: 2 }, + }, + "o1-mini": { + id: "o1-mini", + name: "OpenAI: o1-mini", + family: "o-mini", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + knowledge: "2025-01", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.55 }, + limit: { context: 128000, output: 65536 }, + }, + "gpt-4.1-mini-2025-04-14": { + id: "gpt-4.1-mini-2025-04-14", + name: "OpenAI GPT-4.1 Mini", + family: "gpt-mini", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.39999999999999997, output: 1.5999999999999999, cache_read: 0.09999999999999999 }, + limit: { context: 1047576, output: 32768 }, + }, + "deepseek-r1-distill-llama-70b": { + id: "deepseek-r1-distill-llama-70b", + name: "DeepSeek R1 Distill Llama 70B", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.03, output: 0.13 }, + limit: { context: 128000, output: 4096 }, + }, + "qwen3-32b": { + id: "qwen3-32b", + name: "Qwen3 32B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04-28", + last_updated: "2025-04-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.29, output: 0.59 }, + limit: { context: 131072, output: 40960 }, + }, + "llama-3.3-70b-versatile": { + id: "llama-3.3-70b-versatile", + name: "Meta Llama 3.3 70B Versatile", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.59, output: 0.7899999999999999 }, + limit: { context: 131072, output: 32678 }, + }, + "gpt-5-mini": { + id: "gpt-5-mini", + name: "OpenAI GPT-5 Mini", + family: "gpt-mini", + attachment: false, + reasoning: false, + tool_call: true, + temperature: false, + knowledge: "2025-01", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.024999999999999998 }, + limit: { context: 400000, output: 128000 }, + }, + "gpt-5-nano": { + id: "gpt-5-nano", + name: "OpenAI GPT-5 Nano", + family: "gpt-nano", + attachment: false, + reasoning: false, + tool_call: true, + temperature: false, + knowledge: "2025-01", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.049999999999999996, output: 0.39999999999999997, cache_read: 0.005 }, + limit: { context: 400000, output: 128000 }, + }, + "gemini-3-pro-preview": { + id: "gemini-3-pro-preview", + name: "Google Gemini 3 Pro Preview", + family: "gemini-pro", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-11", + release_date: "2025-11-18", + last_updated: "2025-11-18", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.19999999999999998 }, + limit: { context: 1048576, output: 65536 }, + }, + "claude-3-haiku-20240307": { + id: "claude-3-haiku-20240307", + name: "Anthropic: Claude 3 Haiku", + family: "claude-haiku", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-03", + release_date: "2024-03-07", + last_updated: "2024-03-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.25, cache_read: 0.03, cache_write: 0.3 }, + limit: { context: 200000, output: 4096 }, + }, + "llama-4-maverick": { + id: "llama-4-maverick", + name: "Meta Llama 4 Maverick 17B 128E", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 131072, output: 8192 }, + }, + "claude-sonnet-4-5-20250929": { + id: "claude-sonnet-4-5-20250929", + name: "Anthropic: Claude Sonnet 4.5 (20250929)", + family: "claude-sonnet", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-09", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.30000000000000004, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "gemini-2.5-pro": { + id: "gemini-2.5-pro", + name: "Google Gemini 2.5 Pro", + family: "gemini-pro", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.3125, cache_write: 1.25 }, + limit: { context: 1048576, output: 65536 }, + }, + "claude-4.5-opus": { + id: "claude-4.5-opus", + name: "Anthropic: Claude Opus 4.5", + family: "claude-opus", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-11", + release_date: "2025-11-24", + last_updated: "2025-11-24", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 64000 }, + }, + "grok-4-1-fast-non-reasoning": { + id: "grok-4-1-fast-non-reasoning", + name: "xAI Grok 4.1 Fast Non-Reasoning", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-11", + release_date: "2025-11-17", + last_updated: "2025-11-17", + modalities: { input: ["text", "image"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 0.19999999999999998, output: 0.5, cache_read: 0.049999999999999996 }, + limit: { context: 2000000, output: 30000 }, + }, + "sonar-pro": { + id: "sonar-pro", + name: "Perplexity Sonar Pro", + family: "sonar-pro", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-01", + release_date: "2025-01-27", + last_updated: "2025-01-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 200000, output: 4096 }, + }, + "mistral-large-2411": { + id: "mistral-large-2411", + name: "Mistral-Large", + family: "mistral-large", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2024-07-24", + last_updated: "2024-07-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6 }, + limit: { context: 128000, output: 32768 }, + }, + "o3-pro": { + id: "o3-pro", + name: "OpenAI o3 Pro", + family: "o-pro", + attachment: false, + reasoning: false, + tool_call: true, + temperature: false, + knowledge: "2024-06", + release_date: "2024-06-01", + last_updated: "2024-06-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 20, output: 80 }, + limit: { context: 200000, output: 100000 }, + }, + "claude-opus-4-1": { + id: "claude-opus-4-1", + name: "Anthropic: Claude Opus 4.1", + family: "claude-opus", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "gpt-4o-mini": { + id: "gpt-4o-mini", + name: "OpenAI GPT-4o-mini", + family: "gpt-mini", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6, cache_read: 0.075 }, + limit: { context: 128000, output: 16384 }, + }, + "claude-4.5-haiku": { + id: "claude-4.5-haiku", + name: "Anthropic: Claude 4.5 Haiku", + family: "claude-haiku", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-10", + release_date: "2025-10-01", + last_updated: "2025-10-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.09999999999999999, cache_write: 1.25 }, + limit: { context: 200000, output: 8192 }, + }, + "kimi-k2-0711": { + id: "kimi-k2-0711", + name: "Kimi K2 (07/11)", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5700000000000001, output: 2.3 }, + limit: { context: 131072, output: 16384 }, + }, + "o4-mini": { + id: "o4-mini", + name: "OpenAI o4 Mini", + family: "o-mini", + attachment: false, + reasoning: false, + tool_call: true, + temperature: false, + knowledge: "2024-06", + release_date: "2024-06-01", + last_updated: "2024-06-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.275 }, + limit: { context: 200000, output: 100000 }, + }, + "sonar-deep-research": { + id: "sonar-deep-research", + name: "Perplexity Sonar Deep Research", + family: "sonar-deep-research", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2025-01", + release_date: "2025-01-27", + last_updated: "2025-01-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8 }, + limit: { context: 127000, output: 4096 }, + }, + "gemma-3-12b-it": { + id: "gemma-3-12b-it", + name: "Google Gemma 3 12B", + family: "gemma", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-12", + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.049999999999999996, output: 0.09999999999999999 }, + limit: { context: 131072, output: 8192 }, + }, + "gemini-2.5-flash": { + id: "gemini-2.5-flash", + name: "Google Gemini 2.5 Flash", + family: "gemini-flash", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5, cache_read: 0.075, cache_write: 0.3 }, + limit: { context: 1048576, output: 65535 }, + }, + "deepseek-tng-r1t2-chimera": { + id: "deepseek-tng-r1t2-chimera", + name: "DeepSeek TNG R1T2 Chimera", + family: "deepseek-thinking", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-07-02", + last_updated: "2025-07-02", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 130000, output: 163840 }, + }, + "gpt-5.1-codex-mini": { + id: "gpt-5.1-codex-mini", + name: "OpenAI: GPT-5.1 Codex Mini", + family: "gpt-codex", + attachment: false, + reasoning: false, + tool_call: true, + temperature: false, + knowledge: "2025-01", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text", "image"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.024999999999999998 }, + limit: { context: 400000, output: 128000 }, + }, + "claude-sonnet-4": { + id: "claude-sonnet-4", + name: "Anthropic: Claude Sonnet 4", + family: "claude-sonnet", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05", + release_date: "2025-05-14", + last_updated: "2025-05-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.30000000000000004, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "grok-code-fast-1": { + id: "grok-code-fast-1", + name: "xAI Grok Code Fast 1", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2024-08-25", + last_updated: "2024-08-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.19999999999999998, output: 1.5, cache_read: 0.02 }, + limit: { context: 256000, output: 10000 }, + }, + "gpt-5.1": { + id: "gpt-5.1", + name: "OpenAI GPT-5.1", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: true, + temperature: false, + knowledge: "2025-01", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text", "image"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.12500000000000003 }, + limit: { context: 400000, output: 128000 }, + }, + "deepseek-reasoner": { + id: "deepseek-reasoner", + name: "DeepSeek Reasoner", + family: "deepseek-thinking", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-01", + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.56, output: 1.68, cache_read: 0.07 }, + limit: { context: 128000, output: 64000 }, + }, + "grok-4-fast-reasoning": { + id: "grok-4-fast-reasoning", + name: "xAI: Grok 4 Fast Reasoning", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-09", + release_date: "2025-09-01", + last_updated: "2025-09-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.19999999999999998, output: 0.5, cache_read: 0.049999999999999996 }, + limit: { context: 2000000, output: 2000000 }, + }, + o1: { + id: "o1", + name: "OpenAI: o1", + family: "o", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + knowledge: "2025-01", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 60, cache_read: 7.5 }, + limit: { context: 200000, output: 100000 }, + }, + "llama-3.1-8b-instant": { + id: "llama-3.1-8b-instant", + name: "Meta Llama 3.1 8B Instant", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2024-07-01", + last_updated: "2024-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.049999999999999996, output: 0.08 }, + limit: { context: 131072, output: 32678 }, + }, + "o3-mini": { + id: "o3-mini", + name: "OpenAI o3 Mini", + family: "o-mini", + attachment: false, + reasoning: false, + tool_call: true, + temperature: false, + knowledge: "2023-10", + release_date: "2023-10-01", + last_updated: "2023-10-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.55 }, + limit: { context: 200000, output: 100000 }, + }, + sonar: { + id: "sonar", + name: "Perplexity Sonar", + family: "sonar", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-01", + release_date: "2025-01-27", + last_updated: "2025-01-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 1 }, + limit: { context: 127000, output: 4096 }, + }, + "kimi-k2-0905": { + id: "kimi-k2-0905", + name: "Kimi K2 (09/05)", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-09", + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 2, cache_read: 0.39999999999999997 }, + limit: { context: 262144, output: 16384 }, + }, + "mistral-small": { + id: "mistral-small", + name: "Mistral Small", + family: "mistral-small", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-02", + release_date: "2024-02-26", + last_updated: "2024-02-26", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 75, output: 200 }, + limit: { context: 128000, output: 128000 }, + }, + "qwen3-30b-a3b": { + id: "qwen3-30b-a3b", + name: "Qwen3 30B A3B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-06-01", + last_updated: "2025-06-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.08, output: 0.29 }, + limit: { context: 41000, output: 41000 }, + }, + "grok-4": { + id: "grok-4", + name: "xAI Grok 4", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2024-07-09", + last_updated: "2024-07-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.75 }, + limit: { context: 256000, output: 256000 }, + }, + "qwen3-235b-a22b-thinking": { + id: "qwen3-235b-a22b-thinking", + name: "Qwen3 235B A22B Thinking", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2025-07", + release_date: "2025-07-25", + last_updated: "2025-07-25", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.9000000000000004 }, + limit: { context: 262144, output: 81920 }, + }, + "qwen2.5-coder-7b-fast": { + id: "qwen2.5-coder-7b-fast", + name: "Qwen2.5 Coder 7B fast", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-09", + release_date: "2024-09-15", + last_updated: "2024-09-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.03, output: 0.09 }, + limit: { context: 32000, output: 8192 }, + }, + "llama-3.1-8b-instruct-turbo": { + id: "llama-3.1-8b-instruct-turbo", + name: "Meta Llama 3.1 8B Instruct Turbo", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.02, output: 0.03 }, + limit: { context: 128000, output: 128000 }, + }, + "qwen3-next-80b-a3b-instruct": { + id: "qwen3-next-80b-a3b-instruct", + name: "Qwen3 Next 80B A3B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 1.4 }, + limit: { context: 262000, output: 16384 }, + }, + "glm-4.6": { + id: "glm-4.6", + name: "Zai GLM-4.6", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.44999999999999996, output: 1.5 }, + limit: { context: 204800, output: 131072 }, + }, + "gpt-5-codex": { + id: "gpt-5-codex", + name: "OpenAI: GPT-5 Codex", + family: "gpt-codex", + attachment: false, + reasoning: false, + tool_call: true, + temperature: false, + knowledge: "2025-01", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.12500000000000003 }, + limit: { context: 400000, output: 128000 }, + }, + "claude-opus-4-1-20250805": { + id: "claude-opus-4-1-20250805", + name: "Anthropic: Claude Opus 4.1 (20250805)", + family: "claude-opus", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "gpt-5.1-chat-latest": { + id: "gpt-5.1-chat-latest", + name: "OpenAI GPT-5.1 Chat", + family: "gpt-codex", + attachment: false, + reasoning: false, + tool_call: true, + temperature: false, + knowledge: "2025-01", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text", "image"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.12500000000000003 }, + limit: { context: 128000, output: 16384 }, + }, + "claude-haiku-4-5-20251001": { + id: "claude-haiku-4-5-20251001", + name: "Anthropic: Claude 4.5 Haiku (20251001)", + family: "claude-haiku", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-10", + release_date: "2025-10-01", + last_updated: "2025-10-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.09999999999999999, cache_write: 1.25 }, + limit: { context: 200000, output: 8192 }, + }, + "sonar-reasoning": { + id: "sonar-reasoning", + name: "Perplexity Sonar Reasoning", + family: "sonar-reasoning", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2025-01", + release_date: "2025-01-27", + last_updated: "2025-01-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5 }, + limit: { context: 127000, output: 4096 }, + }, + "claude-opus-4": { + id: "claude-opus-4", + name: "Anthropic: Claude Opus 4", + family: "claude-opus", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05", + release_date: "2025-05-14", + last_updated: "2025-05-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "llama-prompt-guard-2-86m": { + id: "llama-prompt-guard-2-86m", + name: "Meta Llama Prompt Guard 2 86M", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2024-10-01", + last_updated: "2024-10-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.01, output: 0.01 }, + limit: { context: 512, output: 2 }, + }, + "gpt-4.1-nano": { + id: "gpt-4.1-nano", + name: "OpenAI GPT-4.1 Nano", + family: "gpt-nano", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.09999999999999999, output: 0.39999999999999997, cache_read: 0.024999999999999998 }, + limit: { context: 1047576, output: 32768 }, + }, + "qwen3-coder-30b-a3b-instruct": { + id: "qwen3-coder-30b-a3b-instruct", + name: "Qwen3 Coder 30B A3B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-07-31", + last_updated: "2025-07-31", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.09999999999999999, output: 0.3 }, + limit: { context: 262144, output: 262144 }, + }, + "claude-3.5-haiku": { + id: "claude-3.5-haiku", + name: "Anthropic: Claude 3.5 Haiku", + family: "claude-haiku", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.7999999999999999, output: 4, cache_read: 0.08, cache_write: 1 }, + limit: { context: 200000, output: 8192 }, + }, + "grok-3-mini": { + id: "grok-3-mini", + name: "xAI Grok 3 Mini", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-06", + release_date: "2024-06-01", + last_updated: "2024-06-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.5, cache_read: 0.075 }, + limit: { context: 131072, output: 131072 }, + }, + o3: { + id: "o3", + name: "OpenAI o3", + family: "o", + attachment: false, + reasoning: false, + tool_call: true, + temperature: false, + knowledge: "2024-06", + release_date: "2024-06-01", + last_updated: "2024-06-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 200000, output: 100000 }, + }, + "deepseek-v3.2": { + id: "deepseek-v3.2", + name: "DeepSeek V3.2", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-09", + release_date: "2025-09-22", + last_updated: "2025-09-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 0.41 }, + limit: { context: 163840, output: 65536 }, + }, + "gpt-oss-20b": { + id: "gpt-oss-20b", + name: "OpenAI GPT-OSS 20b", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-06", + release_date: "2024-06-01", + last_updated: "2024-06-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.049999999999999996, output: 0.19999999999999998 }, + limit: { context: 131072, output: 131072 }, + }, + "gpt-5-pro": { + id: "gpt-5-pro", + name: "OpenAI: GPT-5 Pro", + family: "gpt-pro", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + knowledge: "2025-01", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 120 }, + limit: { context: 128000, output: 32768 }, + }, + "llama-guard-4": { + id: "llama-guard-4", + name: "Meta Llama Guard 4 12B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-01", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.21, output: 0.21 }, + limit: { context: 131072, output: 1024 }, + }, + "gpt-4o": { + id: "gpt-4o", + name: "OpenAI GPT-4o", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-05", + release_date: "2024-05-13", + last_updated: "2024-05-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10, cache_read: 1.25 }, + limit: { context: 128000, output: 16384 }, + }, + "qwen3-vl-235b-a22b-instruct": { + id: "qwen3-vl-235b-a22b-instruct", + name: "Qwen3 VL 235B A22B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-09", + release_date: "2025-09-23", + last_updated: "2025-09-23", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.5 }, + limit: { context: 256000, output: 16384 }, + }, + "gemini-2.5-flash-lite": { + id: "gemini-2.5-flash-lite", + name: "Google Gemini 2.5 Flash Lite", + family: "gemini-flash-lite", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-07-22", + last_updated: "2025-07-22", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { + input: 0.09999999999999999, + output: 0.39999999999999997, + cache_read: 0.024999999999999998, + cache_write: 0.09999999999999999, + }, + limit: { context: 1048576, output: 65535 }, + }, + "qwen3-coder": { + id: "qwen3-coder", + name: "Qwen3 Coder 480B A35B Instruct Turbo", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.22, output: 0.95 }, + limit: { context: 262144, output: 16384 }, + }, + "gpt-5": { + id: "gpt-5", + name: "OpenAI GPT-5", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: true, + temperature: false, + knowledge: "2025-01", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.12500000000000003 }, + limit: { context: 400000, output: 128000 }, + }, + "ernie-4.5-21b-a3b-thinking": { + id: "ernie-4.5-21b-a3b-thinking", + name: "Baidu Ernie 4.5 21B A3B Thinking", + family: "ernie", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2025-03", + release_date: "2025-03-16", + last_updated: "2025-03-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.07, output: 0.28 }, + limit: { context: 128000, output: 8000 }, + }, + "gpt-oss-120b": { + id: "gpt-oss-120b", + name: "OpenAI GPT-OSS 120b", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-06", + release_date: "2024-06-01", + last_updated: "2024-06-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.04, output: 0.16 }, + limit: { context: 131072, output: 131072 }, + }, + "gpt-5-chat-latest": { + id: "gpt-5-chat-latest", + name: "OpenAI GPT-5 Chat Latest", + family: "gpt-codex", + attachment: false, + reasoning: false, + tool_call: true, + temperature: false, + knowledge: "2024-09", + release_date: "2024-09-30", + last_updated: "2024-09-30", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.12500000000000003 }, + limit: { context: 128000, output: 16384 }, + }, + "claude-4.5-sonnet": { + id: "claude-4.5-sonnet", + name: "Anthropic: Claude Sonnet 4.5", + family: "claude-sonnet", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-09", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.30000000000000004, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "deepseek-v3": { + id: "deepseek-v3", + name: "DeepSeek V3", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2024-12-26", + last_updated: "2024-12-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.56, output: 1.68, cache_read: 0.07 }, + limit: { context: 128000, output: 8192 }, + }, + "llama-3.1-8b-instruct": { + id: "llama-3.1-8b-instruct", + name: "Meta Llama 3.1 8B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.02, output: 0.049999999999999996 }, + limit: { context: 16384, output: 16384 }, + }, + "gpt-4.1": { + id: "gpt-4.1", + name: "OpenAI GPT-4.1", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 1047576, output: 32768 }, + }, + "kimi-k2-thinking": { + id: "kimi-k2-thinking", + name: "Kimi K2 Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-11", + release_date: "2025-11-06", + last_updated: "2025-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.48, output: 2 }, + limit: { context: 256000, output: 262144 }, + }, + "gpt-4.1-mini": { + id: "gpt-4.1-mini", + name: "OpenAI GPT-4.1 Mini", + family: "gpt-mini", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.39999999999999997, output: 1.5999999999999999, cache_read: 0.09999999999999999 }, + limit: { context: 1047576, output: 32768 }, + }, + "deepseek-v3.1-terminus": { + id: "deepseek-v3.1-terminus", + name: "DeepSeek V3.1 Terminus", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-09", + release_date: "2025-09-22", + last_updated: "2025-09-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 1, cache_read: 0.21600000000000003 }, + limit: { context: 128000, output: 16384 }, + }, + "gpt-5.1-codex": { + id: "gpt-5.1-codex", + name: "OpenAI: GPT-5.1 Codex", + family: "gpt-codex", + attachment: false, + reasoning: false, + tool_call: true, + temperature: false, + knowledge: "2025-01", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text", "image"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.12500000000000003 }, + limit: { context: 400000, output: 128000 }, + }, + "grok-3": { + id: "grok-3", + name: "xAI Grok 3", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-06", + release_date: "2024-06-01", + last_updated: "2024-06-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.75 }, + limit: { context: 131072, output: 131072 }, + }, + "grok-4-fast-non-reasoning": { + id: "grok-4-fast-non-reasoning", + name: "xAI Grok 4 Fast Non-Reasoning", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-09", + release_date: "2025-09-19", + last_updated: "2025-09-19", + modalities: { input: ["text", "image", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.19999999999999998, output: 0.5, cache_read: 0.049999999999999996 }, + limit: { context: 2000000, output: 2000000 }, + }, + "sonar-reasoning-pro": { + id: "sonar-reasoning-pro", + name: "Perplexity Sonar Reasoning Pro", + family: "sonar-reasoning", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2025-01", + release_date: "2025-01-27", + last_updated: "2025-01-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8 }, + limit: { context: 127000, output: 4096 }, + }, + }, + }, + "ollama-cloud": { + id: "ollama-cloud", + env: ["OLLAMA_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://ollama.com/v1", + name: "Ollama Cloud", + doc: "https://docs.ollama.com/cloud", + models: { + "minimax-m2.7": { + id: "minimax-m2.7", + name: "minimax-m2.7", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 196608, output: 196608 }, + }, + "gpt-oss:20b": { + id: "gpt-oss:20b", + name: "gpt-oss:20b", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + release_date: "2025-08-05", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 131072, output: 32768 }, + }, + "kimi-k2.5": { + id: "kimi-k2.5", + name: "kimi-k2.5", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + limit: { context: 262144, output: 262144 }, + }, + "glm-4.7": { + id: "glm-4.7", + name: "glm-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + release_date: "2025-12-22", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 202752, output: 131072 }, + }, + "gemma4:31b": { + id: "gemma4:31b", + name: "gemma4:31b", + family: "gemma", + attachment: true, + reasoning: true, + tool_call: true, + knowledge: "2025-01", + release_date: "2026-04-02", + last_updated: "2026-04-08", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + limit: { context: 262144, output: 262144 }, + }, + "gpt-oss:120b": { + id: "gpt-oss:120b", + name: "gpt-oss:120b", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + release_date: "2025-08-05", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 131072, output: 32768 }, + }, + "qwen3.5:397b": { + id: "qwen3.5:397b", + name: "qwen3.5:397b", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_details" }, + release_date: "2026-02-15", + last_updated: "2026-02-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + limit: { context: 262144, output: 65536 }, + }, + "deepseek-v3.1:671b": { + id: "deepseek-v3.1:671b", + name: "deepseek-v3.1:671b", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + release_date: "2025-08-21", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 163840, output: 163840 }, + }, + "glm-5": { + id: "glm-5", + name: "glm-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 202752, output: 131072 }, + }, + "qwen3-vl:235b-instruct": { + id: "qwen3-vl:235b-instruct", + name: "qwen3-vl:235b-instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + release_date: "2025-09-22", + last_updated: "2026-01-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + limit: { context: 262144, output: 131072 }, + }, + "gemma3:4b": { + id: "gemma3:4b", + name: "gemma3:4b", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + release_date: "2024-12-01", + last_updated: "2026-01-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + limit: { context: 131072, output: 131072 }, + }, + "gemini-3-flash-preview": { + id: "gemini-3-flash-preview", + name: "gemini-3-flash-preview", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + knowledge: "2025-01", + release_date: "2025-12-17", + last_updated: "2026-04-08", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + limit: { context: 1048576, output: 65536 }, + }, + "ministral-3:14b": { + id: "ministral-3:14b", + name: "ministral-3:14b", + family: "ministral", + attachment: true, + reasoning: false, + tool_call: true, + release_date: "2024-12-01", + last_updated: "2026-01-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + limit: { context: 262144, output: 128000 }, + }, + "minimax-m2": { + id: "minimax-m2", + name: "minimax-m2", + family: "minimax", + attachment: false, + reasoning: false, + tool_call: true, + release_date: "2025-10-23", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 204800, output: 128000 }, + }, + "qwen3-next:80b": { + id: "qwen3-next:80b", + name: "qwen3-next:80b", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + release_date: "2025-09-15", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 262144, output: 32768 }, + }, + "qwen3-vl:235b": { + id: "qwen3-vl:235b", + name: "qwen3-vl:235b", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + release_date: "2025-09-22", + last_updated: "2026-01-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + limit: { context: 262144, output: 32768 }, + }, + "rnj-1:8b": { + id: "rnj-1:8b", + name: "rnj-1:8b", + family: "rnj", + attachment: false, + reasoning: false, + tool_call: true, + release_date: "2025-12-06", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 32768, output: 4096 }, + }, + "minimax-m2.1": { + id: "minimax-m2.1", + name: "minimax-m2.1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + release_date: "2025-12-23", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 204800, output: 131072 }, + }, + "glm-5.1": { + id: "glm-5.1", + name: "glm-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + release_date: "2026-03-27", + last_updated: "2026-04-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 202752, output: 131072 }, + }, + "mistral-large-3:675b": { + id: "mistral-large-3:675b", + name: "mistral-large-3:675b", + family: "mistral-large", + attachment: true, + reasoning: false, + tool_call: true, + release_date: "2025-12-02", + last_updated: "2026-01-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + limit: { context: 262144, output: 262144 }, + }, + "ministral-3:8b": { + id: "ministral-3:8b", + name: "ministral-3:8b", + family: "ministral", + attachment: true, + reasoning: false, + tool_call: true, + release_date: "2024-12-01", + last_updated: "2026-01-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + limit: { context: 262144, output: 128000 }, + }, + "gemma3:12b": { + id: "gemma3:12b", + name: "gemma3:12b", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + release_date: "2024-12-01", + last_updated: "2026-01-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + limit: { context: 131072, output: 131072 }, + }, + "qwen3-coder:480b": { + id: "qwen3-coder:480b", + name: "qwen3-coder:480b", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + release_date: "2025-07-22", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 262144, output: 65536 }, + }, + "kimi-k2.6:cloud": { + id: "kimi-k2.6:cloud", + name: "kimi-k2.6:cloud", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + release_date: "2026-04-20", + last_updated: "2026-04-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + limit: { context: 262144, output: 262144 }, + }, + "nemotron-3-nano:30b": { + id: "nemotron-3-nano:30b", + name: "nemotron-3-nano:30b", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + release_date: "2025-12-15", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 1048576, output: 131072 }, + }, + "deepseek-v4-flash": { + id: "deepseek-v4-flash", + name: "deepseek-v4-flash", + family: "deepseek-flash", + attachment: false, + reasoning: true, + tool_call: true, + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 1048576, output: 1048576 }, + }, + "glm-4.6": { + id: "glm-4.6", + name: "glm-4.6", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + release_date: "2025-09-29", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 202752, output: 131072 }, + }, + "ministral-3:3b": { + id: "ministral-3:3b", + name: "ministral-3:3b", + family: "ministral", + attachment: true, + reasoning: false, + tool_call: true, + release_date: "2024-10-22", + last_updated: "2026-01-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + limit: { context: 262144, output: 128000 }, + }, + "gemma3:27b": { + id: "gemma3:27b", + name: "gemma3:27b", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + release_date: "2025-07-27", + last_updated: "2026-01-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + limit: { context: 131072, output: 131072 }, + }, + "devstral-2:123b": { + id: "devstral-2:123b", + name: "devstral-2:123b", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: true, + release_date: "2025-12-09", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 262144, output: 262144 }, + }, + "cogito-2.1:671b": { + id: "cogito-2.1:671b", + name: "cogito-2.1:671b", + family: "cogito", + attachment: false, + reasoning: true, + tool_call: true, + release_date: "2025-11-19", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 163840, output: 32000 }, + }, + "qwen3-coder-next": { + id: "qwen3-coder-next", + name: "qwen3-coder-next", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + release_date: "2026-02-02", + last_updated: "2026-02-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 262144, output: 65536 }, + }, + "nemotron-3-super": { + id: "nemotron-3-super", + name: "nemotron-3-super", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + release_date: "2026-03-11", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 262144, output: 65536 }, + }, + "deepseek-v4-pro": { + id: "deepseek-v4-pro", + name: "deepseek-v4-pro", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 1048576, output: 1048576 }, + }, + "minimax-m2.5": { + id: "minimax-m2.5", + name: "minimax-m2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + knowledge: "2025-01", + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 204800, output: 131072 }, + }, + "deepseek-v3.2": { + id: "deepseek-v3.2", + name: "deepseek-v3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + release_date: "2025-06-15", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 163840, output: 65536 }, + }, + "kimi-k2-thinking": { + id: "kimi-k2-thinking", + name: "kimi-k2-thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + knowledge: "2024-08", + release_date: "2025-11-06", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 262144, output: 262144 }, + }, + "devstral-small-2:24b": { + id: "devstral-small-2:24b", + name: "devstral-small-2:24b", + family: "devstral", + attachment: true, + reasoning: false, + tool_call: true, + release_date: "2025-12-09", + last_updated: "2026-01-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + limit: { context: 262144, output: 262144 }, + }, + "kimi-k2:1t": { + id: "kimi-k2:1t", + name: "kimi-k2:1t", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + knowledge: "2024-10", + release_date: "2025-07-11", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 262144, output: 262144 }, + }, + }, + }, + "zai-coding-plan": { + id: "zai-coding-plan", + env: ["ZHIPU_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.z.ai/api/coding/paas/v4", + name: "Z.AI Coding Plan", + doc: "https://docs.z.ai/devpack/overview", + models: { + "glm-4.7": { + id: "glm-4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "glm-5.1": { + id: "glm-5.1", + name: "GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-03-27", + last_updated: "2026-03-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 200000, output: 131072 }, + }, + "glm-4.5-air": { + id: "glm-4.5-air", + name: "GLM-4.5-Air", + family: "glm-air", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 131072, output: 98304 }, + }, + "glm-5-turbo": { + id: "glm-5-turbo", + name: "GLM-5-Turbo", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-03-16", + last_updated: "2026-03-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 200000, output: 131072 }, + }, + "glm-5v-turbo": { + id: "glm-5v-turbo", + name: "GLM-5V-Turbo", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-04-01", + last_updated: "2026-04-01", + modalities: { input: ["text", "image", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 200000, output: 131072 }, + }, + }, + }, + "amazon-bedrock": { + id: "amazon-bedrock", + env: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION", "AWS_BEARER_TOKEN_BEDROCK"], + npm: "@ai-sdk/amazon-bedrock", + name: "Amazon Bedrock", + doc: "https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html", + models: { + "openai.gpt-oss-safeguard-120b": { + id: "openai.gpt-oss-safeguard-120b", + name: "GPT OSS Safeguard 120B", + family: "gpt-oss", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 128000, output: 4096 }, + }, + "nvidia.nemotron-nano-3-30b": { + id: "nvidia.nemotron-nano-3-30b", + name: "NVIDIA Nemotron Nano 3 30B", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.06, output: 0.24 }, + limit: { context: 128000, output: 4096 }, + }, + "nvidia.nemotron-super-3-120b": { + id: "nvidia.nemotron-super-3-120b", + name: "NVIDIA Nemotron 3 Super 120B A12B", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-11", + last_updated: "2026-03-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.65 }, + limit: { context: 262144, output: 131072 }, + }, + "writer.palmyra-x5-v1:0": { + id: "writer.palmyra-x5-v1:0", + name: "Palmyra X5", + family: "palmyra", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-04-28", + last_updated: "2025-04-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 6 }, + limit: { context: 1040000, output: 8192 }, + }, + "mistral.ministral-3-8b-instruct": { + id: "mistral.ministral-3-8b-instruct", + name: "Ministral 3 8B", + family: "ministral", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.15 }, + limit: { context: 128000, output: 4096 }, + }, + "au.anthropic.claude-opus-4-6-v1": { + id: "au.anthropic.claude-opus-4-6-v1", + name: "AU Anthropic Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 16.5, output: 82.5, cache_read: 1.65, cache_write: 20.625 }, + limit: { context: 1000000, output: 128000 }, + }, + "mistral.ministral-3-3b-instruct": { + id: "mistral.ministral-3-3b-instruct", + name: "Ministral 3 3B", + family: "ministral", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-02", + last_updated: "2025-12-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 256000, output: 8192 }, + }, + "anthropic.claude-sonnet-4-5-20250929-v1:0": { + id: "anthropic.claude-sonnet-4-5-20250929-v1:0", + name: "Claude Sonnet 4.5", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "mistral.devstral-2-123b": { + id: "mistral.devstral-2-123b", + name: "Devstral 2 123B", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-02-17", + last_updated: "2026-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 2 }, + limit: { context: 256000, output: 8192 }, + }, + "global.anthropic.claude-opus-4-5-20251101-v1:0": { + id: "global.anthropic.claude-opus-4-5-20251101-v1:0", + name: "Claude Opus 4.5 (Global)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-24", + last_updated: "2025-08-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 64000 }, + }, + "mistral.voxtral-small-24b-2507": { + id: "mistral.voxtral-small-24b-2507", + name: "Voxtral Small 24B 2507", + family: "mistral", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-01", + last_updated: "2025-07-01", + modalities: { input: ["text", "audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.35 }, + limit: { context: 32000, output: 8192 }, + }, + "google.gemma-3-12b-it": { + id: "google.gemma-3-12b-it", + name: "Google Gemma 3 12B", + family: "gemma", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + knowledge: "2024-12", + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.049999999999999996, output: 0.09999999999999999 }, + limit: { context: 131072, output: 8192 }, + }, + "amazon.nova-pro-v1:0": { + id: "amazon.nova-pro-v1:0", + name: "Nova Pro", + family: "nova-pro", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-12-03", + last_updated: "2024-12-03", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 3.2, cache_read: 0.2 }, + limit: { context: 300000, output: 8192 }, + }, + "anthropic.claude-haiku-4-5-20251001-v1:0": { + id: "anthropic.claude-haiku-4-5-20251001-v1:0", + name: "Claude Haiku 4.5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + }, + "minimax.minimax-m2": { + id: "minimax.minimax-m2", + name: "MiniMax M2", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-10-27", + last_updated: "2025-10-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 204608, output: 128000 }, + }, + "global.anthropic.claude-opus-4-7": { + id: "global.anthropic.claude-opus-4-7", + name: "Claude Opus 4.7 (Global)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "mistral.pixtral-large-2502-v1:0": { + id: "mistral.pixtral-large-2502-v1:0", + name: "Pixtral Large (25.02)", + family: "mistral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-04-08", + last_updated: "2025-04-08", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6 }, + limit: { context: 128000, output: 8192 }, + }, + "meta.llama4-maverick-17b-instruct-v1:0": { + id: "meta.llama4-maverick-17b-instruct-v1:0", + name: "Llama 4 Maverick 17B Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.24, output: 0.97 }, + limit: { context: 1000000, output: 16384 }, + }, + "us.anthropic.claude-sonnet-4-5-20250929-v1:0": { + id: "us.anthropic.claude-sonnet-4-5-20250929-v1:0", + name: "Claude Sonnet 4.5 (US)", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "us.anthropic.claude-haiku-4-5-20251001-v1:0": { + id: "us.anthropic.claude-haiku-4-5-20251001-v1:0", + name: "Claude Haiku 4.5 (US)", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + }, + "amazon.nova-micro-v1:0": { + id: "amazon.nova-micro-v1:0", + name: "Nova Micro", + family: "nova-micro", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-12-03", + last_updated: "2024-12-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.035, output: 0.14, cache_read: 0.00875 }, + limit: { context: 128000, output: 8192 }, + }, + "global.anthropic.claude-sonnet-4-5-20250929-v1:0": { + id: "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + name: "Claude Sonnet 4.5 (Global)", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "openai.gpt-oss-20b-1:0": { + id: "openai.gpt-oss-20b-1:0", + name: "gpt-oss-20b", + family: "gpt-oss", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.07, output: 0.3 }, + limit: { context: 128000, output: 4096 }, + }, + "zai.glm-5": { + id: "zai.glm-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3.2 }, + limit: { context: 202752, output: 101376 }, + }, + "qwen.qwen3-32b-v1:0": { + id: "qwen.qwen3-32b-v1:0", + name: "Qwen3 32B (dense)", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-09-18", + last_updated: "2025-09-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 16384, output: 16384 }, + }, + "deepseek.v3.2": { + id: "deepseek.v3.2", + name: "DeepSeek-V3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2026-02-06", + last_updated: "2026-02-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.62, output: 1.85 }, + limit: { context: 163840, output: 81920 }, + }, + "eu.anthropic.claude-haiku-4-5-20251001-v1:0": { + id: "eu.anthropic.claude-haiku-4-5-20251001-v1:0", + name: "Claude Haiku 4.5 (EU)", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + }, + "zai.glm-4.7-flash": { + id: "zai.glm-4.7-flash", + name: "GLM-4.7-Flash", + family: "glm-flash", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-01-19", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.4 }, + limit: { context: 200000, output: 131072 }, + }, + "us.anthropic.claude-opus-4-7": { + id: "us.anthropic.claude-opus-4-7", + name: "Claude Opus 4.7 (US)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "amazon.nova-2-lite-v1:0": { + id: "amazon.nova-2-lite-v1:0", + name: "Nova 2 Lite", + family: "nova", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.33, output: 2.75 }, + limit: { context: 128000, output: 4096 }, + }, + "anthropic.claude-opus-4-5-20251101-v1:0": { + id: "anthropic.claude-opus-4-5-20251101-v1:0", + name: "Claude Opus 4.5", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-24", + last_updated: "2025-08-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 64000 }, + }, + "qwen.qwen3-coder-480b-a35b-v1:0": { + id: "qwen.qwen3-coder-480b-a35b-v1:0", + name: "Qwen3 Coder 480B A35B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-09-18", + last_updated: "2025-09-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.22, output: 1.8 }, + limit: { context: 131072, output: 65536 }, + }, + "amazon.nova-lite-v1:0": { + id: "amazon.nova-lite-v1:0", + name: "Nova Lite", + family: "nova-lite", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-12-03", + last_updated: "2024-12-03", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.06, output: 0.24, cache_read: 0.015 }, + limit: { context: 300000, output: 8192 }, + }, + "meta.llama3-1-8b-instruct-v1:0": { + id: "meta.llama3-1-8b-instruct-v1:0", + name: "Llama 3.1 8B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.22, output: 0.22 }, + limit: { context: 128000, output: 4096 }, + }, + "anthropic.claude-opus-4-7": { + id: "anthropic.claude-opus-4-7", + name: "Claude Opus 4.7", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "google.gemma-3-27b-it": { + id: "google.gemma-3-27b-it", + name: "Google Gemma 3 27B Instruct", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-07-27", + last_updated: "2025-07-27", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.12, output: 0.2 }, + limit: { context: 202752, output: 8192 }, + }, + "global.anthropic.claude-haiku-4-5-20251001-v1:0": { + id: "global.anthropic.claude-haiku-4-5-20251001-v1:0", + name: "Claude Haiku 4.5 (Global)", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + }, + "google.gemma-3-4b-it": { + id: "google.gemma-3-4b-it", + name: "Gemma 3 4B IT", + family: "gemma", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.04, output: 0.08 }, + limit: { context: 128000, output: 4096 }, + }, + "meta.llama4-scout-17b-instruct-v1:0": { + id: "meta.llama4-scout-17b-instruct-v1:0", + name: "Llama 4 Scout 17B Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.17, output: 0.66 }, + limit: { context: 3500000, output: 16384 }, + }, + "deepseek.v3-v1:0": { + id: "deepseek.v3-v1:0", + name: "DeepSeek-V3.1", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-09-18", + last_updated: "2025-09-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.58, output: 1.68 }, + limit: { context: 163840, output: 81920 }, + }, + "mistral.magistral-small-2509": { + id: "mistral.magistral-small-2509", + name: "Magistral Small 1.2", + family: "magistral", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-02", + last_updated: "2025-12-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 1.5 }, + limit: { context: 128000, output: 40000 }, + }, + "qwen.qwen3-next-80b-a3b": { + id: "qwen.qwen3-next-80b-a3b", + name: "Qwen/Qwen3-Next-80B-A3B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 1.4 }, + limit: { context: 262000, output: 262000 }, + }, + "zai.glm-4.7": { + id: "zai.glm-4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2 }, + limit: { context: 204800, output: 131072 }, + }, + "moonshot.kimi-k2-thinking": { + id: "moonshot.kimi-k2-thinking", + name: "Kimi K2 Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: true, + structured_output: true, + temperature: true, + release_date: "2025-12-02", + last_updated: "2025-12-02", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5 }, + limit: { context: 256000, output: 256000 }, + }, + "us.anthropic.claude-opus-4-5-20251101-v1:0": { + id: "us.anthropic.claude-opus-4-5-20251101-v1:0", + name: "Claude Opus 4.5 (US)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-24", + last_updated: "2025-08-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 64000 }, + }, + "mistral.ministral-3-14b-instruct": { + id: "mistral.ministral-3-14b-instruct", + name: "Ministral 14B 3.0", + family: "ministral", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 128000, output: 4096 }, + }, + "deepseek.r1-v1:0": { + id: "deepseek.r1-v1:0", + name: "DeepSeek-R1", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-01-20", + last_updated: "2025-05-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.35, output: 5.4 }, + limit: { context: 128000, output: 32768 }, + }, + "mistral.voxtral-mini-3b-2507": { + id: "mistral.voxtral-mini-3b-2507", + name: "Voxtral Mini 3B 2507", + family: "mistral", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["audio", "text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.04, output: 0.04 }, + limit: { context: 128000, output: 4096 }, + }, + "openai.gpt-oss-120b-1:0": { + id: "openai.gpt-oss-120b-1:0", + name: "gpt-oss-120b", + family: "gpt-oss", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 128000, output: 4096 }, + }, + "nvidia.nemotron-nano-12b-v2": { + id: "nvidia.nemotron-nano-12b-v2", + name: "NVIDIA Nemotron Nano 12B v2 VL BF16", + family: "nemotron", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.6 }, + limit: { context: 128000, output: 4096 }, + }, + "eu.anthropic.claude-opus-4-7": { + id: "eu.anthropic.claude-opus-4-7", + name: "Claude Opus 4.7 (EU)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "minimax.minimax-m2.5": { + id: "minimax.minimax-m2.5", + name: "MiniMax M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 196608, output: 98304 }, + }, + "meta.llama3-3-70b-instruct-v1:0": { + id: "meta.llama3-3-70b-instruct-v1:0", + name: "Llama 3.3 70B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.72, output: 0.72 }, + limit: { context: 128000, output: 4096 }, + }, + "meta.llama3-1-70b-instruct-v1:0": { + id: "meta.llama3-1-70b-instruct-v1:0", + name: "Llama 3.1 70B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.72, output: 0.72 }, + limit: { context: 128000, output: 4096 }, + }, + "eu.anthropic.claude-sonnet-4-5-20250929-v1:0": { + id: "eu.anthropic.claude-sonnet-4-5-20250929-v1:0", + name: "Claude Sonnet 4.5 (EU)", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "eu.anthropic.claude-opus-4-5-20251101-v1:0": { + id: "eu.anthropic.claude-opus-4-5-20251101-v1:0", + name: "Claude Opus 4.5 (EU)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-24", + last_updated: "2025-08-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 64000 }, + }, + "moonshotai.kimi-k2.5": { + id: "moonshotai.kimi-k2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + release_date: "2026-02-06", + last_updated: "2026-02-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3 }, + limit: { context: 256000, output: 256000 }, + }, + "au.anthropic.claude-sonnet-4-6": { + id: "au.anthropic.claude-sonnet-4-6", + name: "AU Anthropic Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-08", + release_date: "2026-02-17", + last_updated: "2026-02-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3.3, output: 16.5, cache_read: 0.33, cache_write: 4.125 }, + limit: { context: 1000000, output: 128000 }, + }, + "openai.gpt-oss-safeguard-20b": { + id: "openai.gpt-oss-safeguard-20b", + name: "GPT OSS Safeguard 20B", + family: "gpt-oss", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.07, output: 0.2 }, + limit: { context: 128000, output: 4096 }, + }, + "qwen.qwen3-coder-30b-a3b-v1:0": { + id: "qwen.qwen3-coder-30b-a3b-v1:0", + name: "Qwen3 Coder 30B A3B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-09-18", + last_updated: "2025-09-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 262144, output: 131072 }, + }, + "minimax.minimax-m2.1": { + id: "minimax.minimax-m2.1", + name: "MiniMax M2.1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 204800, output: 131072 }, + }, + "qwen.qwen3-vl-235b-a22b": { + id: "qwen.qwen3-vl-235b-a22b", + name: "Qwen/Qwen3-VL-235B-A22B-Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-04", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.5 }, + limit: { context: 262000, output: 262000 }, + }, + "qwen.qwen3-coder-next": { + id: "qwen.qwen3-coder-next", + name: "Qwen3 Coder Next", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-06", + last_updated: "2026-02-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.22, output: 1.8 }, + limit: { context: 131072, output: 65536 }, + }, + "nvidia.nemotron-nano-9b-v2": { + id: "nvidia.nemotron-nano-9b-v2", + name: "NVIDIA Nemotron Nano 9B v2", + family: "nemotron", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.06, output: 0.23 }, + limit: { context: 128000, output: 4096 }, + }, + "mistral.mistral-large-3-675b-instruct": { + id: "mistral.mistral-large-3-675b-instruct", + name: "Mistral Large 3", + family: "mistral", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-02", + last_updated: "2025-12-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 1.5 }, + limit: { context: 256000, output: 8192 }, + }, + "qwen.qwen3-235b-a22b-2507-v1:0": { + id: "qwen.qwen3-235b-a22b-2507-v1:0", + name: "Qwen3 235B A22B 2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-09-18", + last_updated: "2025-09-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.22, output: 0.88 }, + limit: { context: 262144, output: 131072 }, + }, + "writer.palmyra-x4-v1:0": { + id: "writer.palmyra-x4-v1:0", + name: "Palmyra X4", + family: "palmyra", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-04-28", + last_updated: "2025-04-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10 }, + limit: { context: 122880, output: 8192 }, + }, + "anthropic.claude-opus-4-1-20250805-v1:0": { + id: "anthropic.claude-opus-4-1-20250805-v1:0", + name: "Claude Opus 4.1", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "us.deepseek.r1-v1:0": { + id: "us.deepseek.r1-v1:0", + name: "DeepSeek-R1 (US)", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-01-20", + last_updated: "2025-05-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.35, output: 5.4 }, + limit: { context: 128000, output: 32768 }, + }, + "eu.anthropic.claude-opus-4-6-v1": { + id: "eu.anthropic.claude-opus-4-6-v1", + name: "Claude Opus 4.6 (EU)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "us.meta.llama4-maverick-17b-instruct-v1:0": { + id: "us.meta.llama4-maverick-17b-instruct-v1:0", + name: "Llama 4 Maverick 17B Instruct (US)", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.24, output: 0.97 }, + limit: { context: 1000000, output: 16384 }, + }, + "au.anthropic.claude-haiku-4-5-20251001-v1:0": { + id: "au.anthropic.claude-haiku-4-5-20251001-v1:0", + name: "Claude Haiku 4.5 (AU)", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + }, + "jp.anthropic.claude-sonnet-4-5-20250929-v1:0": { + id: "jp.anthropic.claude-sonnet-4-5-20250929-v1:0", + name: "Claude Sonnet 4.5 (JP)", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic.claude-sonnet-4-6": { + id: "anthropic.claude-sonnet-4-6", + name: "Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 1000000, output: 64000 }, + }, + "jp.anthropic.claude-sonnet-4-6": { + id: "jp.anthropic.claude-sonnet-4-6", + name: "Claude Sonnet 4.6 (JP)", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 1000000, output: 64000 }, + }, + "global.anthropic.claude-sonnet-4-6": { + id: "global.anthropic.claude-sonnet-4-6", + name: "Claude Sonnet 4.6 (Global)", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 1000000, output: 64000 }, + }, + "us.anthropic.claude-sonnet-4-6": { + id: "us.anthropic.claude-sonnet-4-6", + name: "Claude Sonnet 4.6 (US)", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 1000000, output: 64000 }, + }, + "global.anthropic.claude-opus-4-6-v1": { + id: "global.anthropic.claude-opus-4-6-v1", + name: "Claude Opus 4.6 (Global)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "us.anthropic.claude-opus-4-6-v1": { + id: "us.anthropic.claude-opus-4-6-v1", + name: "Claude Opus 4.6 (US)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "us.anthropic.claude-opus-4-1-20250805-v1:0": { + id: "us.anthropic.claude-opus-4-1-20250805-v1:0", + name: "Claude Opus 4.1 (US)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "au.anthropic.claude-sonnet-4-5-20250929-v1:0": { + id: "au.anthropic.claude-sonnet-4-5-20250929-v1:0", + name: "Claude Sonnet 4.5 (AU)", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "eu.anthropic.claude-sonnet-4-6": { + id: "eu.anthropic.claude-sonnet-4-6", + name: "Claude Sonnet 4.6 (EU)", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 1000000, output: 64000 }, + }, + "us.meta.llama4-scout-17b-instruct-v1:0": { + id: "us.meta.llama4-scout-17b-instruct-v1:0", + name: "Llama 4 Scout 17B Instruct (US)", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.17, output: 0.66 }, + limit: { context: 3500000, output: 16384 }, + }, + "anthropic.claude-opus-4-6-v1": { + id: "anthropic.claude-opus-4-6-v1", + name: "Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "jp.anthropic.claude-opus-4-7": { + id: "jp.anthropic.claude-opus-4-7", + name: "Claude Opus 4.7 (JP)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + }, + }, + "the-grid-ai": { + id: "the-grid-ai", + env: ["THEGRIDAI_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.thegrid.ai/v1", + name: "The Grid AI", + doc: "https://thegrid.ai/docs", + models: { + "text-prime": { + id: "text-prime", + name: "Text Prime", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-26", + last_updated: "2026-02-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 30000 }, + status: "beta", + }, + "text-standard": { + id: "text-standard", + name: "Text Standard", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-26", + last_updated: "2026-02-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 16000 }, + status: "beta", + }, + "text-max": { + id: "text-max", + name: "Text Max", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-03-24", + last_updated: "2026-03-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 1000000, output: 128000 }, + status: "beta", + }, + }, + }, + baseten: { + id: "baseten", + env: ["BASETEN_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://inference.baseten.co/v1", + name: "Baseten", + doc: "https://docs.baseten.co/development/model-apis/overview", + models: { + "zai-org/GLM-4.7": { + id: "zai-org/GLM-4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2 }, + limit: { context: 204800, output: 131072 }, + }, + "zai-org/GLM-5": { + id: "zai-org/GLM-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2026-01", + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 3.15 }, + limit: { context: 202752, output: 131072 }, + }, + "zai-org/GLM-4.6": { + id: "zai-org/GLM-4.6", + name: "GLM 4.6", + family: "glm", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2025-09-16", + last_updated: "2025-09-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2 }, + limit: { context: 200000, output: 200000 }, + }, + "nvidia/Nemotron-120B-A12B": { + id: "nvidia/Nemotron-120B-A12B", + name: "Nemotron 3 Super", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2026-02", + release_date: "2026-03-11", + last_updated: "2026-03-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.75 }, + limit: { context: 262144, output: 32678 }, + }, + "deepseek-ai/DeepSeek-V3.1": { + id: "deepseek-ai/DeepSeek-V3.1", + name: "DeepSeek V3.1", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-25", + last_updated: "2025-08-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 1.5 }, + limit: { context: 164000, output: 131000 }, + }, + "deepseek-ai/DeepSeek-V3-0324": { + id: "deepseek-ai/DeepSeek-V3-0324", + name: "DeepSeek V3 0324", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-03-24", + last_updated: "2025-03-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.77, output: 0.77 }, + limit: { context: 164000, output: 131000 }, + }, + "deepseek-ai/DeepSeek-V3.2": { + id: "deepseek-ai/DeepSeek-V3.2", + name: "DeepSeek V3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-10", + release_date: "2025-12-01", + last_updated: "2026-03-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.45 }, + limit: { context: 163800, output: 131100 }, + status: "deprecated", + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "GPT OSS 120B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.5 }, + limit: { context: 128000, output: 128000 }, + }, + "moonshotai/Kimi-K2-Thinking": { + id: "moonshotai/Kimi-K2-Thinking", + name: "Kimi K2 Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-08", + release_date: "2025-11-06", + last_updated: "2026-03-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5 }, + limit: { context: 262144, output: 262144 }, + status: "deprecated", + }, + "moonshotai/Kimi-K2.6": { + id: "moonshotai/Kimi-K2.6", + name: "Kimi K2.6", + family: "kimi-k2.6", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4, cache_read: 0.16 }, + limit: { context: 262144, output: 262144 }, + }, + "moonshotai/Kimi-K2-Instruct-0905": { + id: "moonshotai/Kimi-K2-Instruct-0905", + name: "Kimi K2 Instruct 0905", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-08", + release_date: "2025-09-05", + last_updated: "2026-03-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5 }, + limit: { context: 262144, output: 262144 }, + status: "deprecated", + }, + "moonshotai/Kimi-K2.5": { + id: "moonshotai/Kimi-K2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-12", + release_date: "2026-01-30", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3 }, + limit: { context: 262144, output: 8192 }, + }, + "MiniMaxAI/MiniMax-M2.5": { + id: "MiniMaxAI/MiniMax-M2.5", + name: "MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2026-01", + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 204000, output: 204000 }, + }, + "deepseek-ai/DeepSeek-V4-Pro": { + id: "deepseek-ai/DeepSeek-V4-Pro", + name: "DeepSeek V4 Pro", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.74, output: 3.48, cache_read: 0.15 }, + limit: { context: 1000000, output: 384000 }, + }, + }, + }, + frogbot: { + id: "frogbot", + env: ["FROGBOT_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://app.frogbot.ai/api/v1", + name: "FrogBot", + doc: "https://docs.frogbot.ai", + models: { + "grok-4-1-fast-reasoning": { + id: "grok-4-1-fast-reasoning", + name: "Grok 4.1 Fast (Reasoning)", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-11", + release_date: "2025-11-25", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 128000 }, + }, + "claude-haiku-4-5": { + id: "claude-haiku-4-5", + name: "Claude Haiku 4.5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + }, + "kimi-k2.5": { + id: "kimi-k2.5", + name: "Kimi-K2.5", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "1970-01-01", + last_updated: "1970-01-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 3, cache_read: 0.1 }, + limit: { context: 256000, output: 128000 }, + }, + "claude-sonnet-4-6": { + id: "claude-sonnet-4-6", + name: "Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-02-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "gemini-3-flash-preview": { + id: "gemini-3-flash-preview", + name: "Gemini 3 Flash Preview", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 3, cache_read: 0.05 }, + limit: { context: 1048576, output: 65536 }, + }, + "claude-opus-4-7": { + id: "claude-opus-4-7", + name: "Claude Opus 4.7", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 128000 }, + }, + "zai-glm-5-1": { + id: "zai-glm-5-1", + name: "Z.AI GLM-5.1", + family: "glm", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-01-20", + last_updated: "2025-02-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.4, output: 4.4, cache_read: 0.26 }, + limit: { context: 198000, output: 8192 }, + }, + "gemini-2.5-pro": { + id: "gemini-2.5-pro", + name: "Gemini 2.5 Pro", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-20", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.31 }, + limit: { context: 1048576, output: 65536 }, + }, + "grok-4-1-fast-non-reasoning": { + id: "grok-4-1-fast-non-reasoning", + name: "Grok 4.1 Fast (Non-Reasoning)", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-11", + release_date: "2025-11-25", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 128000 }, + }, + "gpt-5-4-nano": { + id: "gpt-5-4-nano", + name: "GPT-5.4 Nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.25, cache_read: 0.02 }, + limit: { context: 400000, output: 128000 }, + }, + "gemini-2.5-flash": { + id: "gemini-2.5-flash", + name: "Gemini 2.5 Flash", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-07-17", + last_updated: "2025-07-17", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5, cache_read: 0.075 }, + limit: { context: 1048576, output: 65536 }, + }, + "grok-code-fast-1": { + id: "grok-code-fast-1", + name: "Grok 4.1 Fast (Reasoning)", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2025-08-28", + last_updated: "2025-08-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.5, cache_read: 0.02 }, + limit: { context: 256000, output: 128000 }, + }, + "gpt-5-5": { + id: "gpt-5-5", + name: "GPT-5.5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 15, cache_read: 0.25 }, + limit: { context: 272000, output: 128000 }, + }, + "grok-4-3": { + id: "grok-4-3", + name: "Grok 4.3", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-11", + release_date: "2026-04-30", + last_updated: "2026-04-30", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 2.5, cache_read: 0.2 }, + limit: { context: 1000000, output: 128000 }, + }, + "gpt-5-4-mini": { + id: "gpt-5-4-mini", + name: "GPT-5.4 Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.75, output: 4.5, cache_read: 0.075 }, + limit: { context: 400000, output: 128000 }, + }, + "claude-opus-4-6": { + id: "claude-opus-4-6", + name: "Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 128000 }, + }, + "deepseek-v4-pro": { + id: "deepseek-v4-pro", + name: "DeepSeek v4 Pro", + family: "deepseek", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2026-01", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.74, output: 3.48, cache_read: 0.14 }, + limit: { context: 128000, output: 8192 }, + }, + "gpt-oss-20b": { + id: "gpt-oss-20b", + name: "GPT OSS 20B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "1970-01-01", + last_updated: "1970-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.2 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen-3-6-plus": { + id: "qwen-3-6-plus", + name: "Qwen 3.6 Plus", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-02", + last_updated: "2026-04-03", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 3, cache_read: 0.1 }, + limit: { context: 1000000, output: 64000 }, + }, + "minimax-m2-7": { + id: "minimax-m2-7", + name: "MiniMax-M2.7", + family: "minimax", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-09", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2, cache_read: 0.06 }, + limit: { context: 192000, output: 8192 }, + }, + "minimax-m2-5": { + id: "minimax-m2-5", + name: "MiniMax-M2.5", + family: "minimax", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-09", + release_date: "2025-01-15", + last_updated: "2025-02-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2, cache_read: 0.03 }, + limit: { context: 192000, output: 8192 }, + }, + "gpt-4o": { + id: "gpt-4o", + name: "GPT-4o", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-09", + release_date: "2024-05-13", + last_updated: "2024-08-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10, cache_read: 1.25 }, + limit: { context: 128000, output: 16384 }, + }, + "gpt-oss-120b": { + id: "gpt-oss-120b", + name: "GPT OSS 120B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "1970-01-01", + last_updated: "1970-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 131072, output: 32768 }, + }, + "gemini-3-1-pro-preview": { + id: "gemini-3-1-pro-preview", + name: "Gemini 3.1 Pro Preview", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2026-01", + release_date: "2026-02-18", + last_updated: "2026-02-18", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2 }, + limit: { context: 1000000, output: 64000 }, + }, + "kimi-k2-6": { + id: "kimi-k2-6", + name: "Kimi-K2.6", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "1970-01-01", + last_updated: "1970-01-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.95, output: 4, cache_read: 0.16 }, + limit: { context: 256000, output: 128000 }, + }, + "gpt-5-3-codex": { + id: "gpt-5-3-codex", + name: "GPT-5.3 Codex", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-02-15", + last_updated: "2026-02-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, output: 128000 }, + }, + }, + }, + "zhipuai-coding-plan": { + id: "zhipuai-coding-plan", + env: ["ZHIPU_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://open.bigmodel.cn/api/coding/paas/v4", + name: "Zhipu AI Coding Plan", + doc: "https://docs.bigmodel.cn/cn/coding-plan/overview", + models: { + "glm-5v-turbo": { + id: "glm-5v-turbo", + name: "GLM-5V-Turbo", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-04-01", + last_updated: "2026-04-01", + modalities: { input: ["text", "image", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 200000, output: 131072 }, + }, + "glm-5.1": { + id: "glm-5.1", + name: "GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-03-27", + last_updated: "2026-03-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 200000, output: 131072 }, + }, + "glm-5-turbo": { + id: "glm-5-turbo", + name: "GLM-5-Turbo", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-03-16", + last_updated: "2026-03-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 200000, output: 131072 }, + }, + "glm-4.5-air": { + id: "glm-4.5-air", + name: "GLM-4.5-Air", + family: "glm-air", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 131072, output: 98304 }, + }, + "glm-4.7": { + id: "glm-4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + }, + }, + "alibaba-coding-plan": { + id: "alibaba-coding-plan", + env: ["ALIBABA_CODING_PLAN_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://coding-intl.dashscope.aliyuncs.com/v1", + name: "Alibaba Coding Plan", + doc: "https://www.alibabacloud.com/help/en/model-studio/coding-plan", + models: { + "qwen3-coder-plus": { + id: "qwen3-coder-plus", + name: "Qwen3 Coder Plus", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 1000000, output: 65536 }, + }, + "kimi-k2.5": { + id: "kimi-k2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-01", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 262144, output: 32768 }, + }, + "glm-4.7": { + id: "glm-4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 202752, output: 16384 }, + }, + "glm-5": { + id: "glm-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 202752, output: 16384 }, + }, + "MiniMax-M2.5": { + id: "MiniMax-M2.5", + name: "MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 196608, input: 196601, output: 24576 }, + }, + "qwen3.6-plus": { + id: "qwen3.6-plus", + name: "Qwen3.6 Plus", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 1000000, output: 65536 }, + }, + "qwen3-max-2026-01-23": { + id: "qwen3-max-2026-01-23", + name: "Qwen3 Max", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-01-23", + last_updated: "2026-01-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 262144, output: 32768 }, + }, + "qwen3-coder-next": { + id: "qwen3-coder-next", + name: "Qwen3 Coder Next", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-03", + last_updated: "2026-02-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen3.5-plus": { + id: "qwen3.5-plus", + name: "Qwen3.5 Plus", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-16", + last_updated: "2026-02-16", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 1000000, output: 65536 }, + }, + }, + }, + venice: { + id: "venice", + env: ["VENICE_API_KEY"], + npm: "venice-ai-sdk-provider", + name: "Venice AI", + doc: "https://docs.venice.ai", + models: { + "openai-gpt-4o-mini-2024-07-18": { + id: "openai-gpt-4o-mini-2024-07-18", + name: "GPT-4o Mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-28", + last_updated: "2026-03-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1875, output: 0.75, cache_read: 0.09375 }, + limit: { context: 128000, output: 16384 }, + }, + "qwen3-next-80b": { + id: "qwen3-next-80b", + name: "Qwen 3 Next 80b", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-04-29", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.35, output: 1.9 }, + limit: { context: 256000, output: 16384 }, + }, + "grok-4-20-multi-agent": { + id: "grok-4-20-multi-agent", + name: "Grok 4.20 Multi-Agent", + family: "grok", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2026-03-12", + last_updated: "2026-05-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { + input: 1.42, + output: 2.83, + cache_read: 0.23, + context_over_200k: { input: 2.83, output: 5.67, cache_read: 0.45 }, + }, + limit: { context: 2000000, output: 128000 }, + }, + "qwen3-235b-a22b-instruct-2507": { + id: "qwen3-235b-a22b-instruct-2507", + name: "Qwen 3 235B A22B Instruct 2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-04-29", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.75 }, + limit: { context: 128000, output: 16384 }, + }, + "z-ai-glm-5v-turbo": { + id: "z-ai-glm-5v-turbo", + name: "GLM 5V Turbo", + family: "glmv", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-01", + last_updated: "2026-04-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.5, output: 5, cache_read: 0.3 }, + limit: { context: 200000, output: 32768 }, + }, + "gemma-4-uncensored": { + id: "gemma-4-uncensored", + name: "Gemma 4 Uncensored", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-13", + last_updated: "2026-04-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1625, output: 0.5 }, + limit: { context: 256000, output: 8192 }, + }, + "grok-41-fast": { + id: "grok-41-fast", + name: "Grok 4.1 Fast", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-12-01", + last_updated: "2026-04-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.23, output: 0.57, cache_read: 0.06 }, + limit: { context: 1000000, output: 30000 }, + }, + "claude-sonnet-4-6": { + id: "claude-sonnet-4-6", + name: "Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-03-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3.6, output: 18, cache_read: 0.36, cache_write: 4.5 }, + limit: { context: 1000000, output: 64000 }, + }, + "nvidia-nemotron-cascade-2-30b-a3b": { + id: "nvidia-nemotron-cascade-2-30b-a3b", + name: "Nemotron Cascade 2 30B A3B", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-03-24", + last_updated: "2026-04-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.8 }, + limit: { context: 256000, output: 32768 }, + }, + "gemini-3-flash-preview": { + id: "gemini-3-flash-preview", + name: "Gemini 3 Flash Preview", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-12-19", + last_updated: "2026-03-12", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.7, output: 3.75, cache_read: 0.07 }, + limit: { context: 256000, output: 65536 }, + }, + "grok-4-20": { + id: "grok-4-20", + name: "Grok 4.20", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-03-12", + last_updated: "2026-05-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { + input: 1.42, + output: 2.83, + cache_read: 0.23, + context_over_200k: { input: 2.83, output: 5.67, cache_read: 0.45 }, + }, + limit: { context: 2000000, output: 128000 }, + }, + "google-gemma-4-26b-a4b-it": { + id: "google-gemma-4-26b-a4b-it", + name: "Google Gemma 4 26B A4B Instruct", + family: "gemma", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-02", + last_updated: "2026-04-12", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1625, output: 0.5 }, + limit: { context: 256000, output: 8192 }, + }, + "claude-opus-4-7": { + id: "claude-opus-4-7", + name: "Claude Opus 4.7", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 6, output: 30, cache_read: 0.6, cache_write: 7.5 }, + limit: { context: 1000000, output: 128000 }, + }, + "qwen3-coder-480b-a35b-instruct-turbo": { + id: "qwen3-coder-480b-a35b-instruct-turbo", + name: "Qwen 3 Coder 480B Turbo", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01-27", + last_updated: "2026-02-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.35, output: 1.5, cache_read: 0.04 }, + limit: { context: 256000, output: 65536 }, + }, + "qwen3-5-397b-a17b": { + id: "qwen3-5-397b-a17b", + name: "Qwen 3.5 397B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.75, output: 4.5 }, + limit: { context: 128000, output: 32768 }, + }, + "zai-org-glm-4.7": { + id: "zai-org-glm-4.7", + name: "GLM 4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-24", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 2.65, cache_read: 0.11 }, + limit: { context: 198000, output: 16384 }, + }, + "openai-gpt-54": { + id: "openai-gpt-54", + name: "GPT-5.4", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-03-05", + last_updated: "2026-03-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3.13, output: 18.8, cache_read: 0.313 }, + limit: { context: 1000000, output: 131072 }, + }, + "zai-org-glm-4.7-flash": { + id: "zai-org-glm-4.7-flash", + name: "GLM 4.7 Flash", + family: "glm-flash", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01-29", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.125, output: 0.5 }, + limit: { context: 128000, output: 16384 }, + }, + "nvidia-nemotron-3-nano-30b-a3b": { + id: "nvidia-nemotron-3-nano-30b-a3b", + name: "NVIDIA Nemotron 3 Nano 30B", + family: "nemotron", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01-27", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.075, output: 0.3 }, + limit: { context: 128000, output: 16384 }, + }, + "qwen3-vl-235b-a22b": { + id: "qwen3-vl-235b-a22b", + name: "Qwen3 VL 235B", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01-16", + last_updated: "2026-03-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 1.5 }, + limit: { context: 256000, output: 16384 }, + }, + "openai-gpt-53-codex": { + id: "openai-gpt-53-codex", + name: "GPT-5.3 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-24", + last_updated: "2026-03-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.19, output: 17.5, cache_read: 0.219 }, + limit: { context: 400000, output: 128000 }, + }, + "venice-uncensored-1-2": { + id: "venice-uncensored-1-2", + name: "Venice Uncensored 1.2", + family: "venice", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-01", + last_updated: "2026-04-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.9 }, + limit: { context: 128000, output: 8192 }, + }, + "openai-gpt-52": { + id: "openai-gpt-52", + name: "GPT-5.2", + family: "gpt", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2025-12-13", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.19, output: 17.5, cache_read: 0.219 }, + limit: { context: 256000, output: 65536 }, + }, + "mistral-small-3-2-24b-instruct": { + id: "mistral-small-3-2-24b-instruct", + name: "Mistral Small 3.2 24B Instruct", + family: "mistral-small", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01-15", + last_updated: "2026-03-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.09375, output: 0.25 }, + limit: { context: 256000, output: 16384 }, + }, + "minimax-m27": { + id: "minimax-m27", + name: "MiniMax M2.7", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-04-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.375, output: 1.5, cache_read: 0.075 }, + limit: { context: 198000, output: 32768 }, + }, + "qwen3-235b-a22b-thinking-2507": { + id: "qwen3-235b-a22b-thinking-2507", + name: "Qwen 3 235B A22B Thinking 2507", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-04-29", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.45, output: 3.5 }, + limit: { context: 128000, output: 16384 }, + }, + "qwen3-5-35b-a3b": { + id: "qwen3-5-35b-a3b", + name: "Qwen 3.5 35B A3B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-25", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3125, output: 1.25, cache_read: 0.15625 }, + limit: { context: 256000, output: 65536 }, + }, + "mercury-2": { + id: "mercury-2", + name: "Mercury 2", + family: "mercury", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-20", + last_updated: "2026-04-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3125, output: 0.9375, cache_read: 0.03125 }, + limit: { context: 128000, output: 50000 }, + }, + "google-gemma-3-27b-it": { + id: "google-gemma-3-27b-it", + name: "Google Gemma 3 27B Instruct", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-11-04", + last_updated: "2026-03-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.12, output: 0.2 }, + limit: { context: 198000, output: 16384 }, + }, + "olafangensan-glm-4.7-flash-heretic": { + id: "olafangensan-glm-4.7-flash-heretic", + name: "GLM 4.7 Flash Heretic", + family: "glm-flash", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-04", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.8 }, + limit: { context: 200000, output: 24000 }, + }, + "openai-gpt-55-pro": { + id: "openai-gpt-55-pro", + name: "GPT-5.5 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-24", + last_updated: "2026-04-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 37.5, output: 225 }, + limit: { context: 1000000, output: 128000 }, + }, + "openai-gpt-52-codex": { + id: "openai-gpt-52-codex", + name: "GPT-5.2 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-08", + release_date: "2025-01-15", + last_updated: "2026-03-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.19, output: 17.5, cache_read: 0.219 }, + limit: { context: 256000, output: 65536 }, + }, + "venice-uncensored-role-play": { + id: "venice-uncensored-role-play", + name: "Venice Role Play Uncensored", + family: "venice", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-20", + last_updated: "2026-03-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 2 }, + limit: { context: 128000, output: 4096 }, + }, + "zai-org-glm-5": { + id: "zai-org-glm-5", + name: "GLM 5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3.2, cache_read: 0.2 }, + limit: { context: 198000, output: 32000 }, + }, + "zai-org-glm-4.6": { + id: "zai-org-glm-4.6", + name: "GLM 4.6", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2024-04-01", + last_updated: "2026-04-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.85, output: 2.75, cache_read: 0.3 }, + limit: { context: 198000, output: 16384 }, + }, + "grok-4-3": { + id: "grok-4-3", + name: "Grok 4.3", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-18", + last_updated: "2026-05-04", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { + input: 1.42, + output: 2.83, + cache_read: 0.23, + context_over_200k: { input: 2.83, output: 5.67, cache_read: 0.45 }, + }, + limit: { context: 1000000, output: 32000 }, + }, + "mistral-small-2603": { + id: "mistral-small-2603", + name: "Mistral Small 4", + family: "mistral-small", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-03-16", + last_updated: "2026-04-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1875, output: 0.75 }, + limit: { context: 256000, output: 65536 }, + }, + "openai-gpt-oss-120b": { + id: "openai-gpt-oss-120b", + name: "OpenAI GPT OSS 120B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-11-06", + last_updated: "2026-05-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.3 }, + limit: { context: 128000, output: 16384 }, + }, + "claude-opus-4-5": { + id: "claude-opus-4-5", + name: "Claude Opus 4.5", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-06", + last_updated: "2026-04-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 6, output: 30, cache_read: 0.6, cache_write: 7.5 }, + limit: { context: 198000, output: 32768 }, + }, + "qwen3-5-9b": { + id: "qwen3-5-9b", + name: "Qwen 3.5 9B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-03-05", + last_updated: "2026-04-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.15 }, + limit: { context: 256000, output: 32768 }, + }, + "deepseek-v4-flash": { + id: "deepseek-v4-flash", + name: "DeepSeek V4 Flash", + family: "deepseek-flash", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-04-24", + last_updated: "2026-04-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.17, output: 0.35, cache_read: 0.028 }, + limit: { context: 1000000, output: 32768 }, + }, + "openai-gpt-54-pro": { + id: "openai-gpt-54-pro", + name: "GPT-5.4 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-03-05", + last_updated: "2026-03-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 37.5, output: 225, context_over_200k: { input: 75, output: 337.5 } }, + limit: { context: 1000000, output: 128000 }, + }, + "openai-gpt-54-mini": { + id: "openai-gpt-54-mini", + name: "GPT-5.4 Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-03-27", + last_updated: "2026-03-31", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.9375, output: 5.625, cache_read: 0.09375 }, + limit: { context: 400000, output: 128000 }, + }, + "minimax-m25": { + id: "minimax-m25", + name: "MiniMax M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-04-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.34, output: 1.19, cache_read: 0.04 }, + limit: { context: 198000, output: 32768 }, + }, + "zai-org-glm-5-1": { + id: "zai-org-glm-5-1", + name: "GLM 5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-04-07", + last_updated: "2026-04-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.75, output: 5.5, cache_read: 0.325 }, + limit: { context: 200000, output: 24000 }, + }, + "openai-gpt-55": { + id: "openai-gpt-55", + name: "GPT-5.5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-23", + last_updated: "2026-04-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { + input: 6.25, + output: 37.5, + cache_read: 0.625, + context_over_200k: { input: 12.5, output: 56.25, cache_read: 1.25 }, + }, + limit: { context: 1000000, output: 131072 }, + }, + "qwen3-6-27b": { + id: "qwen3-6-27b", + name: "Qwen 3.6 27B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-24", + last_updated: "2026-04-29", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.325, output: 3.25 }, + limit: { context: 256000, output: 65536 }, + }, + "claude-opus-4-6": { + id: "claude-opus-4-6", + name: "Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-03-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 6, output: 30, cache_read: 0.6, cache_write: 7.5 }, + limit: { context: 1000000, output: 128000 }, + }, + "deepseek-v4-pro": { + id: "deepseek-v4-pro", + name: "DeepSeek V4 Pro", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-04-24", + last_updated: "2026-04-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.73, output: 3.796, cache_read: 0.33 }, + limit: { context: 1000000, output: 32768 }, + }, + "deepseek-v3.2": { + id: "deepseek-v3.2", + name: "DeepSeek V3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-10", + release_date: "2025-12-04", + last_updated: "2026-03-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.33, output: 0.48, cache_read: 0.16 }, + limit: { context: 160000, output: 32768 }, + }, + "qwen-3-6-plus": { + id: "qwen-3-6-plus", + name: "Qwen 3.6 Plus Uncensored", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-06", + last_updated: "2026-04-12", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { + input: 0.625, + output: 3.75, + cache_read: 0.0625, + cache_write: 0.78, + context_over_200k: { input: 2.5, output: 7.5, cache_read: 0.0625, cache_write: 0.78 }, + }, + limit: { context: 1000000, output: 65536 }, + }, + "aion-labs-aion-2-0": { + id: "aion-labs-aion-2-0", + name: "Aion 2.0", + family: "o", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2026-03-24", + last_updated: "2026-04-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 2, cache_read: 0.25 }, + limit: { context: 128000, output: 32768 }, + }, + "claude-sonnet-4-5": { + id: "claude-sonnet-4-5", + name: "Claude Sonnet 4.5", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-01-15", + last_updated: "2026-04-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3.75, output: 18.75, cache_read: 0.375, cache_write: 4.69 }, + limit: { context: 198000, output: 64000 }, + }, + "openai-gpt-4o-2024-11-20": { + id: "openai-gpt-4o-2024-11-20", + name: "GPT-4o", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-28", + last_updated: "2026-03-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3.125, output: 12.5 }, + limit: { context: 128000, output: 16384 }, + }, + "llama-3.3-70b": { + id: "llama-3.3-70b", + name: "Llama 3.3 70B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2025-04-06", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.7, output: 2.8 }, + limit: { context: 128000, output: 4096 }, + }, + "kimi-k2-5": { + id: "kimi-k2-5", + name: "Kimi K2.5", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2026-01-27", + last_updated: "2026-04-30", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.56, output: 3.5, cache_read: 0.22 }, + limit: { context: 256000, output: 65536 }, + }, + "llama-3.2-3b": { + id: "llama-3.2-3b", + name: "Llama 3.2 3B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-10-03", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 128000, output: 4096 }, + }, + "arcee-trinity-large-thinking": { + id: "arcee-trinity-large-thinking", + name: "Trinity Large Thinking", + family: "trinity", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-04-02", + last_updated: "2026-04-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3125, output: 1.125, cache_read: 0.075 }, + limit: { context: 256000, output: 65536 }, + }, + "hermes-3-llama-3.1-405b": { + id: "hermes-3-llama-3.1-405b", + name: "Hermes 3 Llama 3.1 405b", + family: "hermes", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-04", + release_date: "2025-09-25", + last_updated: "2026-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.1, output: 3 }, + limit: { context: 128000, output: 16384 }, + }, + "gemini-3-1-pro-preview": { + id: "gemini-3-1-pro-preview", + name: "Gemini 3.1 Pro Preview", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-19", + last_updated: "2026-03-12", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + cost: { + input: 2.5, + output: 15, + cache_read: 0.5, + cache_write: 0.5, + context_over_200k: { input: 5, output: 22.5, cache_read: 0.5 }, + }, + limit: { context: 1000000, output: 32768 }, + }, + "kimi-k2-6": { + id: "kimi-k2-6", + name: "Kimi K2.6", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-04-20", + last_updated: "2026-04-30", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.85, output: 4.655, cache_read: 0.22 }, + limit: { context: 256000, output: 65536 }, + }, + "claude-opus-4-6-fast": { + id: "claude-opus-4-6-fast", + name: "Claude Opus 4.6 Fast", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-04-08", + last_updated: "2026-04-08", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 36, output: 180, cache_read: 3.6, cache_write: 45 }, + limit: { context: 1000000, output: 128000 }, + }, + "z-ai-glm-5-turbo": { + id: "z-ai-glm-5-turbo", + name: "GLM 5 Turbo", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-03-15", + last_updated: "2026-04-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.2, output: 4, cache_read: 0.24 }, + limit: { context: 200000, output: 32768 }, + }, + "google-gemma-4-31b-it": { + id: "google-gemma-4-31b-it", + name: "Google Gemma 4 31B Instruct", + family: "gemma", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-03", + last_updated: "2026-04-12", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.175, output: 0.5 }, + limit: { context: 256000, output: 8192 }, + }, + }, + }, + aihubmix: { + id: "aihubmix", + env: ["AIHUBMIX_API_KEY"], + npm: "@aihubmix/ai-sdk-provider", + name: "AIHubMix", + doc: "https://docs.aihubmix.com", + models: { + "minimax-m2.7": { + id: "minimax-m2.7", + name: "MiniMax-M2.7", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2958, output: 1.1832, cache_read: 0.05916 }, + limit: { context: 200000, output: 128000 }, + }, + "coding-glm-5.1-free": { + id: "coding-glm-5.1-free", + name: "Coding GLM 5.1 (free)", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-04-11", + last_updated: "2026-04-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0 }, + limit: { context: 204800, output: 128000 }, + }, + "gemini-3.1-pro-preview-customtools": { + id: "gemini-3.1-pro-preview-customtools", + name: "Gemini 3.1 Pro Preview Custom Tools", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-19", + last_updated: "2026-02-19", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2, context_over_200k: { input: 4, output: 18, cache_read: 0.4 } }, + limit: { context: 1048576, output: 65536 }, + }, + "kimi-k2.5": { + id: "kimi-k2.5", + name: "Kimi K2.5", + family: "kimi-k2.5", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: false, + knowledge: "2025-01", + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3, cache_read: 0.105 }, + limit: { context: 256000, output: 0 }, + }, + "glm-5v-turbo": { + id: "glm-5v-turbo", + name: "GLM 5 Vision Turbo", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-05-09", + last_updated: "2026-05-09", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.7042, output: 3.09848, cache_read: 0.169008 }, + limit: { context: 200000, output: 128000 }, + }, + "grok-4.3": { + id: "grok-4.3", + name: "Grok 4.3", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-05-01", + last_updated: "2026-05-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { + input: 1.25, + output: 2.5, + cache_read: 0.2, + context_over_200k: { input: 2.5, output: 5, cache_read: 0.4 }, + }, + limit: { context: 1000000, output: 1000000 }, + }, + "coding-minimax-m2.7-highspeed": { + id: "coding-minimax-m2.7-highspeed", + name: "Coding MiniMax M2.7 Highspeed", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 204800, output: 13100 }, + }, + "claude-sonnet-4-6": { + id: "claude-sonnet-4-6", + name: "Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-02-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 3, + output: 15, + cache_read: 0.3, + cache_write: 3.75, + context_over_200k: { input: 6, output: 22.5, cache_read: 0.6, cache_write: 7.5 }, + }, + limit: { context: 200000, output: 64000 }, + }, + "gemini-3.1-pro-preview": { + id: "gemini-3.1-pro-preview", + name: "Gemini 3.1 Pro Preview", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-19", + last_updated: "2026-02-19", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2 }, + limit: { context: 1048576, output: 65536 }, + }, + "coding-glm-5.1": { + id: "coding-glm-5.1", + name: "Coding-GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-04-11", + last_updated: "2026-04-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.06, output: 0.22 }, + limit: { context: 200000, output: 128000 }, + }, + "gemini-3-flash-preview": { + id: "gemini-3-flash-preview", + name: "Gemini 3 Flash Preview", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 3, cache_read: 0.05 }, + limit: { context: 1048576, output: 65536 }, + }, + "gpt-5.5": { + id: "gpt-5.5", + name: "GPT-5.5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-12-01", + release_date: "2026-04-23", + last_updated: "2026-04-23", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 30, cache_read: 0.5 }, + limit: { context: 1050000, output: 128000 }, + }, + "claude-opus-4-7": { + id: "claude-opus-4-7", + name: "Claude Opus 4.7", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "deepseek-v4-flash-think": { + id: "deepseek-v4-flash-think", + name: "DeepSeek V4 Flash Think", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.154, output: 0.308, cache_read: 0.0308 }, + limit: { context: 1000000, output: 384000 }, + }, + "gpt-5.3-codex": { + id: "gpt-5.3-codex", + name: "GPT-5.3 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gemini-2.5-pro": { + id: "gemini-2.5-pro", + name: "Gemini 2.5 Pro", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-20", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 1048576, output: 65536 }, + }, + "gpt-5.2": { + id: "gpt-5.2", + name: "GPT-5.2", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, output: 128000 }, + }, + "qwen3.6-plus": { + id: "qwen3.6-plus", + name: "Qwen3.6 Plus", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-05-09", + last_updated: "2026-05-09", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.282, output: 1.692, cache_read: 0.0282, cache_write: 0.3525 }, + limit: { context: 991000, output: 64000 }, + }, + "gpt-5.4-mini": { + id: "gpt-5.4-mini", + name: "GPT-5.4-Mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: false, + release_date: "2026-03-11", + last_updated: "2026-03-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.75, output: 4.5, cache_read: 0.075 }, + limit: { context: 400000, output: 128000 }, + }, + "glm-5.1": { + id: "glm-5.1", + name: "GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-03-27", + last_updated: "2026-03-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.845, output: 3.38, cache_read: 0.183112 }, + limit: { context: 200000, output: 128000 }, + }, + "o4-mini": { + id: "o4-mini", + name: "o4-mini", + family: "o-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05", + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.275 }, + limit: { context: 200000, output: 100000 }, + }, + "gpt-5.2-codex": { + id: "gpt-5.2-codex", + name: "GPT-5.2-Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + knowledge: "2025-08-31", + release_date: "2026-01-14", + last_updated: "2026-01-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, output: 128000 }, + }, + "gemini-2.5-flash": { + id: "gemini-2.5-flash", + name: "Gemini 2.5 Flash", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-20", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.499, cache_read: 0.03 }, + limit: { context: 1048576, output: 65536 }, + }, + "gpt-5.1-codex-mini": { + id: "gpt-5.1-codex-mini", + name: "GPT-5.1 Codex mini", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.025 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gemini-3.1-flash-lite": { + id: "gemini-3.1-flash-lite", + name: "Gemini 3.1 Flash Lite", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-03-03", + last_updated: "2026-03-03", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.5, cache_read: 0.25 }, + limit: { context: 1048576, output: 65536 }, + }, + "gpt-5.1": { + id: "gpt-5.1", + name: "GPT-5.1", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-11", + release_date: "2025-11-15", + last_updated: "2025-11-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "claude-opus-4-6-think": { + id: "claude-opus-4-6-think", + name: "Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 32000 }, + }, + "coding-minimax-m2.7-free": { + id: "coding-minimax-m2.7-free", + name: "Coding-MiniMax-M2.7-Free", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 204800, output: 13100 }, + }, + "deepseek-v4-flash": { + id: "deepseek-v4-flash", + name: "DeepSeek V4 Flash", + family: "deepseek-flash", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.154, output: 0.308, cache_read: 0.0308 }, + limit: { context: 1000000, output: 384000 }, + }, + "kimi-k2.6": { + id: "kimi-k2.6", + name: "Kimi K2.6", + family: "kimi-k2.6", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 3.9995, cache_read: 0.160835 }, + limit: { context: 262144, output: 262144 }, + }, + "gpt-5.4": { + id: "gpt-5.4", + name: "GPT-5.4", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + release_date: "2026-03-11", + last_updated: "2026-03-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 15, cache_read: 0.25 }, + limit: { context: 400000, output: 128000 }, + }, + "claude-opus-4-6": { + id: "claude-opus-4-6", + name: "Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "deepseek-v4-pro": { + id: "deepseek-v4-pro", + name: "DeepSeek V4 Pro", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.478, output: 0.956, cache_read: 0.004302 }, + limit: { context: 1000000, output: 384000 }, + }, + "claude-opus-4-7-think": { + id: "claude-opus-4-7-think", + name: "Claude Opus 4.7 Thinking", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 32000 }, + }, + "coding-minimax-m2.7": { + id: "coding-minimax-m2.7", + name: "Coding MiniMax M2.7", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 204800, output: 13100 }, + }, + "qwen3.6-max-preview": { + id: "qwen3.6-max-preview", + name: "Qwen3.6 Max Preview", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-05-09", + last_updated: "2026-05-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.268, output: 7.608, cache_read: 0.1268, cache_write: 1.585 }, + limit: { context: 240000, output: 64000 }, + }, + "gpt-4.1": { + id: "gpt-4.1", + name: "GPT-4.1", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 1047576, output: 32768 }, + }, + "gpt-4.1-mini": { + id: "gpt-4.1-mini", + name: "GPT-4.1 mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.6, cache_read: 0.1 }, + limit: { context: 1047576, output: 32768 }, + }, + "gpt-5.1-codex": { + id: "gpt-5.1-codex", + name: "GPT-5.1 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "claude-sonnet-4-6-think": { + id: "claude-sonnet-4-6-think", + name: "Claude Sonnet 4.6 Think", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-02-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 3, + output: 15, + cache_read: 0.3, + cache_write: 3.75, + context_over_200k: { input: 6, output: 22.5, cache_read: 0.6, cache_write: 7.5 }, + }, + limit: { context: 200000, output: 64000 }, + }, + "qwen3.6-flash": { + id: "qwen3.6-flash", + name: "Qwen3.6 Flash", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.169, output: 1.014, cache_read: 0.0169, cache_write: 0.21125 }, + limit: { context: 991000, output: 64000 }, + }, + }, + }, + cerebras: { + id: "cerebras", + env: ["CEREBRAS_API_KEY"], + npm: "@ai-sdk/cerebras", + name: "Cerebras", + doc: "https://inference-docs.cerebras.ai/models/overview", + models: { + "llama3.1-8b": { + id: "llama3.1-8b", + name: "Llama 3.1 8B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 32000, output: 8000 }, + }, + "qwen-3-235b-a22b-instruct-2507": { + id: "qwen-3-235b-a22b-instruct-2507", + name: "Qwen 3 235B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-22", + last_updated: "2025-07-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 1.2 }, + limit: { context: 131000, output: 32000 }, + }, + "zai-glm-4.7": { + id: "zai-glm-4.7", + name: "Z.AI GLM-4.7", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-01-10", + last_updated: "2026-01-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.25, output: 2.75, cache_read: 0, cache_write: 0 }, + limit: { context: 131072, output: 40000 }, + }, + "gpt-oss-120b": { + id: "gpt-oss-120b", + name: "GPT OSS 120B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 0.69 }, + limit: { context: 131072, output: 32768 }, + }, + }, + }, + lmstudio: { + id: "lmstudio", + env: ["LMSTUDIO_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "http://127.0.0.1:1234/v1", + name: "LMStudio", + doc: "https://lmstudio.ai/models", + models: { + "openai/gpt-oss-20b": { + id: "openai/gpt-oss-20b", + name: "GPT OSS 20B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 32768 }, + }, + "qwen/qwen3-coder-30b": { + id: "qwen/qwen3-coder-30b", + name: "Qwen3 Coder 30B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen/qwen3-30b-a3b-2507": { + id: "qwen/qwen3-30b-a3b-2507", + name: "Qwen3 30B A3B 2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-30", + last_updated: "2025-07-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 16384 }, + }, + }, + }, + lucidquery: { + id: "lucidquery", + env: ["LUCIDQUERY_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://lucidquery.com/api/v1", + name: "LucidQuery AI", + doc: "https://lucidquery.com/api/docs", + models: { + "lucidnova-rf1-100b": { + id: "lucidnova-rf1-100b", + name: "LucidNova RF1 100B", + family: "nova", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-09-16", + release_date: "2024-12-28", + last_updated: "2025-09-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 5 }, + limit: { context: 120000, output: 8000 }, + }, + "lucidquery-nexus-coder": { + id: "lucidquery-nexus-coder", + name: "LucidQuery Nexus Coder", + family: "lucid", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-08-01", + release_date: "2025-09-01", + last_updated: "2025-09-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 5 }, + limit: { context: 250000, output: 60000 }, + }, + }, + }, + "moonshotai-cn": { + id: "moonshotai-cn", + env: ["MOONSHOT_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.moonshot.cn/v1", + name: "Moonshot AI (China)", + doc: "https://platform.moonshot.cn/docs/api/chat", + models: { + "kimi-k2-thinking": { + id: "kimi-k2-thinking", + name: "Kimi K2 Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-08", + release_date: "2025-11-06", + last_updated: "2025-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5, cache_read: 0.15 }, + limit: { context: 262144, output: 262144 }, + }, + "kimi-k2-0711-preview": { + id: "kimi-k2-0711-preview", + name: "Kimi K2 0711", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-07-14", + last_updated: "2025-07-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5, cache_read: 0.15 }, + limit: { context: 131072, output: 16384 }, + }, + "kimi-k2-turbo-preview": { + id: "kimi-k2-turbo-preview", + name: "Kimi K2 Turbo", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.4, output: 10, cache_read: 0.6 }, + limit: { context: 262144, output: 262144 }, + }, + "kimi-k2.6": { + id: "kimi-k2.6", + name: "Kimi K2.6", + family: "kimi-k2.6", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4, cache_read: 0.16 }, + limit: { context: 262144, output: 262144 }, + }, + "kimi-k2-thinking-turbo": { + id: "kimi-k2-thinking-turbo", + name: "Kimi K2 Thinking Turbo", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-08", + release_date: "2025-11-06", + last_updated: "2025-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.15, output: 8, cache_read: 0.15 }, + limit: { context: 262144, output: 262144 }, + }, + "kimi-k2.5": { + id: "kimi-k2.5", + name: "Kimi K2.5", + family: "kimi-k2.5", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: false, + knowledge: "2025-01", + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3, cache_read: 0.1 }, + limit: { context: 262144, output: 262144 }, + }, + "kimi-k2-0905-preview": { + id: "kimi-k2-0905-preview", + name: "Kimi K2 0905", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5, cache_read: 0.15 }, + limit: { context: 262144, output: 262144 }, + }, + }, + }, + "azure-cognitive-services": { + id: "azure-cognitive-services", + env: ["AZURE_COGNITIVE_SERVICES_RESOURCE_NAME", "AZURE_COGNITIVE_SERVICES_API_KEY"], + npm: "@ai-sdk/azure", + name: "Azure Cognitive Services", + doc: "https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models", + models: { + "claude-haiku-4-5": { + id: "claude-haiku-4-5", + name: "Claude Haiku 4.5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-02-31", + release_date: "2025-11-18", + last_updated: "2025-11-18", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + provider: { + npm: "@ai-sdk/anthropic", + api: "https://${AZURE_COGNITIVE_SERVICES_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1", + }, + }, + "claude-opus-4-1": { + id: "claude-opus-4-1", + name: "Claude Opus 4.1", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-18", + last_updated: "2025-11-18", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + provider: { + npm: "@ai-sdk/anthropic", + api: "https://${AZURE_COGNITIVE_SERVICES_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1", + }, + }, + "claude-opus-4-5": { + id: "claude-opus-4-5", + name: "Claude Opus 4.5", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-24", + last_updated: "2025-08-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 64000 }, + provider: { + npm: "@ai-sdk/anthropic", + api: "https://${AZURE_COGNITIVE_SERVICES_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1", + }, + }, + "kimi-k2.6": { + id: "kimi-k2.6", + name: "Kimi K2.6", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4 }, + limit: { context: 262144, output: 262144 }, + provider: { + npm: "@ai-sdk/openai-compatible", + api: "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/models", + shape: "completions", + }, + }, + "claude-opus-4-6": { + id: "claude-opus-4-6", + name: "Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 5, + output: 25, + cache_read: 0.5, + cache_write: 6.25, + context_over_200k: { input: 10, output: 37.5, cache_read: 1, cache_write: 12.5 }, + }, + limit: { context: 200000, output: 128000 }, + provider: { + npm: "@ai-sdk/anthropic", + api: "https://${AZURE_COGNITIVE_SERVICES_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1", + }, + }, + "claude-sonnet-4-5": { + id: "claude-sonnet-4-5", + name: "Claude Sonnet 4.5", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-11-18", + last_updated: "2025-11-18", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + provider: { + npm: "@ai-sdk/anthropic", + api: "https://${AZURE_COGNITIVE_SERVICES_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1", + }, + }, + "mai-ds-r1": { + id: "mai-ds-r1", + name: "MAI-DS-R1", + family: "mai", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2024-06", + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.35, output: 5.4 }, + limit: { context: 128000, output: 8192 }, + }, + "llama-4-maverick-17b-128e-instruct-fp8": { + id: "llama-4-maverick-17b-128e-instruct-fp8", + name: "Llama 4 Maverick 17B 128E Instruct FP8", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 1 }, + limit: { context: 128000, output: 8192 }, + }, + "codestral-2501": { + id: "codestral-2501", + name: "Codestral 25.01", + family: "codestral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-03", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.9 }, + limit: { context: 256000, output: 256000 }, + }, + "gpt-5.1-codex": { + id: "gpt-5.1-codex", + name: "GPT-5.1 Codex", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-14", + last_updated: "2025-11-14", + modalities: { input: ["text", "image", "audio"], output: ["text", "image", "audio"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "kimi-k2-thinking": { + id: "kimi-k2-thinking", + name: "Kimi K2 Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-11-06", + last_updated: "2025-12-02", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5, cache_read: 0.15 }, + limit: { context: 262144, output: 262144 }, + }, + "deepseek-r1-0528": { + id: "deepseek-r1-0528", + name: "DeepSeek-R1-0528", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-05-28", + last_updated: "2025-05-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.35, output: 5.4 }, + limit: { context: 163840, output: 163840 }, + }, + "gpt-3.5-turbo-instruct": { + id: "gpt-3.5-turbo-instruct", + name: "GPT-3.5 Turbo Instruct", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2021-08", + release_date: "2023-09-21", + last_updated: "2023-09-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.5, output: 2 }, + limit: { context: 4096, output: 4096 }, + }, + "mistral-medium-2505": { + id: "mistral-medium-2505", + name: "Mistral Medium 3", + family: "mistral-medium", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-05", + release_date: "2025-05-07", + last_updated: "2025-05-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2 }, + limit: { context: 128000, output: 128000 }, + }, + "phi-4-reasoning-plus": { + id: "phi-4-reasoning-plus", + name: "Phi-4-reasoning-plus", + family: "phi", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.125, output: 0.5 }, + limit: { context: 32000, output: 4096 }, + }, + "cohere-embed-v3-english": { + id: "cohere-embed-v3-english", + name: "Embed v3 English", + family: "cohere-embed", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2023-11-07", + last_updated: "2023-11-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0 }, + limit: { context: 512, output: 1024 }, + }, + "gpt-4-32k": { + id: "gpt-4-32k", + name: "GPT-4 32K", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-11", + release_date: "2023-03-14", + last_updated: "2023-03-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 60, output: 120 }, + limit: { context: 32768, output: 32768 }, + }, + "gpt-5": { + id: "gpt-5", + name: "GPT-5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.13 }, + limit: { context: 272000, output: 128000 }, + }, + "phi-4": { + id: "phi-4", + name: "Phi-4", + family: "phi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.125, output: 0.5 }, + limit: { context: 128000, output: 4096 }, + }, + "gpt-3.5-turbo-0613": { + id: "gpt-3.5-turbo-0613", + name: "GPT-3.5 Turbo 0613", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2021-08", + release_date: "2023-06-13", + last_updated: "2023-06-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 4 }, + limit: { context: 16384, output: 16384 }, + }, + "phi-3-medium-128k-instruct": { + id: "phi-3-medium-128k-instruct", + name: "Phi-3-medium-instruct (128k)", + family: "phi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-04-23", + last_updated: "2024-04-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.17, output: 0.68 }, + limit: { context: 128000, output: 4096 }, + }, + "deepseek-v3.2": { + id: "deepseek-v3.2", + name: "DeepSeek-V3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.58, output: 1.68 }, + limit: { context: 128000, output: 128000 }, + }, + "phi-3-small-128k-instruct": { + id: "phi-3-small-128k-instruct", + name: "Phi-3-small-instruct (128k)", + family: "phi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-04-23", + last_updated: "2024-04-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 128000, output: 4096 }, + }, + "gpt-3.5-turbo-0301": { + id: "gpt-3.5-turbo-0301", + name: "GPT-3.5 Turbo 0301", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2021-08", + release_date: "2023-03-01", + last_updated: "2023-03-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.5, output: 2 }, + limit: { context: 4096, output: 4096 }, + }, + "phi-4-mini": { + id: "phi-4-mini", + name: "Phi-4-mini", + family: "phi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.075, output: 0.3 }, + limit: { context: 128000, output: 4096 }, + }, + "gpt-5-codex": { + id: "gpt-5-codex", + name: "GPT-5-Codex", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-09-15", + last_updated: "2025-09-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.13 }, + limit: { context: 400000, output: 128000 }, + }, + "meta-llama-3-8b-instruct": { + id: "meta-llama-3-8b-instruct", + name: "Meta-Llama-3-8B-Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-12", + release_date: "2024-04-18", + last_updated: "2024-04-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.61 }, + limit: { context: 8192, output: 2048 }, + }, + "gpt-4": { + id: "gpt-4", + name: "GPT-4", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-11", + release_date: "2023-03-14", + last_updated: "2023-03-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 60, output: 120 }, + limit: { context: 8192, output: 8192 }, + }, + "phi-4-mini-reasoning": { + id: "phi-4-mini-reasoning", + name: "Phi-4-mini-reasoning", + family: "phi", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.075, output: 0.3 }, + limit: { context: 128000, output: 4096 }, + }, + "meta-llama-3.1-70b-instruct": { + id: "meta-llama-3.1-70b-instruct", + name: "Meta-Llama-3.1-70B-Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.68, output: 3.54 }, + limit: { context: 128000, output: 32768 }, + }, + "phi-3-mini-4k-instruct": { + id: "phi-3-mini-4k-instruct", + name: "Phi-3-mini-instruct (4k)", + family: "phi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-04-23", + last_updated: "2024-04-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.13, output: 0.52 }, + limit: { context: 4096, output: 1024 }, + }, + "deepseek-v3.1": { + id: "deepseek-v3.1", + name: "DeepSeek-V3.1", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-08-21", + last_updated: "2025-08-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.56, output: 1.68 }, + limit: { context: 131072, output: 131072 }, + }, + "text-embedding-3-small": { + id: "text-embedding-3-small", + name: "text-embedding-3-small", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + release_date: "2024-01-25", + last_updated: "2024-01-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.02, output: 0 }, + limit: { context: 8191, output: 1536 }, + }, + "gpt-3.5-turbo-1106": { + id: "gpt-3.5-turbo-1106", + name: "GPT-3.5 Turbo 1106", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2021-08", + release_date: "2023-11-06", + last_updated: "2023-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 2 }, + limit: { context: 16384, output: 16384 }, + }, + "model-router": { + id: "model-router", + name: "Model Router", + family: "model-router", + attachment: true, + reasoning: false, + tool_call: true, + release_date: "2025-05-19", + last_updated: "2025-11-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "mistral-small-2503": { + id: "mistral-small-2503", + name: "Mistral Small 3.1", + family: "mistral-small", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-09", + release_date: "2025-03-01", + last_updated: "2025-03-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 128000, output: 32768 }, + }, + o1: { + id: "o1", + name: "o1", + family: "o", + attachment: false, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2023-09", + release_date: "2024-12-05", + last_updated: "2024-12-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 60, cache_read: 7.5 }, + limit: { context: 200000, output: 100000 }, + }, + "grok-4-fast-reasoning": { + id: "grok-4-fast-reasoning", + name: "Grok 4 Fast (Reasoning)", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-09-19", + last_updated: "2025-09-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + "gpt-5.1": { + id: "gpt-5.1", + name: "GPT-5.1", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-14", + last_updated: "2025-11-14", + modalities: { input: ["text", "image", "audio"], output: ["text", "image", "audio"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 272000, output: 128000 }, + }, + "cohere-embed-v3-multilingual": { + id: "cohere-embed-v3-multilingual", + name: "Embed v3 Multilingual", + family: "cohere-embed", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2023-11-07", + last_updated: "2023-11-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0 }, + limit: { context: 512, output: 1024 }, + }, + "o1-preview": { + id: "o1-preview", + name: "o1-preview", + family: "o", + attachment: false, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2023-09", + release_date: "2024-09-12", + last_updated: "2024-09-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 16.5, output: 66, cache_read: 8.25 }, + limit: { context: 128000, output: 32768 }, + }, + "gpt-3.5-turbo-0125": { + id: "gpt-3.5-turbo-0125", + name: "GPT-3.5 Turbo 0125", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2021-08", + release_date: "2024-01-25", + last_updated: "2024-01-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 1.5 }, + limit: { context: 16384, output: 16384 }, + }, + "gpt-5.1-codex-mini": { + id: "gpt-5.1-codex-mini", + name: "GPT-5.1 Codex Mini", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-14", + last_updated: "2025-11-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.025 }, + limit: { context: 400000, output: 128000 }, + }, + "cohere-embed-v-4-0": { + id: "cohere-embed-v-4-0", + name: "Embed v4", + family: "cohere-embed", + attachment: true, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-04-15", + last_updated: "2025-04-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.12, output: 0 }, + limit: { context: 128000, output: 1536 }, + }, + "gpt-5.2-codex": { + id: "gpt-5.2-codex", + name: "GPT-5.2 Codex", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-01-14", + last_updated: "2026-01-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, output: 128000 }, + }, + "gpt-4-turbo-vision": { + id: "gpt-4-turbo-vision", + name: "GPT-4 Turbo Vision", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-11", + release_date: "2023-11-06", + last_updated: "2024-04-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 10, output: 30 }, + limit: { context: 128000, output: 4096 }, + }, + "gpt-5.1-chat": { + id: "gpt-5.1-chat", + name: "GPT-5.1 Chat", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-14", + last_updated: "2025-11-14", + modalities: { input: ["text", "image", "audio"], output: ["text", "image", "audio"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 128000, output: 16384 }, + }, + "meta-llama-3.1-405b-instruct": { + id: "meta-llama-3.1-405b-instruct", + name: "Meta-Llama-3.1-405B-Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 5.33, output: 16 }, + limit: { context: 128000, output: 32768 }, + }, + "llama-3.2-11b-vision-instruct": { + id: "llama-3.2-11b-vision-instruct", + name: "Llama-3.2-11B-Vision-Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-09-25", + last_updated: "2024-09-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.37, output: 0.37 }, + limit: { context: 128000, output: 8192 }, + }, + "cohere-command-a": { + id: "cohere-command-a", + name: "Command A", + family: "command-a", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-06-01", + release_date: "2025-03-13", + last_updated: "2025-03-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.5, output: 10 }, + limit: { context: 256000, output: 8000 }, + }, + "mistral-large-2411": { + id: "mistral-large-2411", + name: "Mistral Large 24.11", + family: "mistral-large", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-09", + release_date: "2024-11-01", + last_updated: "2024-11-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6 }, + limit: { context: 128000, output: 32768 }, + }, + "gpt-5.2": { + id: "gpt-5.2", + name: "GPT-5.2", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "deepseek-v3.2-speciale": { + id: "deepseek-v3.2-speciale", + name: "DeepSeek-V3.2-Speciale", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2024-07", + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.58, output: 1.68 }, + limit: { context: 128000, output: 128000 }, + }, + "deepseek-r1": { + id: "deepseek-r1", + name: "DeepSeek-R1", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2024-07", + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.35, output: 5.4 }, + limit: { context: 163840, output: 163840 }, + }, + "llama-3.2-90b-vision-instruct": { + id: "llama-3.2-90b-vision-instruct", + name: "Llama-3.2-90B-Vision-Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-09-25", + last_updated: "2024-09-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 2.04, output: 2.04 }, + limit: { context: 128000, output: 8192 }, + }, + "text-embedding-ada-002": { + id: "text-embedding-ada-002", + name: "text-embedding-ada-002", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + release_date: "2022-12-15", + last_updated: "2022-12-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0 }, + limit: { context: 8192, output: 1536 }, + }, + "gpt-5.3-codex": { + id: "gpt-5.3-codex", + name: "GPT-5.3 Codex", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-02-24", + last_updated: "2026-02-24", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, output: 128000 }, + }, + "phi-3-small-8k-instruct": { + id: "phi-3-small-8k-instruct", + name: "Phi-3-small-instruct (8k)", + family: "phi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-04-23", + last_updated: "2024-04-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 8192, output: 2048 }, + }, + "meta-llama-3-70b-instruct": { + id: "meta-llama-3-70b-instruct", + name: "Meta-Llama-3-70B-Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-12", + release_date: "2024-04-18", + last_updated: "2024-04-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.68, output: 3.54 }, + limit: { context: 8192, output: 2048 }, + }, + "gpt-5-nano": { + id: "gpt-5-nano", + name: "GPT-5 Nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.4, cache_read: 0.01 }, + limit: { context: 272000, output: 128000 }, + }, + "gpt-5-mini": { + id: "gpt-5-mini", + name: "GPT-5 Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.03 }, + limit: { context: 272000, output: 128000 }, + }, + "phi-4-reasoning": { + id: "phi-4-reasoning", + name: "Phi-4-reasoning", + family: "phi", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.125, output: 0.5 }, + limit: { context: 32000, output: 4096 }, + }, + "phi-3-mini-128k-instruct": { + id: "phi-3-mini-128k-instruct", + name: "Phi-3-mini-instruct (128k)", + family: "phi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-04-23", + last_updated: "2024-04-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.13, output: 0.52 }, + limit: { context: 128000, output: 4096 }, + }, + "text-embedding-3-large": { + id: "text-embedding-3-large", + name: "text-embedding-3-large", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + release_date: "2024-01-25", + last_updated: "2024-01-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.13, output: 0 }, + limit: { context: 8191, output: 3072 }, + }, + "o1-mini": { + id: "o1-mini", + name: "o1-mini", + family: "o-mini", + attachment: false, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2023-09", + release_date: "2024-09-12", + last_updated: "2024-09-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.55 }, + limit: { context: 128000, output: 65536 }, + }, + "phi-3.5-moe-instruct": { + id: "phi-3.5-moe-instruct", + name: "Phi-3.5-MoE-instruct", + family: "phi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-08-20", + last_updated: "2024-08-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.16, output: 0.64 }, + limit: { context: 128000, output: 4096 }, + }, + "gpt-5-chat": { + id: "gpt-5-chat", + name: "GPT-5 Chat", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: false, + temperature: false, + knowledge: "2024-10-24", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.13 }, + limit: { context: 128000, output: 16384 }, + }, + "deepseek-v3-0324": { + id: "deepseek-v3-0324", + name: "DeepSeek-V3-0324", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-03-24", + last_updated: "2025-03-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.14, output: 4.56 }, + limit: { context: 131072, output: 131072 }, + }, + "llama-3.3-70b-instruct": { + id: "llama-3.3-70b-instruct", + name: "Llama-3.3-70B-Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.71, output: 0.71 }, + limit: { context: 128000, output: 32768 }, + }, + "kimi-k2.5": { + id: "kimi-k2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-06", + last_updated: "2026-02-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3 }, + limit: { context: 262144, output: 262144 }, + provider: { + npm: "@ai-sdk/openai-compatible", + api: "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/models", + shape: "completions", + }, + }, + "meta-llama-3.1-8b-instruct": { + id: "meta-llama-3.1-8b-instruct", + name: "Meta-Llama-3.1-8B-Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.61 }, + limit: { context: 128000, output: 32768 }, + }, + "ministral-3b": { + id: "ministral-3b", + name: "Ministral 3B", + family: "ministral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-03", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.04, output: 0.04 }, + limit: { context: 128000, output: 8192 }, + }, + "phi-3-medium-4k-instruct": { + id: "phi-3-medium-4k-instruct", + name: "Phi-3-medium-instruct (4k)", + family: "phi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-04-23", + last_updated: "2024-04-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.17, output: 0.68 }, + limit: { context: 4096, output: 1024 }, + }, + "llama-4-scout-17b-16e-instruct": { + id: "llama-4-scout-17b-16e-instruct", + name: "Llama 4 Scout 17B 16E Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.78 }, + limit: { context: 128000, output: 8192 }, + }, + "phi-3.5-mini-instruct": { + id: "phi-3.5-mini-instruct", + name: "Phi-3.5-mini-instruct", + family: "phi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-08-20", + last_updated: "2024-08-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.13, output: 0.52 }, + limit: { context: 128000, output: 4096 }, + }, + "phi-4-multimodal": { + id: "phi-4-multimodal", + name: "Phi-4-multimodal", + family: "phi", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text", "image", "audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0.08, output: 0.32, input_audio: 4 }, + limit: { context: 128000, output: 4096 }, + }, + "codex-mini": { + id: "codex-mini", + name: "Codex Mini", + family: "gpt-codex-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-04", + release_date: "2025-05-16", + last_updated: "2025-05-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.5, output: 6, cache_read: 0.375 }, + limit: { context: 200000, output: 100000 }, + }, + "gpt-5.2-chat": { + id: "gpt-5.2-chat", + name: "GPT-5.2 Chat", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 128000, output: 16384 }, + }, + "mistral-nemo": { + id: "mistral-nemo", + name: "Mistral Nemo", + family: "mistral-nemo", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.15 }, + limit: { context: 128000, output: 128000 }, + }, + "gpt-5.4-mini": { + id: "gpt-5.4-mini", + name: "GPT-5.4 Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.75, output: 4.5, cache_read: 0.075 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gpt-5.4-nano": { + id: "gpt-5.4-nano", + name: "GPT-5.4 Nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.25, cache_read: 0.02 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gpt-5.4-pro": { + id: "gpt-5.4-pro", + name: "GPT-5.4 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 180, context_over_200k: { input: 60, output: 270 } }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "gpt-5.4": { + id: "gpt-5.4", + name: "GPT-5.4", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 2.5, + output: 15, + cache_read: 0.25, + context_over_200k: { input: 5, output: 22.5, cache_read: 0.5 }, + }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "grok-4-fast-non-reasoning": { + id: "grok-4-fast-non-reasoning", + name: "Grok 4 Fast (Non-Reasoning)", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-09-19", + last_updated: "2025-09-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + "grok-3": { + id: "grok-3", + name: "Grok 3", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.75 }, + limit: { context: 131072, output: 8192 }, + }, + "gpt-4.1-mini": { + id: "gpt-4.1-mini", + name: "GPT-4.1 mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.6, cache_read: 0.1 }, + limit: { context: 1047576, output: 32768 }, + }, + "gpt-4.1": { + id: "gpt-4.1", + name: "GPT-4.1", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 1047576, output: 32768 }, + }, + "cohere-command-r-plus-08-2024": { + id: "cohere-command-r-plus-08-2024", + name: "Command R+", + family: "command-r", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-06-01", + release_date: "2024-08-30", + last_updated: "2024-08-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.5, output: 10 }, + limit: { context: 128000, output: 4000 }, + }, + "gpt-4o": { + id: "gpt-4o", + name: "GPT-4o", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-09", + release_date: "2024-05-13", + last_updated: "2024-08-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10, cache_read: 1.25 }, + limit: { context: 128000, output: 16384 }, + }, + "gpt-5-pro": { + id: "gpt-5-pro", + name: "GPT-5 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-10-06", + last_updated: "2025-10-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 120 }, + limit: { context: 400000, output: 272000 }, + }, + o3: { + id: "o3", + name: "o3", + family: "o", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05", + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 200000, output: 100000 }, + }, + "grok-3-mini": { + id: "grok-3-mini", + name: "Grok 3 Mini", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.5, reasoning: 0.5, cache_read: 0.075 }, + limit: { context: 131072, output: 8192 }, + }, + "gpt-4.1-nano": { + id: "gpt-4.1-nano", + name: "GPT-4.1 nano", + family: "gpt-nano", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.03 }, + limit: { context: 1047576, output: 32768 }, + }, + "grok-4": { + id: "grok-4", + name: "Grok 4", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, reasoning: 15, cache_read: 0.75 }, + limit: { context: 256000, output: 64000 }, + }, + "o3-mini": { + id: "o3-mini", + name: "o3-mini", + family: "o-mini", + attachment: false, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05", + release_date: "2024-12-20", + last_updated: "2025-01-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.55 }, + limit: { context: 200000, output: 100000 }, + }, + "grok-code-fast-1": { + id: "grok-code-fast-1", + name: "Grok Code Fast 1", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2025-08-28", + last_updated: "2025-08-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.5, cache_read: 0.02 }, + limit: { context: 256000, output: 10000 }, + }, + "o4-mini": { + id: "o4-mini", + name: "o4-mini", + family: "o-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05", + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.28 }, + limit: { context: 200000, output: 100000 }, + }, + "cohere-command-r-08-2024": { + id: "cohere-command-r-08-2024", + name: "Command R", + family: "command-r", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-06-01", + release_date: "2024-08-30", + last_updated: "2024-08-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 128000, output: 4000 }, + }, + "gpt-4o-mini": { + id: "gpt-4o-mini", + name: "GPT-4o mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-09", + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6, cache_read: 0.08 }, + limit: { context: 128000, output: 16384 }, + }, + "gpt-4-turbo": { + id: "gpt-4-turbo", + name: "GPT-4 Turbo", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2023-11-06", + last_updated: "2024-04-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 10, output: 30 }, + limit: { context: 128000, output: 4096 }, + }, + "gpt-5.5": { + id: "gpt-5.5", + name: "GPT-5.5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-12-01", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 30, cache_read: 0.5, context_over_200k: { input: 10, output: 45, cache_read: 1 } }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + }, + }, + "abliteration-ai": { + id: "abliteration-ai", + env: ["ABLIT_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.abliteration.ai/v1", + name: "abliteration.ai", + doc: "https://docs.abliteration.ai/models", + models: { + "abliterated-model": { + id: "abliterated-model", + name: "Abliterated Model", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2026-01-06", + last_updated: "2026-01-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 3, output: 3 }, + limit: { context: 150000, input: 150000, output: 8192 }, + }, + }, + }, + "wafer.ai": { + id: "wafer.ai", + env: ["WAFER_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://pass.wafer.ai/v1", + name: "Wafer", + doc: "https://docs.wafer.ai/wafer-pass", + models: { + "Qwen3.5-397B-A17B": { + id: "Qwen3.5-397B-A17B", + name: "Qwen3.5 397B A17B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-16", + last_updated: "2026-02-16", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 262144, output: 65536 }, + }, + "GLM-5.1": { + id: "GLM-5.1", + name: "GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-04-07", + last_updated: "2026-04-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 202752, output: 131072 }, + }, + "MiniMax-M2.7": { + id: "MiniMax-M2.7", + name: "MiniMax-M2.7", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "DeepSeek-V4-Pro": { + id: "DeepSeek-V4-Pro", + name: "DeepSeek V4 Pro", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 1000000, output: 384000 }, + }, + }, + }, + cohere: { + id: "cohere", + env: ["COHERE_API_KEY"], + npm: "@ai-sdk/cohere", + name: "Cohere", + doc: "https://docs.cohere.com/docs/models", + models: { + "command-a-reasoning-08-2025": { + id: "command-a-reasoning-08-2025", + name: "Command A Reasoning", + family: "command-a", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-06-01", + release_date: "2025-08-21", + last_updated: "2025-08-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.5, output: 10 }, + limit: { context: 256000, output: 32000 }, + }, + "command-r7b-12-2024": { + id: "command-r7b-12-2024", + name: "Command R7B", + family: "command-r", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-06-01", + release_date: "2024-02-27", + last_updated: "2024-02-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.0375, output: 0.15 }, + limit: { context: 128000, output: 4000 }, + }, + "c4ai-aya-vision-8b": { + id: "c4ai-aya-vision-8b", + name: "Aya Vision 8B", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-03-04", + last_updated: "2025-05-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + limit: { context: 16000, output: 4000 }, + }, + "command-r-plus-08-2024": { + id: "command-r-plus-08-2024", + name: "Command R+", + family: "command-r", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-06-01", + release_date: "2024-08-30", + last_updated: "2024-08-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.5, output: 10 }, + limit: { context: 128000, output: 4000 }, + }, + "c4ai-aya-expanse-8b": { + id: "c4ai-aya-expanse-8b", + name: "Aya Expanse 8B", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-10-24", + last_updated: "2024-10-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 8000, output: 4000 }, + }, + "command-r7b-arabic-02-2025": { + id: "command-r7b-arabic-02-2025", + name: "Command R7B Arabic", + family: "command-r", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-06-01", + release_date: "2025-02-27", + last_updated: "2025-02-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.0375, output: 0.15 }, + limit: { context: 128000, output: 4000 }, + }, + "command-a-vision-07-2025": { + id: "command-a-vision-07-2025", + name: "Command A Vision", + family: "command-a", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-06-01", + release_date: "2025-07-31", + last_updated: "2025-07-31", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 2.5, output: 10 }, + limit: { context: 128000, output: 8000 }, + }, + "c4ai-aya-vision-32b": { + id: "c4ai-aya-vision-32b", + name: "Aya Vision 32B", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-03-04", + last_updated: "2025-05-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + limit: { context: 16000, output: 4000 }, + }, + "command-a-translate-08-2025": { + id: "command-a-translate-08-2025", + name: "Command A Translate", + family: "command-a", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-06-01", + release_date: "2025-08-28", + last_updated: "2025-08-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.5, output: 10 }, + limit: { context: 8000, output: 8000 }, + }, + "command-r-08-2024": { + id: "command-r-08-2024", + name: "Command R", + family: "command-r", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-06-01", + release_date: "2024-08-30", + last_updated: "2024-08-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 128000, output: 4000 }, + }, + "c4ai-aya-expanse-32b": { + id: "c4ai-aya-expanse-32b", + name: "Aya Expanse 32B", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-10-24", + last_updated: "2024-10-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 128000, output: 4000 }, + }, + "command-a-03-2025": { + id: "command-a-03-2025", + name: "Command A", + family: "command-a", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-06-01", + release_date: "2025-03-13", + last_updated: "2025-03-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.5, output: 10 }, + limit: { context: 256000, output: 8000 }, + }, + }, + }, + "cloudferro-sherlock": { + id: "cloudferro-sherlock", + env: ["CLOUDFERRO_SHERLOCK_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api-sherlock.cloudferro.com/openai/v1/", + name: "CloudFerro Sherlock", + doc: "https://docs.sherlock.cloudferro.com/", + models: { + "meta-llama/Llama-3.3-70B-Instruct": { + id: "meta-llama/Llama-3.3-70B-Instruct", + name: "Llama 3.3 70B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10-09", + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.92, output: 2.92 }, + limit: { context: 70000, output: 70000 }, + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "OpenAI GPT OSS 120B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-28", + last_updated: "2025-08-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.92, output: 2.92 }, + limit: { context: 131000, output: 131000 }, + }, + "speakleash/Bielik-11B-v3.0-Instruct": { + id: "speakleash/Bielik-11B-v3.0-Instruct", + name: "Bielik 11B v3.0 Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-03", + release_date: "2025-03-13", + last_updated: "2025-03-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.67, output: 0.67 }, + limit: { context: 32000, output: 32000 }, + }, + "speakleash/Bielik-11B-v2.6-Instruct": { + id: "speakleash/Bielik-11B-v2.6-Instruct", + name: "Bielik 11B v2.6 Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-03", + release_date: "2025-03-13", + last_updated: "2025-03-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.67, output: 0.67 }, + limit: { context: 32000, output: 32000 }, + }, + "MiniMaxAI/MiniMax-M2.5": { + id: "MiniMaxAI/MiniMax-M2.5", + name: "MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2026-01", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 196000, input: 180000, output: 16000 }, + }, + }, + }, + "kuae-cloud-coding-plan": { + id: "kuae-cloud-coding-plan", + env: ["KUAE_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://coding-plan-endpoint.kuaecloud.net/v1", + name: "KUAE Cloud Coding Plan", + doc: "https://docs.mthreads.com/kuaecloud/kuaecloud-doc-online/coding_plan/", + models: { + "GLM-4.7": { + id: "GLM-4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + }, + }, + xai: { + id: "xai", + env: ["XAI_API_KEY"], + npm: "@ai-sdk/xai", + name: "xAI", + doc: "https://docs.x.ai/docs/models", + models: { + "grok-2-1212": { + id: "grok-2-1212", + name: "Grok 2 (1212)", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2024-12-12", + last_updated: "2024-12-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 10, cache_read: 2 }, + limit: { context: 131072, output: 8192 }, + }, + "grok-vision-beta": { + id: "grok-vision-beta", + name: "Grok Vision Beta", + family: "grok-vision", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2024-11-01", + last_updated: "2024-11-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 15, cache_read: 5 }, + limit: { context: 8192, output: 4096 }, + }, + "grok-4.3": { + id: "grok-4.3", + name: "Grok 4.3", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-05-01", + last_updated: "2026-05-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { + input: 1.25, + output: 2.5, + cache_read: 0.2, + context_over_200k: { input: 2.5, output: 5, cache_read: 0.4 }, + }, + limit: { context: 1000000, output: 30000 }, + }, + "grok-3-mini-fast": { + id: "grok-3-mini-fast", + name: "Grok 3 Mini Fast", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 4, reasoning: 4, cache_read: 0.15 }, + limit: { context: 131072, output: 8192 }, + }, + "grok-3-mini-latest": { + id: "grok-3-mini-latest", + name: "Grok 3 Mini Latest", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.5, reasoning: 0.5, cache_read: 0.075 }, + limit: { context: 131072, output: 8192 }, + }, + "grok-3-fast": { + id: "grok-3-fast", + name: "Grok 3 Fast", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 1.25 }, + limit: { context: 131072, output: 8192 }, + }, + "grok-2-vision-latest": { + id: "grok-2-vision-latest", + name: "Grok 2 Vision Latest", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2024-08-20", + last_updated: "2024-12-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 10, cache_read: 2 }, + limit: { context: 8192, output: 4096 }, + }, + "grok-4.20-0309-reasoning": { + id: "grok-4.20-0309-reasoning", + name: "Grok 4.20 (Reasoning)", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-09", + last_updated: "2026-03-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6, cache_read: 0.2, context_over_200k: { input: 4, output: 12, cache_read: 0.4 } }, + limit: { context: 2000000, output: 30000 }, + }, + "grok-4-1-fast-non-reasoning": { + id: "grok-4-1-fast-non-reasoning", + name: "Grok 4.1 Fast (Non-Reasoning)", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-11-19", + last_updated: "2025-11-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + "grok-3-mini-fast-latest": { + id: "grok-3-mini-fast-latest", + name: "Grok 3 Mini Fast Latest", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 4, reasoning: 4, cache_read: 0.15 }, + limit: { context: 131072, output: 8192 }, + }, + "grok-4-fast": { + id: "grok-4-fast", + name: "Grok 4 Fast", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-09-19", + last_updated: "2025-09-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + "grok-3-latest": { + id: "grok-3-latest", + name: "Grok 3 Latest", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.75 }, + limit: { context: 131072, output: 8192 }, + }, + "grok-2": { + id: "grok-2", + name: "Grok 2", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2024-08-20", + last_updated: "2024-08-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 10, cache_read: 2 }, + limit: { context: 131072, output: 8192 }, + }, + "grok-code-fast-1": { + id: "grok-code-fast-1", + name: "Grok Code Fast 1", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2025-08-28", + last_updated: "2025-08-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.5, cache_read: 0.02 }, + limit: { context: 256000, output: 10000 }, + }, + "grok-4.20-0309-non-reasoning": { + id: "grok-4.20-0309-non-reasoning", + name: "Grok 4.20 (Non-Reasoning)", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-03-09", + last_updated: "2026-03-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6, cache_read: 0.2, context_over_200k: { input: 4, output: 12, cache_read: 0.4 } }, + limit: { context: 2000000, output: 30000 }, + }, + "grok-3-fast-latest": { + id: "grok-3-fast-latest", + name: "Grok 3 Fast Latest", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 1.25 }, + limit: { context: 131072, output: 8192 }, + }, + "grok-4.20-multi-agent-0309": { + id: "grok-4.20-multi-agent-0309", + name: "Grok 4.20 Multi-Agent", + family: "grok", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2026-03-09", + last_updated: "2026-03-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6, cache_read: 0.2, context_over_200k: { input: 4, output: 12, cache_read: 0.4 } }, + limit: { context: 2000000, output: 30000 }, + }, + "grok-4": { + id: "grok-4", + name: "Grok 4", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, reasoning: 15, cache_read: 0.75 }, + limit: { context: 256000, output: 64000 }, + }, + "grok-2-latest": { + id: "grok-2-latest", + name: "Grok 2 Latest", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2024-08-20", + last_updated: "2024-12-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 10, cache_read: 2 }, + limit: { context: 131072, output: 8192 }, + }, + "grok-beta": { + id: "grok-beta", + name: "Grok Beta", + family: "grok-beta", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2024-11-01", + last_updated: "2024-11-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 15, cache_read: 5 }, + limit: { context: 131072, output: 4096 }, + }, + "grok-2-vision": { + id: "grok-2-vision", + name: "Grok 2 Vision", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2024-08-20", + last_updated: "2024-08-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 10, cache_read: 2 }, + limit: { context: 8192, output: 4096 }, + }, + "grok-4-1-fast": { + id: "grok-4-1-fast", + name: "Grok 4.1 Fast", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-11-19", + last_updated: "2025-11-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + "grok-3-mini": { + id: "grok-3-mini", + name: "Grok 3 Mini", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.5, reasoning: 0.5, cache_read: 0.075 }, + limit: { context: 131072, output: 8192 }, + }, + "grok-2-vision-1212": { + id: "grok-2-vision-1212", + name: "Grok 2 Vision (1212)", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2024-08-20", + last_updated: "2024-12-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 10, cache_read: 2 }, + limit: { context: 8192, output: 4096 }, + }, + "grok-3": { + id: "grok-3", + name: "Grok 3", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.75 }, + limit: { context: 131072, output: 8192 }, + }, + "grok-4-fast-non-reasoning": { + id: "grok-4-fast-non-reasoning", + name: "Grok 4 Fast (Non-Reasoning)", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-09-19", + last_updated: "2025-09-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + }, + }, + meganova: { + id: "meganova", + env: ["MEGANOVA_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.meganova.ai/v1", + name: "Meganova", + doc: "https://docs.meganova.ai", + models: { + "Qwen/Qwen3-235B-A22B-Instruct-2507": { + id: "Qwen/Qwen3-235B-A22B-Instruct-2507", + name: "Qwen3 235B A22B Instruct 2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.09, output: 0.6 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3.5-Plus": { + id: "Qwen/Qwen3.5-Plus", + name: "Qwen3.5 Plus", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02", + last_updated: "2026-02", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2.4, reasoning: 2.4 }, + limit: { context: 1000000, output: 65536 }, + }, + "Qwen/Qwen2.5-VL-32B-Instruct": { + id: "Qwen/Qwen2.5-VL-32B-Instruct", + name: "Qwen2.5 VL 32B Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-03-24", + last_updated: "2025-03-24", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.6 }, + limit: { context: 16384, output: 16384 }, + }, + "zai-org/GLM-4.7": { + id: "zai-org/GLM-4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.8 }, + limit: { context: 202752, output: 131072 }, + }, + "zai-org/GLM-5": { + id: "zai-org/GLM-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.8, output: 2.56 }, + limit: { context: 202752, output: 131072 }, + }, + "zai-org/GLM-4.6": { + id: "zai-org/GLM-4.6", + name: "GLM-4.6", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.45, output: 1.9 }, + limit: { context: 202752, output: 131072 }, + }, + "mistralai/Mistral-Small-3.2-24B-Instruct-2506": { + id: "mistralai/Mistral-Small-3.2-24B-Instruct-2506", + name: "Mistral Small 3.2 24B Instruct", + family: "mistral-small", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-06-20", + last_updated: "2025-06-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 32768, output: 8192 }, + }, + "mistralai/Mistral-Nemo-Instruct-2407": { + id: "mistralai/Mistral-Nemo-Instruct-2407", + name: "Mistral Nemo Instruct 2407", + family: "mistral", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0.04 }, + limit: { context: 131072, output: 65536 }, + }, + "XiaomiMiMo/MiMo-V2-Flash": { + id: "XiaomiMiMo/MiMo-V2-Flash", + name: "MiMo V2 Flash", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-12-01", + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 262144, output: 32000 }, + }, + "meta-llama/Llama-3.3-70B-Instruct": { + id: "meta-llama/Llama-3.3-70B-Instruct", + name: "Llama 3.3 70B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 131072, output: 16384 }, + }, + "deepseek-ai/DeepSeek-V3.1": { + id: "deepseek-ai/DeepSeek-V3.1", + name: "DeepSeek V3.1", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-25", + last_updated: "2025-08-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 1 }, + limit: { context: 164000, output: 164000 }, + }, + "deepseek-ai/DeepSeek-V3-0324": { + id: "deepseek-ai/DeepSeek-V3-0324", + name: "DeepSeek V3 0324", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-03-24", + last_updated: "2025-03-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 0.88 }, + limit: { context: 163840, output: 163840 }, + }, + "deepseek-ai/DeepSeek-V3.2-Exp": { + id: "deepseek-ai/DeepSeek-V3.2-Exp", + name: "DeepSeek V3.2 Exp", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-10", + last_updated: "2025-10-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 0.4 }, + limit: { context: 164000, output: 164000 }, + }, + "deepseek-ai/DeepSeek-R1-0528": { + id: "deepseek-ai/DeepSeek-R1-0528", + name: "DeepSeek R1 0528", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: false, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-07", + release_date: "2025-05-28", + last_updated: "2025-05-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 2.15 }, + limit: { context: 163840, output: 64000 }, + }, + "deepseek-ai/DeepSeek-V3.2": { + id: "deepseek-ai/DeepSeek-V3.2", + name: "DeepSeek V3.2", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-03", + last_updated: "2025-12-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.26, output: 0.38 }, + limit: { context: 164000, output: 164000 }, + }, + "moonshotai/Kimi-K2-Thinking": { + id: "moonshotai/Kimi-K2-Thinking", + name: "Kimi K2 Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-08", + release_date: "2025-11-06", + last_updated: "2025-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.6 }, + limit: { context: 262144, output: 262144 }, + }, + "moonshotai/Kimi-K2.5": { + id: "moonshotai/Kimi-K2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2026-01", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.45, output: 2.8 }, + limit: { context: 262144, output: 262144 }, + }, + "MiniMaxAI/MiniMax-M2.5": { + id: "MiniMaxAI/MiniMax-M2.5", + name: "MiniMax M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMaxAI/MiniMax-M2.1": { + id: "MiniMaxAI/MiniMax-M2.1", + name: "MiniMax M2.1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.28, output: 1.2 }, + limit: { context: 196608, output: 131072 }, + }, + }, + }, + "google-vertex-anthropic": { + id: "google-vertex-anthropic", + env: ["GOOGLE_VERTEX_PROJECT", "GOOGLE_VERTEX_LOCATION", "GOOGLE_APPLICATION_CREDENTIALS"], + npm: "@ai-sdk/google-vertex/anthropic", + name: "Vertex (Anthropic)", + doc: "https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/claude", + models: { + "claude-haiku-4-5@20251001": { + id: "claude-haiku-4-5@20251001", + name: "Claude Haiku 4.5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + }, + "claude-sonnet-4-6@default": { + id: "claude-sonnet-4-6@default", + name: "Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 3, + output: 15, + cache_read: 0.3, + cache_write: 3.75, + context_over_200k: { input: 6, output: 22.5, cache_read: 0.6, cache_write: 7.5 }, + }, + limit: { context: 200000, output: 64000 }, + }, + "claude-3-5-haiku@20241022": { + id: "claude-3-5-haiku@20241022", + name: "Claude Haiku 3.5", + family: "claude-haiku", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07-31", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 4, cache_read: 0.08, cache_write: 1 }, + limit: { context: 200000, output: 8192 }, + }, + "claude-3-5-sonnet@20241022": { + id: "claude-3-5-sonnet@20241022", + name: "Claude Sonnet 3.5 v2", + family: "claude-sonnet", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04-30", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 8192 }, + }, + "claude-opus-4-1@20250805": { + id: "claude-opus-4-1@20250805", + name: "Claude Opus 4.1", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "claude-sonnet-4@20250514": { + id: "claude-sonnet-4@20250514", + name: "Claude Sonnet 4", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "claude-3-7-sonnet@20250219": { + id: "claude-3-7-sonnet@20250219", + name: "Claude Sonnet 3.7", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10-31", + release_date: "2025-02-19", + last_updated: "2025-02-19", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "claude-opus-4@20250514": { + id: "claude-opus-4@20250514", + name: "Claude Opus 4", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "claude-opus-4-5@20251101": { + id: "claude-opus-4-5@20251101", + name: "Claude Opus 4.5", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-01", + last_updated: "2025-11-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 64000 }, + }, + "claude-sonnet-4-5@20250929": { + id: "claude-sonnet-4-5@20250929", + name: "Claude Sonnet 4.5", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "claude-opus-4-6@default": { + id: "claude-opus-4-6@default", + name: "Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 5, + output: 25, + cache_read: 0.5, + cache_write: 6.25, + context_over_200k: { input: 10, output: 37.5, cache_read: 1, cache_write: 12.5 }, + }, + limit: { context: 1000000, output: 128000 }, + }, + "claude-opus-4-7@default": { + id: "claude-opus-4-7@default", + name: "Claude Opus 4.7", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 5, + output: 25, + cache_read: 0.5, + cache_write: 6.25, + context_over_200k: { input: 10, output: 37.5, cache_read: 1, cache_write: 12.5 }, + }, + limit: { context: 1000000, output: 128000 }, + }, + }, + }, + evroc: { + id: "evroc", + env: ["EVROC_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://models.think.evroc.com/v1", + name: "evroc", + doc: "https://docs.evroc.com/products/think/overview.html", + models: { + "Qwen/Qwen3-VL-30B-A3B-Instruct": { + id: "Qwen/Qwen3-VL-30B-A3B-Instruct", + name: "Qwen3 VL 30B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + release_date: "2025-07-30", + last_updated: "2025-07-30", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.24, output: 0.94 }, + limit: { context: 100000, output: 100000 }, + }, + "Qwen/Qwen3-Embedding-8B": { + id: "Qwen/Qwen3-Embedding-8B", + name: "Qwen3 Embedding 8B", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + release_date: "2025-07-30", + last_updated: "2025-07-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.12, output: 0.12 }, + limit: { context: 40960, output: 40960 }, + }, + "Qwen/Qwen3-30B-A3B-Instruct-2507-FP8": { + id: "Qwen/Qwen3-30B-A3B-Instruct-2507-FP8", + name: "Qwen3 30B 2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + release_date: "2025-07-30", + last_updated: "2025-07-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.35, output: 1.42 }, + limit: { context: 64000, output: 64000 }, + }, + "mistralai/devstral-small-2-24b-instruct-2512": { + id: "mistralai/devstral-small-2-24b-instruct-2512", + name: "Devstral Small 2 24B Instruct 2512", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: true, + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.12, output: 0.47 }, + limit: { context: 32768, output: 32768 }, + }, + "mistralai/Voxtral-Small-24B-2507": { + id: "mistralai/Voxtral-Small-24B-2507", + name: "Voxtral Small 24B", + family: "voxtral", + attachment: false, + reasoning: false, + tool_call: false, + release_date: "2025-03-01", + last_updated: "2025-03-01", + modalities: { input: ["audio", "text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.00236, output: 0.00236, output_audio: 2.36 }, + limit: { context: 32000, output: 32000 }, + }, + "mistralai/Magistral-Small-2509": { + id: "mistralai/Magistral-Small-2509", + name: "Magistral Small 1.2 24B", + family: "magistral-small", + attachment: false, + reasoning: false, + tool_call: false, + release_date: "2025-06-01", + last_updated: "2025-06-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.59, output: 2.36 }, + limit: { context: 131072, output: 131072 }, + }, + "microsoft/Phi-4-multimodal-instruct": { + id: "microsoft/Phi-4-multimodal-instruct", + name: "Phi-4 15B", + family: "phi", + attachment: false, + reasoning: false, + tool_call: false, + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.24, output: 0.47 }, + limit: { context: 32000, output: 32000 }, + }, + "KBLab/kb-whisper-large": { + id: "KBLab/kb-whisper-large", + name: "KB Whisper", + family: "whisper", + attachment: false, + reasoning: false, + tool_call: false, + release_date: "2024-10-01", + last_updated: "2024-10-01", + modalities: { input: ["audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0.00236, output: 0.00236, output_audio: 2.36 }, + limit: { context: 448, output: 448 }, + }, + "nvidia/Llama-3.3-70B-Instruct-FP8": { + id: "nvidia/Llama-3.3-70B-Instruct-FP8", + name: "Llama 3.3 70B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.18, output: 1.18 }, + limit: { context: 131072, output: 32768 }, + }, + "openai/whisper-large-v3": { + id: "openai/whisper-large-v3", + name: "Whisper 3 Large", + family: "whisper", + attachment: false, + reasoning: false, + tool_call: false, + release_date: "2024-10-01", + last_updated: "2024-10-01", + modalities: { input: ["audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0.00236, output: 0.00236, output_audio: 2.36 }, + limit: { context: 448, output: 4096 }, + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "GPT OSS 120B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.24, output: 0.94 }, + limit: { context: 65536, output: 65536 }, + }, + "moonshotai/Kimi-K2.5": { + id: "moonshotai/Kimi-K2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 1.47, output: 5.9 }, + limit: { context: 262144, output: 262144 }, + }, + "intfloat/multilingual-e5-large-instruct": { + id: "intfloat/multilingual-e5-large-instruct", + name: "E5 Multi-Lingual Large Embeddings 0.6B", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + release_date: "2024-06-01", + last_updated: "2024-06-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.12, output: 0.12 }, + limit: { context: 512, output: 512 }, + }, + }, + }, + synthetic: { + id: "synthetic", + env: ["SYNTHETIC_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.synthetic.new/openai/v1", + name: "Synthetic", + doc: "https://synthetic.new/pricing", + models: { + "hf:meta-llama/Llama-3.1-405B-Instruct": { + id: "hf:meta-llama/Llama-3.1-405B-Instruct", + name: "Llama-3.1-405B-Instruct", + family: "llama", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 3, output: 3 }, + limit: { context: 128000, output: 32768 }, + }, + "hf:meta-llama/Llama-4-Scout-17B-16E-Instruct": { + id: "hf:meta-llama/Llama-4-Scout-17B-16E-Instruct", + name: "Llama-4-Scout-17B-16E-Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 328000, output: 4096 }, + }, + "hf:meta-llama/Llama-3.3-70B-Instruct": { + id: "hf:meta-llama/Llama-3.3-70B-Instruct", + name: "Llama-3.3-70B-Instruct", + family: "llama", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.9, output: 0.9 }, + limit: { context: 128000, output: 32768 }, + }, + "hf:meta-llama/Llama-3.1-8B-Instruct": { + id: "hf:meta-llama/Llama-3.1-8B-Instruct", + name: "Llama-3.1-8B-Instruct", + family: "llama", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 128000, output: 32768 }, + }, + "hf:meta-llama/Llama-3.1-70B-Instruct": { + id: "hf:meta-llama/Llama-3.1-70B-Instruct", + name: "Llama-3.1-70B-Instruct", + family: "llama", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.9, output: 0.9 }, + limit: { context: 128000, output: 32768 }, + }, + "hf:meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8": { + id: "hf:meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8", + name: "Llama-4-Maverick-17B-128E-Instruct-FP8", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.22, output: 0.88 }, + limit: { context: 524000, output: 4096 }, + }, + "hf:MiniMaxAI/MiniMax-M2": { + id: "hf:MiniMaxAI/MiniMax-M2", + name: "MiniMax-M2", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-10-27", + last_updated: "2025-10-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 2.19 }, + limit: { context: 196608, output: 131000 }, + }, + "hf:MiniMaxAI/MiniMax-M2.5": { + id: "hf:MiniMaxAI/MiniMax-M2.5", + name: "MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-02-07", + last_updated: "2026-02-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3, cache_read: 0.6 }, + limit: { context: 191488, output: 65536 }, + }, + "hf:MiniMaxAI/MiniMax-M2.1": { + id: "hf:MiniMaxAI/MiniMax-M2.1", + name: "MiniMax-M2.1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 2.19 }, + limit: { context: 204800, output: 131072 }, + }, + "hf:Qwen/Qwen3.5-397B-A17B": { + id: "hf:Qwen/Qwen3.5-397B-A17B", + name: "Qwen3.5-97B-A17B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3, cache_read: 0.6 }, + limit: { context: 262144, output: 65536 }, + status: "beta", + }, + "hf:Qwen/Qwen2.5-Coder-32B-Instruct": { + id: "hf:Qwen/Qwen2.5-Coder-32B-Instruct", + name: "Qwen2.5-Coder-32B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2024-11-11", + last_updated: "2024-11-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.8, output: 0.8 }, + limit: { context: 32768, output: 32768 }, + }, + "hf:Qwen/Qwen3-235B-A22B-Instruct-2507": { + id: "hf:Qwen/Qwen3-235B-A22B-Instruct-2507", + name: "Qwen 3 235B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04-28", + last_updated: "2025-07-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.6 }, + limit: { context: 256000, output: 32000 }, + }, + "hf:Qwen/Qwen3-235B-A22B-Thinking-2507": { + id: "hf:Qwen/Qwen3-235B-A22B-Thinking-2507", + name: "Qwen3 235B A22B Thinking 2507", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-25", + last_updated: "2025-07-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.65, output: 3 }, + limit: { context: 256000, output: 32000 }, + }, + "hf:Qwen/Qwen3-Coder-480B-A35B-Instruct": { + id: "hf:Qwen/Qwen3-Coder-480B-A35B-Instruct", + name: "Qwen 3 Coder 480B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2, output: 2 }, + limit: { context: 256000, output: 32000 }, + }, + "hf:deepseek-ai/DeepSeek-V3.1": { + id: "hf:deepseek-ai/DeepSeek-V3.1", + name: "DeepSeek V3.1", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-21", + last_updated: "2025-08-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.56, output: 1.68 }, + limit: { context: 128000, output: 128000 }, + }, + "hf:deepseek-ai/DeepSeek-V3-0324": { + id: "hf:deepseek-ai/DeepSeek-V3-0324", + name: "DeepSeek V3 (0324)", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-08-01", + last_updated: "2025-08-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.2, output: 1.2 }, + limit: { context: 128000, output: 128000 }, + }, + "hf:deepseek-ai/DeepSeek-V3": { + id: "hf:deepseek-ai/DeepSeek-V3", + name: "DeepSeek V3", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-01-20", + last_updated: "2025-05-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.25, output: 1.25 }, + limit: { context: 128000, output: 128000 }, + }, + "hf:deepseek-ai/DeepSeek-R1": { + id: "hf:deepseek-ai/DeepSeek-R1", + name: "DeepSeek R1", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 2.19 }, + limit: { context: 128000, output: 128000 }, + }, + "hf:deepseek-ai/DeepSeek-R1-0528": { + id: "hf:deepseek-ai/DeepSeek-R1-0528", + name: "DeepSeek R1 (0528)", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-01", + last_updated: "2025-08-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 8 }, + limit: { context: 128000, output: 128000 }, + }, + "hf:deepseek-ai/DeepSeek-V3.2": { + id: "hf:deepseek-ai/DeepSeek-V3.2", + name: "DeepSeek V3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 0.4, cache_read: 0.27, cache_write: 0 }, + limit: { context: 162816, input: 162816, output: 8000 }, + }, + "hf:deepseek-ai/DeepSeek-V3.1-Terminus": { + id: "hf:deepseek-ai/DeepSeek-V3.1-Terminus", + name: "DeepSeek V3.1 Terminus", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-09-22", + last_updated: "2025-09-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.2, output: 1.2 }, + limit: { context: 128000, output: 128000 }, + }, + "hf:openai/gpt-oss-120b": { + id: "hf:openai/gpt-oss-120b", + name: "GPT OSS 120B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 128000, output: 32768 }, + }, + "hf:moonshotai/Kimi-K2-Thinking": { + id: "hf:moonshotai/Kimi-K2-Thinking", + name: "Kimi K2 Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-11", + release_date: "2025-11-07", + last_updated: "2025-11-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 2.19 }, + limit: { context: 262144, output: 262144 }, + }, + "hf:moonshotai/Kimi-K2-Instruct-0905": { + id: "hf:moonshotai/Kimi-K2-Instruct-0905", + name: "Kimi K2 0905", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.2, output: 1.2 }, + limit: { context: 262144, output: 32768 }, + }, + "hf:moonshotai/Kimi-K2.5": { + id: "hf:moonshotai/Kimi-K2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-01", + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 2.19 }, + limit: { context: 262144, output: 65536 }, + }, + "hf:nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4": { + id: "hf:nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4", + name: "Nemotron 3 Super 120B", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2026-03-11", + last_updated: "2026-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1, cache_read: 0.3 }, + limit: { context: 262144, output: 65536 }, + }, + "hf:nvidia/Kimi-K2.5-NVFP4": { + id: "hf:nvidia/Kimi-K2.5-NVFP4", + name: "Kimi K2.5 (NVFP4)", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-01", + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 2.19 }, + limit: { context: 262144, output: 65536 }, + }, + "hf:zai-org/GLM-4.7-Flash": { + id: "hf:zai-org/GLM-4.7-Flash", + name: "GLM-4.7-Flash", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-01-18", + last_updated: "2026-01-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.06, output: 0.4, cache_read: 0.06 }, + limit: { context: 196608, output: 65536 }, + }, + "hf:zai-org/GLM-4.7": { + id: "hf:zai-org/GLM-4.7", + name: "GLM 4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 2.19 }, + limit: { context: 200000, output: 64000 }, + }, + "hf:zai-org/GLM-5.1": { + id: "hf:zai-org/GLM-5.1", + name: "GLM 5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-03-27", + last_updated: "2026-04-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3, cache_read: 1 }, + limit: { context: 196608, output: 65536 }, + }, + "hf:zai-org/GLM-5": { + id: "hf:zai-org/GLM-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-04-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3, cache_read: 1 }, + limit: { context: 196608, output: 65536 }, + }, + "hf:zai-org/GLM-4.6": { + id: "hf:zai-org/GLM-4.6", + name: "GLM 4.6", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 2.19 }, + limit: { context: 200000, output: 64000 }, + }, + "hf:moonshotai/Kimi-K2.6": { + id: "hf:moonshotai/Kimi-K2.6", + name: "Kimi K2.6", + family: "kimi-k2.6", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4, cache_read: 0.95 }, + limit: { context: 262144, output: 65536 }, + }, + }, + }, + nvidia: { + id: "nvidia", + env: ["NVIDIA_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://integrate.api.nvidia.com/v1", + name: "Nvidia", + doc: "https://docs.api.nvidia.com/nim/", + models: { + "black-forest-labs/flux.1-dev": { + id: "black-forest-labs/flux.1-dev", + name: "FLUX.1-dev", + family: "flux", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-08", + release_date: "2024-08-01", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 4096, output: 0 }, + }, + "stepfun-ai/step-3.5-flash": { + id: "stepfun-ai/step-3.5-flash", + name: "Step 3.5 Flash", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-02", + last_updated: "2026-02-02", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 256000, output: 16384 }, + }, + "mistralai/codestral-22b-instruct-v0.1": { + id: "mistralai/codestral-22b-instruct-v0.1", + name: "Codestral 22b Instruct V0.1", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-05-29", + last_updated: "2024-05-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "mistralai/mistral-large-3-675b-instruct-2512": { + id: "mistralai/mistral-large-3-675b-instruct-2512", + name: "Mistral Large 3 675B Instruct 2512", + family: "mistral-large", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-12-02", + last_updated: "2025-12-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 262144 }, + }, + "mistralai/devstral-2-123b-instruct-2512": { + id: "mistralai/devstral-2-123b-instruct-2512", + name: "Devstral-2-123B-Instruct-2512", + family: "devstral", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-12", + release_date: "2025-12-08", + last_updated: "2025-12-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 262144 }, + }, + "mistralai/ministral-14b-instruct-2512": { + id: "mistralai/ministral-14b-instruct-2512", + name: "Ministral 3 14B Instruct 2512", + family: "ministral", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-12", + release_date: "2025-12-01", + last_updated: "2025-12-08", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 262144 }, + }, + "mistralai/mistral-small-3.1-24b-instruct-2503": { + id: "mistralai/mistral-small-3.1-24b-instruct-2503", + name: "Mistral Small 3.1 24b Instruct 2503", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-03-11", + last_updated: "2025-03-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "mistralai/mamba-codestral-7b-v0.1": { + id: "mistralai/mamba-codestral-7b-v0.1", + name: "Mamba Codestral 7b V0.1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2024-07-16", + last_updated: "2024-07-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "mistralai/mistral-large-2-instruct": { + id: "mistralai/mistral-large-2-instruct", + name: "Mistral Large 2 Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-07-24", + last_updated: "2024-07-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "microsoft/phi-3-medium-4k-instruct": { + id: "microsoft/phi-3-medium-4k-instruct", + name: "Phi 3 Medium 4k Instruct", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-05-07", + last_updated: "2024-05-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 4000, output: 4096 }, + }, + "microsoft/phi-3.5-moe-instruct": { + id: "microsoft/phi-3.5-moe-instruct", + name: "Phi 3.5 Moe Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-08-17", + last_updated: "2024-08-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "microsoft/phi-4-mini-instruct": { + id: "microsoft/phi-4-mini-instruct", + name: "Phi-4-Mini", + family: "phi", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2024-12-01", + last_updated: "2025-09-05", + modalities: { input: ["text", "image", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 8192 }, + }, + "microsoft/phi-3-small-8k-instruct": { + id: "microsoft/phi-3-small-8k-instruct", + name: "Phi 3 Small 8k Instruct", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-05-07", + last_updated: "2024-05-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 8000, output: 4096 }, + }, + "microsoft/phi-3-vision-128k-instruct": { + id: "microsoft/phi-3-vision-128k-instruct", + name: "Phi 3 Vision 128k Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-05-19", + last_updated: "2024-05-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "microsoft/phi-3.5-vision-instruct": { + id: "microsoft/phi-3.5-vision-instruct", + name: "Phi 3.5 Vision Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-08-16", + last_updated: "2024-08-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "microsoft/phi-3-small-128k-instruct": { + id: "microsoft/phi-3-small-128k-instruct", + name: "Phi 3 Small 128k Instruct", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-05-07", + last_updated: "2024-05-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "microsoft/phi-3-medium-128k-instruct": { + id: "microsoft/phi-3-medium-128k-instruct", + name: "Phi 3 Medium 128k Instruct", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-05-07", + last_updated: "2024-05-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning": { + id: "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning", + name: "Nemotron 3 Nano Omni", + family: "nemotron", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-28", + last_updated: "2026-04-28", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 256000, output: 65536 }, + }, + "nvidia/llama-3.3-nemotron-super-49b-v1": { + id: "nvidia/llama-3.3-nemotron-super-49b-v1", + name: "Llama 3.3 Nemotron Super 49b V1", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-03-16", + last_updated: "2025-03-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "nvidia/nemotron-4-340b-instruct": { + id: "nvidia/nemotron-4-340b-instruct", + name: "Nemotron 4 340b Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-06-13", + last_updated: "2024-06-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "nvidia/llama3-chatqa-1.5-70b": { + id: "nvidia/llama3-chatqa-1.5-70b", + name: "Llama3 Chatqa 1.5 70b", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-04-28", + last_updated: "2024-04-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "nvidia/llama-3.3-nemotron-super-49b-v1.5": { + id: "nvidia/llama-3.3-nemotron-super-49b-v1.5", + name: "Llama 3.3 Nemotron Super 49b V1.5", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-03-16", + last_updated: "2025-03-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "nvidia/nemotron-3-super-120b-a12b": { + id: "nvidia/nemotron-3-super-120b-a12b", + name: "Nemotron 3 Super", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2026-03-11", + last_updated: "2026-03-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.8 }, + limit: { context: 262144, output: 262144 }, + }, + "nvidia/parakeet-tdt-0.6b-v2": { + id: "nvidia/parakeet-tdt-0.6b-v2", + name: "Parakeet TDT 0.6B v2", + family: "parakeet", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + knowledge: "2024-01", + release_date: "2024-01-01", + last_updated: "2025-09-05", + modalities: { input: ["audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 0, output: 4096 }, + }, + "nvidia/nemotron-3-nano-30b-a3b": { + id: "nvidia/nemotron-3-nano-30b-a3b", + name: "nemotron-3-nano-30b-a3b", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-09", + release_date: "2024-12", + last_updated: "2024-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 131072 }, + }, + "nvidia/llama-3.1-nemotron-ultra-253b-v1": { + id: "nvidia/llama-3.1-nemotron-ultra-253b-v1", + name: "Llama-3.1-Nemotron-Ultra-253B-v1", + family: "llama", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2024-07-01", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 8192 }, + }, + "nvidia/nvidia-nemotron-nano-9b-v2": { + id: "nvidia/nvidia-nemotron-nano-9b-v2", + name: "nvidia-nemotron-nano-9b-v2", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-09", + release_date: "2025-08-18", + last_updated: "2025-08-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 131072 }, + }, + "nvidia/cosmos-nemotron-34b": { + id: "nvidia/cosmos-nemotron-34b", + name: "Cosmos Nemotron 34B", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2024-01", + release_date: "2024-01-01", + last_updated: "2025-09-05", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 8192 }, + }, + "nvidia/llama-embed-nemotron-8b": { + id: "nvidia/llama-embed-nemotron-8b", + name: "Llama Embed Nemotron 8B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + knowledge: "2025-03", + release_date: "2025-03-18", + last_updated: "2025-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 32768, output: 2048 }, + }, + "nvidia/llama-3.1-nemotron-51b-instruct": { + id: "nvidia/llama-3.1-nemotron-51b-instruct", + name: "Llama 3.1 Nemotron 51b Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-09-22", + last_updated: "2024-09-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "nvidia/llama-3.1-nemotron-70b-instruct": { + id: "nvidia/llama-3.1-nemotron-70b-instruct", + name: "Llama 3.1 Nemotron 70b Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-10-12", + last_updated: "2024-10-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "nvidia/nemoretriever-ocr-v1": { + id: "nvidia/nemoretriever-ocr-v1", + name: "NeMo Retriever OCR v1", + family: "nemoretriever", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + knowledge: "2024-01", + release_date: "2024-01-01", + last_updated: "2025-09-05", + modalities: { input: ["image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 0, output: 4096 }, + }, + "deepseek-ai/deepseek-r1": { + id: "deepseek-ai/deepseek-r1", + name: "Deepseek R1", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "deepseek-ai/deepseek-v3.1": { + id: "deepseek-ai/deepseek-v3.1", + name: "DeepSeek V3.1", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-08-20", + last_updated: "2025-08-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 8192 }, + }, + "deepseek-ai/deepseek-coder-6.7b-instruct": { + id: "deepseek-ai/deepseek-coder-6.7b-instruct", + name: "Deepseek Coder 6.7b Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2023-10-29", + last_updated: "2023-10-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "deepseek-ai/deepseek-v3.2": { + id: "deepseek-ai/deepseek-v3.2", + name: "DeepSeek V3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 163840, output: 65536 }, + }, + "deepseek-ai/deepseek-r1-0528": { + id: "deepseek-ai/deepseek-r1-0528", + name: "Deepseek R1 0528", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-05-28", + last_updated: "2025-05-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "deepseek-ai/deepseek-v3.1-terminus": { + id: "deepseek-ai/deepseek-v3.1-terminus", + name: "DeepSeek V3.1 Terminus", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-09-22", + last_updated: "2025-09-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 8192 }, + }, + "openai/whisper-large-v3": { + id: "openai/whisper-large-v3", + name: "Whisper Large v3", + family: "whisper", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + knowledge: "2023-09", + release_date: "2023-09-01", + last_updated: "2025-09-05", + modalities: { input: ["audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 0, output: 4096 }, + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "GPT-OSS-120B", + family: "gpt-oss", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2025-08", + release_date: "2025-08-04", + last_updated: "2025-08-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 8192 }, + }, + "minimaxai/minimax-m2.7": { + id: "minimaxai/minimax-m2.7", + name: "MiniMax-M2.7", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-04-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "minimaxai/minimax-m2.1": { + id: "minimaxai/minimax-m2.1", + name: "MiniMax-M2.1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "minimaxai/minimax-m2.5": { + id: "minimaxai/minimax-m2.5", + name: "MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08", + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "z-ai/glm4.7": { + id: "z-ai/glm4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "z-ai/glm5": { + id: "z-ai/glm5", + name: "GLM5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 202752, output: 131000 }, + }, + "z-ai/glm-5.1": { + id: "z-ai/glm-5.1", + name: "GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-03-27", + last_updated: "2026-03-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 131072 }, + }, + "meta/llama-4-scout-17b-16e-instruct": { + id: "meta/llama-4-scout-17b-16e-instruct", + name: "Llama 4 Scout 17b 16e Instruct", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-02", + release_date: "2025-04-02", + last_updated: "2025-04-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "meta/llama-3.3-70b-instruct": { + id: "meta/llama-3.3-70b-instruct", + name: "Llama 3.3 70b Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-11-26", + last_updated: "2024-11-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "meta/llama3-8b-instruct": { + id: "meta/llama3-8b-instruct", + name: "Llama3 8b Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-04-17", + last_updated: "2024-04-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "meta/llama3-70b-instruct": { + id: "meta/llama3-70b-instruct", + name: "Llama3 70b Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-04-17", + last_updated: "2024-04-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "meta/codellama-70b": { + id: "meta/codellama-70b", + name: "Codellama 70b", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2024-01-29", + last_updated: "2024-01-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "meta/llama-3.2-11b-vision-instruct": { + id: "meta/llama-3.2-11b-vision-instruct", + name: "Llama 3.2 11b Vision Instruct", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-09-18", + last_updated: "2024-09-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "meta/llama-3.1-70b-instruct": { + id: "meta/llama-3.1-70b-instruct", + name: "Llama 3.1 70b Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-07-16", + last_updated: "2024-07-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "meta/llama-3.2-1b-instruct": { + id: "meta/llama-3.2-1b-instruct", + name: "Llama 3.2 1b Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-09-18", + last_updated: "2024-09-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "meta/llama-4-maverick-17b-128e-instruct": { + id: "meta/llama-4-maverick-17b-128e-instruct", + name: "Llama 4 Maverick 17b 128e Instruct", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-02", + release_date: "2025-04-01", + last_updated: "2025-04-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "meta/llama-3.1-405b-instruct": { + id: "meta/llama-3.1-405b-instruct", + name: "Llama 3.1 405b Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-07-16", + last_updated: "2024-07-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "qwen/qwen3-235b-a22b": { + id: "qwen/qwen3-235b-a22b", + name: "Qwen3-235B-A22B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2024-12-01", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen/qwen2.5-coder-7b-instruct": { + id: "qwen/qwen2.5-coder-7b-instruct", + name: "Qwen2.5 Coder 7b Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-09-17", + last_updated: "2024-09-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "qwen/qwen2.5-coder-32b-instruct": { + id: "qwen/qwen2.5-coder-32b-instruct", + name: "Qwen2.5 Coder 32b Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-11-06", + last_updated: "2024-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "qwen/qwen3.5-397b-a17b": { + id: "qwen/qwen3.5-397b-a17b", + name: "Qwen3.5-397B-A17B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2026-01", + release_date: "2026-02-16", + last_updated: "2026-02-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 8192 }, + }, + "qwen/qwen3-next-80b-a3b-thinking": { + id: "qwen/qwen3-next-80b-a3b-thinking", + name: "Qwen3-Next-80B-A3B-Thinking", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2024-12-01", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 16384 }, + }, + "qwen/qwq-32b": { + id: "qwen/qwq-32b", + name: "Qwq 32b", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-03-05", + last_updated: "2025-03-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "qwen/qwen3-next-80b-a3b-instruct": { + id: "qwen/qwen3-next-80b-a3b-instruct", + name: "Qwen3-Next-80B-A3B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2024-12-01", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 16384 }, + }, + "qwen/qwen3-coder-480b-a35b-instruct": { + id: "qwen/qwen3-coder-480b-a35b-instruct", + name: "Qwen3 Coder 480B A35B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 66536 }, + }, + "google/gemma-2-2b-it": { + id: "google/gemma-2-2b-it", + name: "Gemma 2 2b It", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-07-16", + last_updated: "2024-07-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "google/gemma-3n-e4b-it": { + id: "google/gemma-3n-e4b-it", + name: "Gemma 3n E4b It", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-06", + release_date: "2025-06-03", + last_updated: "2025-06-03", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "google/gemma-3-1b-it": { + id: "google/gemma-3-1b-it", + name: "Gemma 3 1b It", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-03-10", + last_updated: "2025-03-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "google/codegemma-7b": { + id: "google/codegemma-7b", + name: "Codegemma 7b", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2024-03-21", + last_updated: "2024-03-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "google/gemma-4-31b-it": { + id: "google/gemma-4-31b-it", + name: "Gemma-4-31B-IT", + family: "gemma", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 256000, output: 16384 }, + }, + "google/gemma-3-12b-it": { + id: "google/gemma-3-12b-it", + name: "Gemma 3 12b It", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-03-01", + last_updated: "2025-03-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "google/codegemma-1.1-7b": { + id: "google/codegemma-1.1-7b", + name: "Codegemma 1.1 7b", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2024-04-30", + last_updated: "2024-04-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "google/gemma-3n-e2b-it": { + id: "google/gemma-3n-e2b-it", + name: "Gemma 3n E2b It", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-06", + release_date: "2025-06-12", + last_updated: "2025-06-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "google/gemma-2-27b-it": { + id: "google/gemma-2-27b-it", + name: "Gemma 2 27b It", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-06-24", + last_updated: "2024-06-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "google/gemma-3-27b-it": { + id: "google/gemma-3-27b-it", + name: "Gemma-3-27B-IT", + family: "gemma", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2024-12-01", + last_updated: "2025-09-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 8192 }, + }, + "moonshotai/kimi-k2.5": { + id: "moonshotai/kimi-k2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-07", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 262144 }, + }, + "moonshotai/kimi-k2-instruct": { + id: "moonshotai/kimi-k2-instruct", + name: "Kimi K2 Instruct", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-01", + release_date: "2025-01-01", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 8192 }, + }, + "moonshotai/kimi-k2.6": { + id: "moonshotai/kimi-k2.6", + name: "Kimi K2.6", + family: "kimi-k2.6", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 262144 }, + }, + "moonshotai/kimi-k2-instruct-0905": { + id: "moonshotai/kimi-k2-instruct-0905", + name: "Kimi K2 0905", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 262144 }, + }, + "moonshotai/kimi-k2-thinking": { + id: "moonshotai/kimi-k2-thinking", + name: "Kimi K2 Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: true, + structured_output: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-11", + last_updated: "2025-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 262144, output: 262144 }, + }, + "mistralai/mistral-medium-3.5-128b": { + id: "mistralai/mistral-medium-3.5-128b", + name: "Mistral Medium 3.5 128B", + family: "mistral-medium", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-29", + last_updated: "2026-04-29", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 262144 }, + }, + "deepseek-ai/deepseek-v4-flash": { + id: "deepseek-ai/deepseek-v4-flash", + name: "DeepSeek V4 Flash", + family: "deepseek-flash", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.28, cache_read: 0.028 }, + limit: { context: 1048576, output: 393216 }, + }, + "deepseek-ai/deepseek-v4-pro": { + id: "deepseek-ai/deepseek-v4-pro", + name: "DeepSeek V4 Pro", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.74, output: 3.48, cache_read: 0.145 }, + limit: { context: 1048576, output: 393216 }, + }, + }, + }, + inference: { + id: "inference", + env: ["INFERENCE_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://inference.net/v1", + name: "Inference", + doc: "https://inference.net/models", + models: { + "mistral/mistral-nemo-12b-instruct": { + id: "mistral/mistral-nemo-12b-instruct", + name: "Mistral Nemo 12B Instruct", + family: "mistral-nemo", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.038, output: 0.1 }, + limit: { context: 16000, output: 4096 }, + }, + "meta/llama-3.2-11b-vision-instruct": { + id: "meta/llama-3.2-11b-vision-instruct", + name: "Llama 3.2 11B Vision Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.055, output: 0.055 }, + limit: { context: 16000, output: 4096 }, + }, + "meta/llama-3.2-1b-instruct": { + id: "meta/llama-3.2-1b-instruct", + name: "Llama 3.2 1B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.01, output: 0.01 }, + limit: { context: 16000, output: 4096 }, + }, + "meta/llama-3.2-3b-instruct": { + id: "meta/llama-3.2-3b-instruct", + name: "Llama 3.2 3B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0.02 }, + limit: { context: 16000, output: 4096 }, + }, + "meta/llama-3.1-8b-instruct": { + id: "meta/llama-3.1-8b-instruct", + name: "Llama 3.1 8B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.025, output: 0.025 }, + limit: { context: 16000, output: 4096 }, + }, + "qwen/qwen-2.5-7b-vision-instruct": { + id: "qwen/qwen-2.5-7b-vision-instruct", + name: "Qwen 2.5 7B Vision Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 125000, output: 4096 }, + }, + "qwen/qwen3-embedding-4b": { + id: "qwen/qwen3-embedding-4b", + name: "Qwen 3 Embedding 4B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + knowledge: "2024-12", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.01, output: 0 }, + limit: { context: 32000, output: 2048 }, + }, + "google/gemma-3": { + id: "google/gemma-3", + name: "Google Gemma 3", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.3 }, + limit: { context: 125000, output: 4096 }, + }, + "osmosis/osmosis-structure-0.6b": { + id: "osmosis/osmosis-structure-0.6b", + name: "Osmosis Structure 0.6B", + family: "osmosis", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.5 }, + limit: { context: 4000, output: 2048 }, + }, + }, + }, + inception: { + id: "inception", + env: ["INCEPTION_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.inceptionlabs.ai/v1/", + name: "Inception", + doc: "https://platform.inceptionlabs.ai/docs", + models: { + "mercury-edit-2": { + id: "mercury-edit-2", + name: "Mercury Edit 2", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2026-03-30", + last_updated: "2026-03-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 0.75, cache_read: 0.025 }, + limit: { context: 128000, output: 8192 }, + }, + "mercury-2": { + id: "mercury-2", + name: "Mercury 2", + family: "mercury", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01-01", + release_date: "2026-02-24", + last_updated: "2026-02-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 0.75, cache_read: 0.025 }, + limit: { context: 128000, output: 50000 }, + }, + }, + }, + openai: { + id: "openai", + env: ["OPENAI_API_KEY"], + npm: "@ai-sdk/openai", + name: "OpenAI", + doc: "https://platform.openai.com/docs/models", + models: { + "gpt-5.1-codex-max": { + id: "gpt-5.1-codex-max", + name: "GPT-5.1 Codex Max", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gpt-4o-2024-05-13": { + id: "gpt-4o-2024-05-13", + name: "GPT-4o (2024-05-13)", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-09", + release_date: "2024-05-13", + last_updated: "2024-05-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 15 }, + limit: { context: 128000, output: 4096 }, + }, + "o1-mini": { + id: "o1-mini", + name: "o1-mini", + family: "o-mini", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: true, + temperature: false, + knowledge: "2023-09", + release_date: "2024-09-12", + last_updated: "2024-09-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.55 }, + limit: { context: 128000, output: 65536 }, + }, + "gpt-5.2-pro": { + id: "gpt-5.2-pro", + name: "GPT-5.2 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 21, output: 168 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "text-embedding-3-large": { + id: "text-embedding-3-large", + name: "text-embedding-3-large", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + knowledge: "2024-01", + release_date: "2024-01-25", + last_updated: "2024-01-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.13, output: 0 }, + limit: { context: 8191, output: 3072 }, + }, + "gpt-5.3-chat-latest": { + id: "gpt-5.3-chat-latest", + name: "GPT-5.3 Chat (latest)", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-03-03", + last_updated: "2026-03-03", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 128000, output: 16384 }, + }, + "gpt-5.5": { + id: "gpt-5.5", + name: "GPT-5.5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-12-01", + release_date: "2026-04-23", + last_updated: "2026-04-23", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 30, cache_read: 0.5, context_over_200k: { input: 10, output: 45, cache_read: 1 } }, + limit: { context: 1050000, input: 922000, output: 128000 }, + experimental: { + modes: { + fast: { + cost: { input: 12.5, output: 75, cache_read: 1.25 }, + provider: { body: { service_tier: "priority" } }, + }, + }, + }, + }, + "gpt-5-mini": { + id: "gpt-5-mini", + name: "GPT-5 Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.025 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gpt-5-nano": { + id: "gpt-5-nano", + name: "GPT-5 Nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.4, cache_read: 0.005 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gpt-5.3-codex": { + id: "gpt-5.3-codex", + name: "GPT-5.3 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gpt-4-turbo": { + id: "gpt-4-turbo", + name: "GPT-4 Turbo", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + knowledge: "2023-12", + release_date: "2023-11-06", + last_updated: "2024-04-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 10, output: 30 }, + limit: { context: 128000, output: 4096 }, + }, + "text-embedding-ada-002": { + id: "text-embedding-ada-002", + name: "text-embedding-ada-002", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + knowledge: "2022-12", + release_date: "2022-12-15", + last_updated: "2022-12-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0 }, + limit: { context: 8192, output: 1536 }, + }, + "gpt-5.2": { + id: "gpt-5.2", + name: "GPT-5.2", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "o3-pro": { + id: "o3-pro", + name: "o3-pro", + family: "o-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05", + release_date: "2025-06-10", + last_updated: "2025-06-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 20, output: 80 }, + limit: { context: 200000, output: 100000 }, + }, + "gpt-4o-mini": { + id: "gpt-4o-mini", + name: "GPT-4o mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-09", + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6, cache_read: 0.08 }, + limit: { context: 128000, output: 16384 }, + }, + "o4-mini-deep-research": { + id: "o4-mini-deep-research", + name: "o4-mini-deep-research", + family: "o-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05", + release_date: "2024-06-26", + last_updated: "2024-06-26", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 200000, output: 100000 }, + }, + "gpt-5.4-mini": { + id: "gpt-5.4-mini", + name: "GPT-5.4 mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.75, output: 4.5, cache_read: 0.075 }, + limit: { context: 400000, input: 272000, output: 128000 }, + experimental: { + modes: { + fast: { + cost: { input: 1.5, output: 9, cache_read: 0.15 }, + provider: { body: { service_tier: "priority" } }, + }, + }, + }, + }, + "o4-mini": { + id: "o4-mini", + name: "o4-mini", + family: "o-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05", + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.28 }, + limit: { context: 200000, output: 100000 }, + }, + "gpt-5.4-nano": { + id: "gpt-5.4-nano", + name: "GPT-5.4 nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.25, cache_read: 0.02 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gpt-image-1": { + id: "gpt-image-1", + name: "gpt-image-1", + family: "gpt-image", + attachment: true, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-04-24", + last_updated: "2025-04-24", + modalities: { input: ["text", "image"], output: ["image"] }, + open_weights: false, + limit: { context: 0, input: 0, output: 0 }, + }, + "gpt-5.2-codex": { + id: "gpt-5.2-codex", + name: "GPT-5.2 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gpt-5.2-chat-latest": { + id: "gpt-5.2-chat-latest", + name: "GPT-5.2 Chat", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 128000, output: 16384 }, + }, + "gpt-5.1-codex-mini": { + id: "gpt-5.1-codex-mini", + name: "GPT-5.1 Codex mini", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.025 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "o1-preview": { + id: "o1-preview", + name: "o1-preview", + family: "o", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2023-09", + release_date: "2024-09-12", + last_updated: "2024-09-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 60, cache_read: 7.5 }, + limit: { context: 128000, output: 32768 }, + }, + "gpt-4o-2024-08-06": { + id: "gpt-4o-2024-08-06", + name: "GPT-4o (2024-08-06)", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-09", + release_date: "2024-08-06", + last_updated: "2024-08-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10, cache_read: 1.25 }, + limit: { context: 128000, output: 16384 }, + }, + "gpt-5.1": { + id: "gpt-5.1", + name: "GPT-5.1", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.13 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gpt-image-1-mini": { + id: "gpt-image-1-mini", + name: "gpt-image-1-mini", + family: "gpt-image", + attachment: true, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-09-26", + last_updated: "2025-09-26", + modalities: { input: ["text", "image"], output: ["text", "image"] }, + open_weights: false, + limit: { context: 0, input: 0, output: 0 }, + }, + o1: { + id: "o1", + name: "o1", + family: "o", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2023-09", + release_date: "2024-12-05", + last_updated: "2024-12-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 60, cache_read: 7.5 }, + limit: { context: 200000, output: 100000 }, + }, + "gpt-5.4-pro": { + id: "gpt-5.4-pro", + name: "GPT-5.4 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 180, context_over_200k: { input: 60, output: 270 } }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "gpt-3.5-turbo": { + id: "gpt-3.5-turbo", + name: "GPT-3.5-turbo", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + knowledge: "2021-09-01", + release_date: "2023-03-01", + last_updated: "2023-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 1.5, cache_read: 1.25 }, + limit: { context: 16385, output: 4096 }, + }, + "o3-deep-research": { + id: "o3-deep-research", + name: "o3-deep-research", + family: "o", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05", + release_date: "2024-06-26", + last_updated: "2024-06-26", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 10, output: 40, cache_read: 2.5 }, + limit: { context: 200000, output: 100000 }, + }, + "o3-mini": { + id: "o3-mini", + name: "o3-mini", + family: "o-mini", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05", + release_date: "2024-12-20", + last_updated: "2025-01-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.55 }, + limit: { context: 200000, output: 100000 }, + }, + "text-embedding-3-small": { + id: "text-embedding-3-small", + name: "text-embedding-3-small", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + knowledge: "2024-01", + release_date: "2024-01-25", + last_updated: "2024-01-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.02, output: 0 }, + limit: { context: 8191, output: 1536 }, + }, + "o1-pro": { + id: "o1-pro", + name: "o1-pro", + family: "o-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2023-09", + release_date: "2025-03-19", + last_updated: "2025-03-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 150, output: 600 }, + limit: { context: 200000, output: 100000 }, + }, + "gpt-4": { + id: "gpt-4", + name: "GPT-4", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + knowledge: "2023-11", + release_date: "2023-11-06", + last_updated: "2024-04-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 60 }, + limit: { context: 8192, output: 8192 }, + }, + "gpt-5-codex": { + id: "gpt-5-codex", + name: "GPT-5-Codex", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-09-15", + last_updated: "2025-09-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gpt-5.4": { + id: "gpt-5.4", + name: "GPT-5.4", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 2.5, + output: 15, + cache_read: 0.25, + context_over_200k: { input: 5, output: 22.5, cache_read: 0.5 }, + }, + limit: { context: 1050000, input: 922000, output: 128000 }, + experimental: { + modes: { + fast: { cost: { input: 5, output: 30, cache_read: 0.5 }, provider: { body: { service_tier: "priority" } } }, + }, + }, + }, + "gpt-5.1-chat-latest": { + id: "gpt-5.1-chat-latest", + name: "GPT-5.1 Chat", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 128000, output: 16384 }, + }, + "gpt-5.3-codex-spark": { + id: "gpt-5.3-codex-spark", + name: "GPT-5.3 Codex Spark", + family: "gpt-codex-spark", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 128000, input: 100000, output: 32000 }, + }, + "chatgpt-image-latest": { + id: "chatgpt-image-latest", + name: "chatgpt-image-latest", + family: "gpt-image", + attachment: true, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-12-16", + last_updated: "2025-12-16", + modalities: { input: ["text", "image"], output: ["text", "image"] }, + open_weights: false, + limit: { context: 0, input: 0, output: 0 }, + }, + "gpt-4.1-nano": { + id: "gpt-4.1-nano", + name: "GPT-4.1 nano", + family: "gpt-nano", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.03 }, + limit: { context: 1047576, output: 32768 }, + }, + o3: { + id: "o3", + name: "o3", + family: "o", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05", + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 200000, output: 100000 }, + }, + "gpt-5-pro": { + id: "gpt-5-pro", + name: "GPT-5 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-10-06", + last_updated: "2025-10-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 120 }, + limit: { context: 400000, input: 272000, output: 272000 }, + }, + "gpt-4o": { + id: "gpt-4o", + name: "GPT-4o", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-09", + release_date: "2024-05-13", + last_updated: "2024-08-06", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10, cache_read: 1.25 }, + limit: { context: 128000, output: 16384 }, + }, + "gpt-5": { + id: "gpt-5", + name: "GPT-5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gpt-5-chat-latest": { + id: "gpt-5-chat-latest", + name: "GPT-5 Chat (latest)", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: true, + temperature: true, + knowledge: "2024-09-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gpt-image-1.5": { + id: "gpt-image-1.5", + name: "gpt-image-1.5", + family: "gpt-image", + attachment: true, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-11-25", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text", "image"] }, + open_weights: false, + limit: { context: 0, input: 0, output: 0 }, + }, + "gpt-5.5-pro": { + id: "gpt-5.5-pro", + name: "GPT-5.5 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-12-01", + release_date: "2026-04-23", + last_updated: "2026-04-23", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 180, context_over_200k: { input: 60, output: 270 } }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "gpt-4.1": { + id: "gpt-4.1", + name: "GPT-4.1", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 1047576, output: 32768 }, + }, + "gpt-4.1-mini": { + id: "gpt-4.1-mini", + name: "GPT-4.1 mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.6, cache_read: 0.1 }, + limit: { context: 1047576, output: 32768 }, + }, + "gpt-5.1-codex": { + id: "gpt-5.1-codex", + name: "GPT-5.1 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gpt-4o-2024-11-20": { + id: "gpt-4o-2024-11-20", + name: "GPT-4o (2024-11-20)", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-09", + release_date: "2024-11-20", + last_updated: "2024-11-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10, cache_read: 1.25 }, + limit: { context: 128000, output: 16384 }, + }, + }, + }, + requesty: { + id: "requesty", + env: ["REQUESTY_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://router.requesty.ai/v1", + name: "Requesty", + doc: "https://requesty.ai/solution/llm-routing/models", + models: { + "xai/grok-4-fast": { + id: "xai/grok-4-fast", + name: "Grok 4 Fast", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-09-19", + last_updated: "2025-09-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05, cache_write: 0.2 }, + limit: { context: 2000000, output: 64000 }, + }, + "xai/grok-4": { + id: "xai/grok-4", + name: "Grok 4", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-09-09", + last_updated: "2025-09-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.75, cache_write: 3 }, + limit: { context: 256000, output: 64000 }, + }, + "openai/gpt-5.1-codex-max": { + id: "openai/gpt-5.1-codex-max", + name: "GPT-5.1-Codex-Max", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 9, cache_read: 0.11 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5-chat": { + id: "openai/gpt-5-chat", + name: "GPT-5 Chat (latest)", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: true, + temperature: true, + knowledge: "2024-09-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.2-pro": { + id: "openai/gpt-5.2-pro", + name: "GPT-5.2 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 21, output: 168 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5-mini": { + id: "openai/gpt-5-mini", + name: "GPT-5 Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.03 }, + limit: { context: 128000, output: 32000 }, + }, + "openai/gpt-5-nano": { + id: "openai/gpt-5-nano", + name: "GPT-5 Nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.4, cache_read: 0.01 }, + limit: { context: 16000, output: 4000 }, + }, + "openai/gpt-5.3-codex": { + id: "openai/gpt-5.3-codex", + name: "GPT-5.3-Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-02-24", + last_updated: "2026-02-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-4o-mini": { + id: "openai/gpt-4o-mini", + name: "GPT-4o Mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6, cache_read: 0.08 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-5.1-chat": { + id: "openai/gpt-5.1-chat", + name: "GPT-5.1 Chat", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/o4-mini": { + id: "openai/o4-mini", + name: "o4 Mini", + family: "o-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-06", + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.28 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-5.2-codex": { + id: "openai/gpt-5.2-codex", + name: "GPT-5.2-Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-01-14", + last_updated: "2026-01-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.1-codex-mini": { + id: "openai/gpt-5.1-codex-mini", + name: "GPT-5.1-Codex-Mini", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.025 }, + limit: { context: 400000, output: 100000 }, + }, + "openai/gpt-5-image": { + id: "openai/gpt-5-image", + name: "GPT-5 Image", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-10-01", + release_date: "2025-10-14", + last_updated: "2025-10-14", + modalities: { input: ["text", "image", "pdf"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 5, output: 10, cache_read: 1.25 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.1": { + id: "openai/gpt-5.1", + name: "GPT-5.1", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.4-pro": { + id: "openai/gpt-5.4-pro", + name: "GPT-5.4 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 180, cache_read: 30 }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "openai/gpt-5-codex": { + id: "openai/gpt-5-codex", + name: "GPT-5 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-10-01", + release_date: "2025-09-15", + last_updated: "2025-09-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5": { + id: "openai/gpt-5", + name: "GPT-5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "audio", "image", "video"], output: ["text", "audio", "image"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.13 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-4.1-mini": { + id: "openai/gpt-4.1-mini", + name: "GPT-4.1 Mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.6, cache_read: 0.1 }, + limit: { context: 1047576, output: 32768 }, + }, + "openai/gpt-5.1-codex": { + id: "openai/gpt-5.1-codex", + name: "GPT-5.1-Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "google/gemini-3-flash-preview": { + id: "google/gemini-3-flash-preview", + name: "Gemini 3 Flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 3, cache_read: 0.05, cache_write: 1 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-3-pro-preview": { + id: "google/gemini-3-pro-preview", + name: "Gemini 3 Pro", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-11-18", + last_updated: "2025-11-18", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2, cache_write: 4.5 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-2.5-flash": { + id: "google/gemini-2.5-flash", + name: "Gemini 2.5 Flash", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5, cache_read: 0.075, cache_write: 0.55 }, + limit: { context: 1048576, output: 65536 }, + }, + "anthropic/claude-haiku-4-5": { + id: "anthropic/claude-haiku-4-5", + name: "Claude Haiku 4.5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-01", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 62000 }, + }, + "anthropic/claude-sonnet-4-6": { + id: "anthropic/claude-sonnet-4-6", + name: "Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-02-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { + input: 3, + output: 15, + cache_read: 0.3, + cache_write: 3.75, + context_over_200k: { input: 6, output: 22.5, cache_read: 0.6, cache_write: 7.5 }, + }, + limit: { context: 1000000, output: 128000 }, + }, + "anthropic/claude-3-7-sonnet": { + id: "anthropic/claude-3-7-sonnet", + name: "Claude Sonnet 3.7", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-01", + release_date: "2025-02-19", + last_updated: "2025-02-19", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-opus-4-5": { + id: "anthropic/claude-opus-4-5", + name: "Claude Opus 4.5", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-24", + last_updated: "2025-11-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-opus-4": { + id: "anthropic/claude-opus-4", + name: "Claude Opus 4", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "anthropic/claude-opus-4-6": { + id: "anthropic/claude-opus-4-6", + name: "Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 5, + output: 25, + cache_read: 0.5, + cache_write: 6.25, + context_over_200k: { input: 10, output: 37.5, cache_read: 1, cache_write: 12.5 }, + }, + limit: { context: 1000000, output: 128000 }, + }, + "openai/gpt-5.2-chat": { + id: "openai/gpt-5.2-chat", + name: "GPT-5.2 Chat", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-5.2": { + id: "openai/gpt-5.2", + name: "GPT-5.2", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5.4": { + id: "openai/gpt-5.4", + name: "GPT-5.4", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 2.5, + output: 15, + cache_read: 0.25, + context_over_200k: { input: 5, output: 22.5, cache_read: 0.5 }, + }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "openai/gpt-5-pro": { + id: "openai/gpt-5-pro", + name: "GPT-5 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-10-06", + last_updated: "2025-10-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 120 }, + limit: { context: 400000, output: 272000 }, + }, + "openai/gpt-4.1": { + id: "openai/gpt-4.1", + name: "GPT-4.1", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 1047576, output: 32768 }, + }, + "google/gemini-2.5-pro": { + id: "google/gemini-2.5-pro", + name: "Gemini 2.5 Pro", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-20", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 1.25, + output: 10, + cache_read: 0.31, + cache_write: 2.375, + context_over_200k: { input: 2.5, output: 15, cache_read: 0.25 }, + }, + limit: { context: 1048576, output: 65536 }, + }, + "anthropic/claude-opus-4-1": { + id: "anthropic/claude-opus-4-1", + name: "Claude Opus 4.1", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "anthropic/claude-sonnet-4": { + id: "anthropic/claude-sonnet-4", + name: "Claude Sonnet 4", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-sonnet-4-5": { + id: "anthropic/claude-sonnet-4-5", + name: "Claude Sonnet 4.5", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 1000000, output: 64000 }, + }, + }, + }, + digitalocean: { + id: "digitalocean", + env: ["DIGITALOCEAN_ACCESS_TOKEN"], + npm: "@ai-sdk/openai-compatible", + api: "https://inference.do-ai.run/v1", + name: "DigitalOcean", + doc: "https://docs.digitalocean.com/products/gradient-ai-platform/details/models/", + models: { + "openai-gpt-4o-mini": { + id: "openai-gpt-4o-mini", + name: "GPT-4o mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-09", + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6, cache_read: 0.075 }, + limit: { context: 128000, output: 16384 }, + }, + "multi-qa-mpnet-base-dot-v1": { + id: "multi-qa-mpnet-base-dot-v1", + name: "Multi-QA-mpnet-base-dot-v1", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2021-08-30", + last_updated: "2026-04-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.009, output: 0 }, + limit: { context: 512, output: 768 }, + }, + "kimi-k2.5": { + id: "kimi-k2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: false, + knowledge: "2025-01", + release_date: "2026-01", + last_updated: "2026-04-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 2.7 }, + limit: { context: 262144, output: 32768 }, + }, + "nemotron-3-nano-omni": { + id: "nemotron-3-nano-omni", + name: "Nemotron Nano 3 Omni", + family: "nemotron", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-28", + last_updated: "2026-04-30", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 0.9 }, + limit: { context: 65536, output: 65536 }, + }, + "llama3-8b-instruct": { + id: "llama3-8b-instruct", + name: "Llama 3.1 Instruct (8B)", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.198, output: 0.198 }, + limit: { context: 131072, output: 131072 }, + }, + "anthropic-claude-opus-4.7": { + id: "anthropic-claude-opus-4.7", + name: "Claude Opus 4.7", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "anthropic-claude-sonnet-4": { + id: "anthropic-claude-sonnet-4", + name: "Claude Sonnet 4", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 3, + output: 15, + cache_read: 0.3, + cache_write: 3.75, + context_over_200k: { input: 6, output: 22.5, cache_read: 0.3, cache_write: 3.75 }, + }, + limit: { context: 1000000, output: 64000 }, + }, + "wan2-2-t2v-a14b": { + id: "wan2-2-t2v-a14b", + name: "Wan2.2-T2V-A14B", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-07-28", + last_updated: "2026-04-30", + modalities: { input: ["text"], output: ["video"] }, + open_weights: true, + cost: { input: 0.6, output: 0 }, + limit: { context: 100, output: 1 }, + }, + "qwen-2.5-14b-instruct": { + id: "qwen-2.5-14b-instruct", + name: "Qwen 2.5 14B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-09", + release_date: "2024-09-19", + last_updated: "2024-09-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 131072, output: 131072 }, + }, + "openai-gpt-5.4": { + id: "openai-gpt-5.4", + name: "GPT-5.4", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 15, cache_read: 0.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "qwen3.5-397b-a17b": { + id: "qwen3.5-397b-a17b", + name: "Qwen 3.5 397B A17B", + family: "qwen3.5", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-15", + last_updated: "2026-04-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 3.5 }, + limit: { context: 262144, output: 81920 }, + }, + "openai-o3": { + id: "openai-o3", + name: "o3", + family: "o", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05", + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 200000, output: 100000 }, + }, + "e5-large-v2": { + id: "e5-large-v2", + name: "E5 Large v2", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2023-05-19", + last_updated: "2026-04-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0 }, + limit: { context: 512, output: 1024 }, + }, + "openai-gpt-5.2-pro": { + id: "openai-gpt-5.2-pro", + name: "GPT-5.2 pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 21, output: 168 }, + limit: { context: 400000, output: 128000 }, + }, + "glm-5": { + id: "glm-5", + name: "GLM 5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + release_date: "2026-02-11", + last_updated: "2026-04-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3.2 }, + limit: { context: 202752, output: 128000 }, + }, + "openai-gpt-5.4-nano": { + id: "openai-gpt-5.4-nano", + name: "GPT-5.4 nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.25, cache_read: 0.02 }, + limit: { context: 400000, output: 128000 }, + }, + "mistral-7b-instruct-v0.3": { + id: "mistral-7b-instruct-v0.3", + name: "Mistral 7B Instruct v0.3", + family: "mistral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-05-22", + last_updated: "2024-05-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 32768, output: 32768 }, + }, + "llama3.3-70b-instruct": { + id: "llama3.3-70b-instruct", + name: "Llama 3.3 Instruct 70B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.65, output: 0.65 }, + limit: { context: 128000, output: 128000 }, + }, + "mistral-3-14B": { + id: "mistral-3-14B", + name: "Ministral 3 14B Instruct", + family: "ministral", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-15", + last_updated: "2026-04-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 262144, output: 128000 }, + }, + "deepseek-r1-distill-llama-70b": { + id: "deepseek-r1-distill-llama-70b", + name: "DeepSeek R1 Distill Llama 70B", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-01-30", + last_updated: "2025-01-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.99, output: 0.99 }, + limit: { context: 131072, output: 32768 }, + }, + "alibaba-qwen3-32b": { + id: "alibaba-qwen3-32b", + name: "Qwen3-32B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-30", + last_updated: "2026-04-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 0.55 }, + limit: { context: 131000, output: 40960 }, + }, + "anthropic-claude-opus-4.5": { + id: "anthropic-claude-opus-4.5", + name: "Claude Opus 4.5", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-24", + last_updated: "2025-11-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 64000 }, + }, + "openai-o1": { + id: "openai-o1", + name: "o1", + family: "o", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2023-09", + release_date: "2024-12-05", + last_updated: "2024-12-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 60, cache_read: 7.5 }, + limit: { context: 200000, output: 100000 }, + }, + "anthropic-claude-3-opus": { + id: "anthropic-claude-3-opus", + name: "Claude 3 Opus", + family: "claude-opus", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-08", + release_date: "2024-02-29", + last_updated: "2024-02-29", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 4096 }, + status: "deprecated", + }, + "stable-diffusion-3.5-large": { + id: "stable-diffusion-3.5-large", + name: "Stable Diffusion 3.5 Large", + family: "stable-diffusion", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-10-22", + last_updated: "2026-04-30", + modalities: { input: ["text"], output: ["image"] }, + open_weights: true, + cost: { input: 0.08, output: 0 }, + limit: { context: 256, output: 1 }, + }, + "openai-gpt-5-nano": { + id: "openai-gpt-5-nano", + name: "GPT-5 nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.4, cache_read: 0.005 }, + limit: { context: 400000, output: 128000 }, + }, + "llama-4-maverick": { + id: "llama-4-maverick", + name: "Llama 4 Maverick 17B 128E Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-04-05", + last_updated: "2026-04-30", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 0.87 }, + limit: { context: 1000000, output: 16384 }, + }, + "anthropic-claude-4.5-sonnet": { + id: "anthropic-claude-4.5-sonnet", + name: "Claude Sonnet 4.5", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 3, + output: 15, + cache_read: 0.3, + cache_write: 3.75, + context_over_200k: { input: 6, output: 22.5, cache_read: 0.3, cache_write: 3.75 }, + }, + limit: { context: 1000000, output: 64000 }, + }, + "qwen3-embedding-0.6b": { + id: "qwen3-embedding-0.6b", + name: "Qwen3 Embedding 0.6B", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-06-03", + last_updated: "2026-04-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.04, output: 0 }, + limit: { context: 8000, output: 1024 }, + status: "beta", + }, + "anthropic-claude-4.5-haiku": { + id: "anthropic-claude-4.5-haiku", + name: "Claude Haiku 4.5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + }, + "gte-large-en-v1.5": { + id: "gte-large-en-v1.5", + name: "GTE Large (v1.5)", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-03-27", + last_updated: "2026-04-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.09, output: 0 }, + limit: { context: 8192, output: 1024 }, + }, + "openai-gpt-4.1": { + id: "openai-gpt-4.1", + name: "GPT-4.1", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 1047576, output: 32768 }, + }, + "anthropic-claude-3.5-haiku": { + id: "anthropic-claude-3.5-haiku", + name: "Claude 3.5 Haiku", + family: "claude-haiku", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2024-11-05", + last_updated: "2024-11-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 4, cache_read: 0.08, cache_write: 1 }, + limit: { context: 200000, output: 8192 }, + status: "deprecated", + }, + "openai-gpt-5.2": { + id: "openai-gpt-5.2", + name: "GPT-5.2", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, output: 128000 }, + }, + "deepseek-3.2": { + id: "deepseek-3.2", + name: "DeepSeek V3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2025-12-02", + last_updated: "2026-04-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 1.6 }, + limit: { context: 128000, output: 64000 }, + }, + "nemotron-3-nano-30b": { + id: "nemotron-3-nano-30b", + name: "Nemotron 3 Nano 30B A3B", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 262144, output: 262144 }, + }, + "anthropic-claude-opus-4": { + id: "anthropic-claude-opus-4", + name: "Claude Opus 4", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "openai-gpt-oss-20b": { + id: "openai-gpt-oss-20b", + name: "gpt-oss-20b", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-06", + release_date: "2025-08-05", + last_updated: "2026-04-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.45 }, + limit: { context: 131072, output: 131072 }, + }, + "qwen3-coder-flash": { + id: "qwen3-coder-flash", + name: "Qwen3 Coder Flash", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2026-04-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.45, output: 1.7 }, + limit: { context: 262144, output: 65536 }, + }, + "openai-o3-mini": { + id: "openai-o3-mini", + name: "o3-mini", + family: "o-mini", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05", + release_date: "2024-12-20", + last_updated: "2025-01-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.55 }, + limit: { context: 200000, output: 100000 }, + }, + "openai-gpt-oss-120b": { + id: "openai-gpt-oss-120b", + name: "gpt-oss-120b", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2024-06", + release_date: "2025-08-05", + last_updated: "2026-04-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.7 }, + limit: { context: 131072, output: 131072 }, + }, + "gemma-4-31B-it": { + id: "gemma-4-31B-it", + name: "Gemma 4 31B", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-22", + last_updated: "2026-04-30", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.18, output: 0.5 }, + limit: { context: 256000, output: 8192 }, + }, + "nemotron-nano-12b-v2-vl": { + id: "nemotron-nano-12b-v2-vl", + name: "Nemotron Nano 12B v2 VL", + family: "nemotron", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-12-01", + last_updated: "2026-04-30", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.6 }, + limit: { context: 128000, output: 16384 }, + }, + "anthropic-claude-4.1-opus": { + id: "anthropic-claude-4.1-opus", + name: "Claude Opus 4.1", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "kimi-k2.6": { + id: "kimi-k2.6", + name: "Kimi K2.6", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: false, + knowledge: "2025-01", + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4 }, + limit: { context: 262144, output: 262144 }, + }, + "openai-gpt-image-2": { + id: "openai-gpt-image-2", + name: "GPT Image 2", + family: "gpt-image", + attachment: true, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-04-24", + last_updated: "2025-04-24", + modalities: { input: ["text", "image"], output: ["image"] }, + open_weights: false, + limit: { context: 0, output: 0 }, + }, + "anthropic-claude-4.6-sonnet": { + id: "anthropic-claude-4.6-sonnet", + name: "Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 3, + output: 15, + cache_read: 0.3, + cache_write: 3.75, + context_over_200k: { input: 6, output: 22.5, cache_read: 0.3, cache_write: 3.75 }, + }, + limit: { context: 1000000, output: 64000 }, + }, + "openai-gpt-5-mini": { + id: "openai-gpt-5-mini", + name: "GPT-5 mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.025 }, + limit: { context: 400000, output: 128000 }, + }, + "anthropic-claude-haiku-4.5": { + id: "anthropic-claude-haiku-4.5", + name: "Claude Haiku 4.5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + }, + "deepseek-v4-pro": { + id: "deepseek-v4-pro", + name: "DeepSeek V4 Pro", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.74, output: 3.48 }, + limit: { context: 1048576, output: 393216 }, + }, + "ministral-3-8b-instruct-2512": { + id: "ministral-3-8b-instruct-2512", + name: "Ministral 3 8B", + family: "ministral", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-12-15", + last_updated: "2025-12-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + limit: { context: 262144, output: 262144 }, + }, + "minimax-m2.5": { + id: "minimax-m2.5", + name: "MiniMax M2.5", + family: "minimax-m2.5", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08", + release_date: "2026-02-12", + last_updated: "2026-04-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 204800, output: 128000 }, + status: "beta", + }, + "openai-gpt-image-1": { + id: "openai-gpt-image-1", + name: "GPT Image 1", + family: "gpt-image", + attachment: true, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-04-24", + last_updated: "2025-04-24", + modalities: { input: ["text", "image"], output: ["image"] }, + open_weights: false, + cost: { input: 5, output: 40, cache_read: 1.25 }, + limit: { context: 0, output: 0 }, + }, + "openai-gpt-5.5": { + id: "openai-gpt-5.5", + name: "GPT-5.5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-12-01", + release_date: "2026-04-23", + last_updated: "2026-04-30", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 30, cache_read: 0.5, context_over_200k: { input: 10, output: 45, cache_read: 1 } }, + limit: { context: 1000000, output: 128000 }, + }, + "nvidia-nemotron-3-super-120b": { + id: "nvidia-nemotron-3-super-120b", + name: "Nemotron-3-Super-120B", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2026-02", + release_date: "2026-03-11", + last_updated: "2026-04-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.65 }, + limit: { context: 256000, output: 32768 }, + status: "beta", + }, + "openai-gpt-5.4-pro": { + id: "openai-gpt-5.4-pro", + name: "GPT-5.4 pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 180 }, + limit: { context: 400000, output: 128000 }, + }, + "all-mini-lm-l6-v2": { + id: "all-mini-lm-l6-v2", + name: "All-MiniLM-L6-v2", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2021-08-30", + last_updated: "2026-04-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.009, output: 0 }, + limit: { context: 256, output: 384 }, + }, + "bge-m3": { + id: "bge-m3", + name: "BGE M3", + family: "bge", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-01-30", + last_updated: "2026-04-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0 }, + limit: { context: 8192, output: 1024 }, + }, + "openai-gpt-5.1-codex-max": { + id: "openai-gpt-5.1-codex-max", + name: "GPT-5.1 Codex Max", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "anthropic-claude-opus-4.6": { + id: "anthropic-claude-opus-4.6", + name: "Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 5, + output: 25, + cache_read: 0.5, + cache_write: 6.25, + context_over_200k: { input: 10, output: 37.5, cache_read: 0.5, cache_write: 6.25 }, + }, + limit: { context: 1000000, output: 128000 }, + }, + "openai-gpt-4o": { + id: "openai-gpt-4o", + name: "GPT-4o", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-09", + release_date: "2024-05-13", + last_updated: "2024-08-06", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10, cache_read: 1.25 }, + limit: { context: 128000, output: 16384 }, + }, + "openai-gpt-5.4-mini": { + id: "openai-gpt-5.4-mini", + name: "GPT-5.4 mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.75, output: 4.5, cache_read: 0.075 }, + limit: { context: 400000, output: 128000 }, + }, + "openai-gpt-5": { + id: "openai-gpt-5", + name: "GPT-5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "arcee-trinity-large-thinking": { + id: "arcee-trinity-large-thinking", + name: "Trinity Large Thinking", + family: "trinity", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-02", + last_updated: "2026-04-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 0.9, cache_read: 0.06 }, + limit: { context: 256000, output: 128000 }, + status: "beta", + }, + "mistral-nemo-instruct-2407": { + id: "mistral-nemo-instruct-2407", + name: "Mistral Nemo Instruct", + family: "mistral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.3 }, + limit: { context: 128000, output: 16384 }, + status: "deprecated", + }, + "deepseek-v3": { + id: "deepseek-v3", + name: "DeepSeek V3", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2024-12-26", + last_updated: "2025-03-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + limit: { context: 163840, output: 131072 }, + }, + "bge-reranker-v2-m3": { + id: "bge-reranker-v2-m3", + name: "BGE Reranker v2 M3", + family: "bge", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-03-12", + last_updated: "2026-04-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.01, output: 0 }, + limit: { context: 8192, output: 1 }, + }, + "anthropic-claude-3.7-sonnet": { + id: "anthropic-claude-3.7-sonnet", + name: "Claude 3.7 Sonnet", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-24", + last_updated: "2025-02-24", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + status: "deprecated", + }, + "qwen3-tts-voicedesign": { + id: "qwen3-tts-voicedesign", + name: "Qwen3 TTS VoiceDesign", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2026-04-21", + last_updated: "2026-04-30", + modalities: { input: ["text"], output: ["audio"] }, + open_weights: true, + limit: { context: 32768, output: 1 }, + }, + "openai-gpt-image-1.5": { + id: "openai-gpt-image-1.5", + name: "GPT Image 1.5", + family: "gpt-image", + attachment: true, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-11-25", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["image"] }, + open_weights: false, + cost: { input: 5, output: 10, cache_read: 1 }, + limit: { context: 0, output: 0 }, + }, + "openai-gpt-5.3-codex": { + id: "openai-gpt-5.3-codex", + name: "GPT-5.3 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, output: 128000 }, + }, + "anthropic-claude-3.5-sonnet": { + id: "anthropic-claude-3.5-sonnet", + name: "Claude 3.5 Sonnet", + family: "claude-sonnet", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-06-20", + last_updated: "2024-10-22", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 8192 }, + status: "deprecated", + }, + "fal-ai/fast-sdxl": { + id: "fal-ai/fast-sdxl", + name: "Fast SDXL", + family: "stable-diffusion", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2023-07-26", + last_updated: "2026-04-16", + modalities: { input: ["text"], output: ["image"] }, + open_weights: true, + limit: { context: 0, output: 0 }, + }, + "fal-ai/flux/schnell": { + id: "fal-ai/flux/schnell", + name: "FLUX.1 [schnell]", + family: "flux", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-08-01", + last_updated: "2026-04-16", + modalities: { input: ["text"], output: ["image"] }, + open_weights: true, + limit: { context: 0, output: 0 }, + }, + "fal-ai/elevenlabs/tts/multilingual-v2": { + id: "fal-ai/elevenlabs/tts/multilingual-v2", + name: "ElevenLabs Multilingual TTS v2", + family: "elevenlabs", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2023-08-22", + last_updated: "2026-04-16", + modalities: { input: ["text"], output: ["audio"] }, + open_weights: false, + limit: { context: 0, output: 0 }, + }, + "fal-ai/stable-audio-25/text-to-audio": { + id: "fal-ai/stable-audio-25/text-to-audio", + name: "Stable Audio 2.5 (Text-to-Audio)", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-10-08", + last_updated: "2026-04-16", + modalities: { input: ["text"], output: ["audio"] }, + open_weights: false, + limit: { context: 0, output: 0 }, + }, + }, + }, + vultr: { + id: "vultr", + env: ["VULTR_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.vultrinference.com/v1", + name: "Vultr", + doc: "https://api.vultrinference.com/", + models: { + "MiniMax-M2.5": { + id: "MiniMax-M2.5", + name: "MiniMax M2.5", + family: "minimax", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-09", + release_date: "2025-02-11", + last_updated: "2025-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 194000, output: 4096 }, + }, + "GLM-5-FP8": { + id: "GLM-5-FP8", + name: "GLM 5 FP8", + family: "glm", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.85, output: 3.1 }, + limit: { context: 200000, output: 131072 }, + }, + "DeepSeek-V3.2": { + id: "DeepSeek-V3.2", + name: "DeepSeek V3.2", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 1.65 }, + limit: { context: 127000, output: 4096 }, + }, + "gpt-oss-120b": { + id: "gpt-oss-120b", + name: "GPT OSS 120B", + family: "gpt-oss", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-06", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 129000, output: 4096 }, + }, + "Kimi-K2.5": { + id: "Kimi-K2.5", + name: "Kimi K2 Instruct", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 2.75 }, + limit: { context: 254000, output: 32768 }, + }, + }, + }, + "alibaba-coding-plan-cn": { + id: "alibaba-coding-plan-cn", + env: ["ALIBABA_CODING_PLAN_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://coding.dashscope.aliyuncs.com/v1", + name: "Alibaba Coding Plan (China)", + doc: "https://help.aliyun.com/zh/model-studio/coding-plan", + models: { + "qwen3-coder-plus": { + id: "qwen3-coder-plus", + name: "Qwen3 Coder Plus", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 1000000, output: 65536 }, + }, + "kimi-k2.5": { + id: "kimi-k2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-01", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 262144, output: 32768 }, + }, + "glm-4.7": { + id: "glm-4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 202752, output: 16384 }, + }, + "glm-5": { + id: "glm-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 202752, output: 16384 }, + }, + "MiniMax-M2.5": { + id: "MiniMax-M2.5", + name: "MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 196608, output: 24576 }, + }, + "qwen3.6-plus": { + id: "qwen3.6-plus", + name: "Qwen3.6 Plus", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 1000000, output: 65536 }, + }, + "qwen3-max-2026-01-23": { + id: "qwen3-max-2026-01-23", + name: "Qwen3 Max", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-01-23", + last_updated: "2026-01-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 262144, output: 32768 }, + }, + "qwen3-coder-next": { + id: "qwen3-coder-next", + name: "Qwen3 Coder Next", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-03", + last_updated: "2026-02-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen3.5-plus": { + id: "qwen3.5-plus", + name: "Qwen3.5 Plus", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-16", + last_updated: "2026-02-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 1000000, output: 65536 }, + }, + }, + }, + mistral: { + id: "mistral", + env: ["MISTRAL_API_KEY"], + npm: "@ai-sdk/mistral", + name: "Mistral", + doc: "https://docs.mistral.ai/getting-started/models/", + models: { + "mistral-small-latest": { + id: "mistral-small-latest", + name: "Mistral Small (latest)", + family: "mistral-small", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2026-03-16", + last_updated: "2026-03-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 256000, output: 256000 }, + }, + "mistral-nemo": { + id: "mistral-nemo", + name: "Mistral Nemo", + family: "mistral-nemo", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2024-07-01", + last_updated: "2024-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.15 }, + limit: { context: 128000, output: 128000 }, + }, + "mistral-large-2512": { + id: "mistral-large-2512", + name: "Mistral Large 3", + family: "mistral-large", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2024-11-01", + last_updated: "2025-12-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 1.5 }, + limit: { context: 262144, output: 262144 }, + }, + "labs-devstral-small-2512": { + id: "labs-devstral-small-2512", + name: "Devstral Small 2", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-12", + release_date: "2025-12-09", + last_updated: "2025-12-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 256000, output: 256000 }, + }, + "devstral-2512": { + id: "devstral-2512", + name: "Devstral 2", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-12", + release_date: "2025-12-09", + last_updated: "2025-12-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 2 }, + limit: { context: 262144, output: 262144 }, + }, + "magistral-medium-latest": { + id: "magistral-medium-latest", + name: "Magistral Medium (latest)", + family: "magistral-medium", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-03-17", + last_updated: "2025-03-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2, output: 5 }, + limit: { context: 128000, output: 16384 }, + }, + "open-mixtral-8x7b": { + id: "open-mixtral-8x7b", + name: "Mixtral 8x7B", + family: "mixtral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-01", + release_date: "2023-12-11", + last_updated: "2023-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.7, output: 0.7 }, + limit: { context: 32000, output: 32000 }, + }, + "pixtral-large-latest": { + id: "pixtral-large-latest", + name: "Pixtral Large (latest)", + family: "pixtral", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2024-11-01", + last_updated: "2024-11-04", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 2, output: 6 }, + limit: { context: 128000, output: 128000 }, + }, + "mistral-large-2411": { + id: "mistral-large-2411", + name: "Mistral Large 2.1", + family: "mistral-large", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2024-11-01", + last_updated: "2024-11-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2, output: 6 }, + limit: { context: 131072, output: 16384 }, + }, + "codestral-latest": { + id: "codestral-latest", + name: "Codestral (latest)", + family: "codestral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-05-29", + last_updated: "2025-01-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.9 }, + limit: { context: 256000, output: 4096 }, + }, + "mistral-large-latest": { + id: "mistral-large-latest", + name: "Mistral Large (latest)", + family: "mistral-large", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2024-11-01", + last_updated: "2025-12-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 1.5 }, + limit: { context: 262144, output: 262144 }, + }, + "mistral-small-2506": { + id: "mistral-small-2506", + name: "Mistral Small 3.2", + family: "mistral-small", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-03", + release_date: "2025-06-20", + last_updated: "2025-06-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 128000, output: 16384 }, + }, + "pixtral-12b": { + id: "pixtral-12b", + name: "Pixtral 12B", + family: "pixtral", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-09", + release_date: "2024-09-01", + last_updated: "2024-09-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.15 }, + limit: { context: 128000, output: 128000 }, + }, + "ministral-8b-latest": { + id: "ministral-8b-latest", + name: "Ministral 8B (latest)", + family: "ministral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-10-01", + last_updated: "2024-10-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 128000, output: 128000 }, + }, + "mistral-embed": { + id: "mistral-embed", + name: "Mistral Embed", + family: "mistral-embed", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2023-12-11", + last_updated: "2023-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0 }, + limit: { context: 8000, output: 3072 }, + }, + "magistral-small": { + id: "magistral-small", + name: "Magistral Small", + family: "magistral-small", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-03-17", + last_updated: "2025-03-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 1.5 }, + limit: { context: 128000, output: 128000 }, + }, + "mistral-small-2603": { + id: "mistral-small-2603", + name: "Mistral Small 4", + family: "mistral-small", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2026-03-16", + last_updated: "2026-03-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 256000, output: 256000 }, + }, + "ministral-3b-latest": { + id: "ministral-3b-latest", + name: "Ministral 3B (latest)", + family: "ministral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-10-01", + last_updated: "2024-10-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.04, output: 0.04 }, + limit: { context: 128000, output: 128000 }, + }, + "open-mixtral-8x22b": { + id: "open-mixtral-8x22b", + name: "Mixtral 8x22B", + family: "mixtral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-04-17", + last_updated: "2024-04-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2, output: 6 }, + limit: { context: 64000, output: 64000 }, + }, + "mistral-medium-2604": { + id: "mistral-medium-2604", + name: "Mistral Medium 3.5", + family: "mistral-medium", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-29", + last_updated: "2026-04-29", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 1.5, output: 7.5 }, + limit: { context: 262144, output: 262144 }, + }, + "devstral-small-2505": { + id: "devstral-small-2505", + name: "Devstral Small 2505", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-05", + release_date: "2025-05-07", + last_updated: "2025-05-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 128000, output: 128000 }, + }, + "devstral-medium-2507": { + id: "devstral-medium-2507", + name: "Devstral Medium", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-05", + release_date: "2025-07-10", + last_updated: "2025-07-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 2 }, + limit: { context: 128000, output: 128000 }, + }, + "open-mistral-7b": { + id: "open-mistral-7b", + name: "Mistral 7B", + family: "mistral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2023-09-27", + last_updated: "2023-09-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 0.25 }, + limit: { context: 8000, output: 8000 }, + }, + "devstral-medium-latest": { + id: "devstral-medium-latest", + name: "Devstral 2 (latest)", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-12", + release_date: "2025-12-02", + last_updated: "2025-12-02", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 2 }, + limit: { context: 262144, output: 262144 }, + }, + "mistral-medium-2505": { + id: "mistral-medium-2505", + name: "Mistral Medium 3", + family: "mistral-medium", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-05", + release_date: "2025-05-07", + last_updated: "2025-05-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2 }, + limit: { context: 131072, output: 131072 }, + }, + "devstral-small-2507": { + id: "devstral-small-2507", + name: "Devstral Small", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-05", + release_date: "2025-07-10", + last_updated: "2025-07-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 128000, output: 128000 }, + }, + "mistral-medium-2508": { + id: "mistral-medium-2508", + name: "Mistral Medium 3.1", + family: "mistral-medium", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-05", + release_date: "2025-08-12", + last_updated: "2025-08-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2 }, + limit: { context: 262144, output: 262144 }, + }, + "mistral-medium-latest": { + id: "mistral-medium-latest", + name: "Mistral Medium (latest)", + family: "mistral-medium", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-29", + last_updated: "2026-04-29", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 1.5, output: 7.5 }, + limit: { context: 262144, output: 262144 }, + }, + }, + }, + ovhcloud: { + id: "ovhcloud", + env: ["OVHCLOUD_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://oai.endpoints.kepler.ai.cloud.ovh.net/v1", + name: "OVHcloud AI Endpoints", + doc: "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog//", + models: { + "meta-llama-3_3-70b-instruct": { + id: "meta-llama-3_3-70b-instruct", + name: "Meta-Llama-3_3-70B-Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-01", + last_updated: "2025-04-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.74, output: 0.74 }, + limit: { context: 131072, output: 131072 }, + }, + "mistral-7b-instruct-v0.3": { + id: "mistral-7b-instruct-v0.3", + name: "Mistral-7B-Instruct-v0.3", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-01", + last_updated: "2025-04-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.11, output: 0.11 }, + limit: { context: 65536, output: 65536 }, + }, + "qwen3-32b": { + id: "qwen3-32b", + name: "Qwen3-32B", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-16", + last_updated: "2025-07-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.09, output: 0.25 }, + limit: { context: 32768, output: 32768 }, + }, + "qwen2.5-vl-72b-instruct": { + id: "qwen2.5-vl-72b-instruct", + name: "Qwen2.5-VL-72B-Instruct", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-03-31", + last_updated: "2025-03-31", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 1.01, output: 1.01 }, + limit: { context: 32768, output: 32768 }, + }, + "qwen3-coder-30b-a3b-instruct": { + id: "qwen3-coder-30b-a3b-instruct", + name: "Qwen3-Coder-30B-A3B-Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-28", + last_updated: "2025-10-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.26 }, + limit: { context: 262144, output: 262144 }, + }, + "gpt-oss-20b": { + id: "gpt-oss-20b", + name: "gpt-oss-20b", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-08-28", + last_updated: "2025-08-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.18 }, + limit: { context: 131072, output: 131072 }, + }, + "mistral-small-3.2-24b-instruct-2506": { + id: "mistral-small-3.2-24b-instruct-2506", + name: "Mistral-Small-3.2-24B-Instruct-2506", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-16", + last_updated: "2025-07-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.31 }, + limit: { context: 131072, output: 131072 }, + }, + "qwen3.5-9b": { + id: "qwen3.5-9b", + name: "Qwen3.5-9B", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-15", + last_updated: "2026-02-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.15 }, + limit: { context: 262144, output: 262144 }, + }, + "gpt-oss-120b": { + id: "gpt-oss-120b", + name: "gpt-oss-120b", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + release_date: "2025-08-28", + last_updated: "2025-08-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.09, output: 0.47 }, + limit: { context: 131072, output: 131072 }, + }, + "mistral-nemo-instruct-2407": { + id: "mistral-nemo-instruct-2407", + name: "Mistral-Nemo-Instruct-2407", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-11-20", + last_updated: "2024-11-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.14 }, + limit: { context: 65536, output: 65536 }, + }, + "llama-3.1-8b-instruct": { + id: "llama-3.1-8b-instruct", + name: "Llama-3.1-8B-Instruct", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-06-11", + last_updated: "2025-06-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.11, output: 0.11 }, + limit: { context: 131072, output: 131072 }, + }, + }, + }, + friendli: { + id: "friendli", + env: ["FRIENDLI_TOKEN"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.friendli.ai/serverless/v1", + name: "Friendli", + doc: "https://friendli.ai/docs/guides/serverless_endpoints/introduction", + models: { + "Qwen/Qwen3-235B-A22B-Instruct-2507": { + id: "Qwen/Qwen3-235B-A22B-Instruct-2507", + name: "Qwen3 235B A22B Instruct 2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-29", + last_updated: "2026-01-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.8 }, + limit: { context: 262144, output: 262144 }, + }, + "zai-org/GLM-5.1": { + id: "zai-org/GLM-5.1", + name: "GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-04-07", + last_updated: "2026-04-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.4, output: 4.4, cache_read: 0.26 }, + limit: { context: 202752, output: 202752 }, + }, + "zai-org/GLM-5": { + id: "zai-org/GLM-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3.2, cache_read: 0.5 }, + limit: { context: 202752, output: 202752 }, + }, + "meta-llama/Llama-3.3-70B-Instruct": { + id: "meta-llama/Llama-3.3-70B-Instruct", + name: "Llama 3.3 70B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-08-01", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 0.6 }, + limit: { context: 131072, output: 131072 }, + }, + "meta-llama/Llama-3.1-8B-Instruct": { + id: "meta-llama/Llama-3.1-8B-Instruct", + name: "Llama 3.1 8B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-08-01", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 131072, output: 8000 }, + }, + "MiniMaxAI/MiniMax-M2.5": { + id: "MiniMaxAI/MiniMax-M2.5", + name: "MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.06 }, + limit: { context: 196608, output: 196608 }, + }, + }, + }, + cortecs: { + id: "cortecs", + env: ["CORTECS_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.cortecs.ai/v1", + name: "Cortecs", + doc: "https://api.cortecs.ai/v1/models", + models: { + "minimax-m2.7": { + id: "minimax-m2.7", + name: "MiniMax-m2.7", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.47, output: 1.4 }, + limit: { context: 202752, output: 196072 }, + }, + "claude-haiku-4-5": { + id: "claude-haiku-4-5", + name: "Claude Haiku 4.5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.09, output: 5.43 }, + limit: { context: 200000, output: 200000 }, + }, + "qwen3-235b-a22b-instruct-2507": { + id: "qwen3-235b-a22b-instruct-2507", + name: "Qwen3 235B A22B Instruct 2507", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.062, output: 0.408 }, + limit: { context: 131000, output: 131000 }, + }, + "kimi-k2.5": { + id: "kimi-k2.5", + name: "Kimi K2.5", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-01", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 2.76 }, + limit: { context: 256000, output: 256000 }, + }, + "deepseek-v3-0324": { + id: "deepseek-v3-0324", + name: "DeepSeek V3 0324", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-03-24", + last_updated: "2025-03-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.551, output: 1.654 }, + limit: { context: 128000, output: 128000 }, + }, + "glm-4.7": { + id: "glm-4.7", + name: "GLM 4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.45, output: 2.23 }, + limit: { context: 198000, output: 198000 }, + }, + "claude-opus4-7": { + id: "claude-opus4-7", + name: "Claude Opus 4.7", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5.6, output: 27.99, cache_read: 0.56, cache_write: 6.99 }, + limit: { context: 1000000, output: 128000 }, + }, + "glm-5": { + id: "glm-5", + name: "GLM 5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.08, output: 3.44 }, + limit: { context: 202752, output: 202752 }, + }, + "nova-pro-v1": { + id: "nova-pro-v1", + name: "Nova Pro 1.0", + family: "nova-pro", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-12-03", + last_updated: "2024-12-03", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.016, output: 4.061 }, + limit: { context: 300000, output: 5000 }, + }, + "devstral-2512": { + id: "devstral-2512", + name: "Devstral 2 2512", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-12", + release_date: "2025-12-09", + last_updated: "2025-12-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262000, output: 262000 }, + }, + "qwen3-32b": { + id: "qwen3-32b", + name: "Qwen3 32B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-04-29", + last_updated: "2025-04-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.099, output: 0.33 }, + limit: { context: 16384, output: 16384 }, + }, + "codestral-2508": { + id: "codestral-2508", + name: "Codestral 2508", + family: "mistral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-03", + release_date: "2025-07-30", + last_updated: "2025-07-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.9, cache_read: 0.03 }, + limit: { context: 256000, output: 256000 }, + }, + "claude-4-5-sonnet": { + id: "claude-4-5-sonnet", + name: "Claude 4.5 Sonnet", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3.259, output: 16.296 }, + limit: { context: 200000, output: 200000 }, + }, + "kimi-k2-instruct": { + id: "kimi-k2-instruct", + name: "Kimi K2 Instruct", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-07-11", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.551, output: 2.646 }, + limit: { context: 131000, output: 131000 }, + }, + "nemotron-3-super-120b-a12b": { + id: "nemotron-3-super-120b-a12b", + name: "Nemotron 3 Super 120B A12B", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-12", + release_date: "2026-03-11", + last_updated: "2026-03-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.266, output: 0.799 }, + limit: { context: 262144, output: 262144 }, + }, + "minimax-m2": { + id: "minimax-m2", + name: "MiniMax-M2", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-11", + release_date: "2025-10-27", + last_updated: "2025-10-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.39, output: 1.57 }, + limit: { context: 400000, output: 400000 }, + }, + "gemini-2.5-pro": { + id: "gemini-2.5-pro", + name: "Gemini 2.5 Pro", + family: "gemini-pro", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-20", + last_updated: "2025-06-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.654, output: 11.024 }, + limit: { context: 1048576, output: 65535 }, + }, + "claude-opus4-6": { + id: "claude-opus4-6", + name: "Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5.98, output: 29.89 }, + limit: { context: 1000000, output: 1000000 }, + }, + "devstral-small-2512": { + id: "devstral-small-2512", + name: "Devstral Small 2 2512", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-12", + release_date: "2025-12-09", + last_updated: "2025-12-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262000, output: 262000 }, + }, + "minimax-m2.1": { + id: "minimax-m2.1", + name: "MiniMax-M2.1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.34, output: 1.34 }, + limit: { context: 196000, output: 196000 }, + }, + "glm-5.1": { + id: "glm-5.1", + name: "GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-04-14", + last_updated: "2026-04-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.31, output: 4.1, cache_read: 0.24 }, + limit: { context: 204800, output: 131072 }, + }, + "glm-4.5": { + id: "glm-4.5", + name: "GLM 4.5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-29", + last_updated: "2025-07-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.67, output: 2.46 }, + limit: { context: 131072, output: 131072 }, + }, + "claude-opus4-5": { + id: "claude-opus4-5", + name: "Claude Opus 4.5", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-24", + last_updated: "2025-11-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5.98, output: 29.89 }, + limit: { context: 200000, output: 200000 }, + }, + "claude-sonnet-4": { + id: "claude-sonnet-4", + name: "Claude Sonnet 4", + family: "claude-sonnet", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-03", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3.307, output: 16.536 }, + limit: { context: 200000, output: 64000 }, + }, + "qwen3-next-80b-a3b-thinking": { + id: "qwen3-next-80b-a3b-thinking", + name: "Qwen3 Next 80B A3B Thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-11", + last_updated: "2025-09-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.164, output: 1.311 }, + limit: { context: 128000, output: 128000 }, + }, + "glm-4.5-air": { + id: "glm-4.5-air", + name: "GLM 4.5 Air", + family: "glm-air", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-08-01", + last_updated: "2025-08-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.22, output: 1.34 }, + limit: { context: 131072, output: 131072 }, + }, + "kimi-k2.6": { + id: "kimi-k2.6", + name: "Kimi K2.6", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-04-17", + last_updated: "2026-04-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.81, output: 3.54, cache_read: 0.2 }, + limit: { context: 256000, output: 256000 }, + }, + "qwen3-coder-next": { + id: "qwen3-coder-next", + name: "Qwen3 Coder Next 80B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-02-04", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.158, output: 0.84 }, + limit: { context: 256000, output: 65536 }, + }, + "claude-4-6-sonnet": { + id: "claude-4-6-sonnet", + name: "Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3.59, output: 17.92 }, + limit: { context: 1000000, output: 1000000 }, + }, + "qwen3-coder-480b-a35b-instruct": { + id: "qwen3-coder-480b-a35b-instruct", + name: "Qwen3 Coder 480B A35B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-07-25", + last_updated: "2025-07-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.441, output: 1.984 }, + limit: { context: 262000, output: 262000 }, + }, + "mixtral-8x7B-instruct-v0.1": { + id: "mixtral-8x7B-instruct-v0.1", + name: "Mixtral 8x7B Instruct v0.1", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2023-09", + release_date: "2023-12-11", + last_updated: "2023-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.438, output: 0.68 }, + limit: { context: 32000, output: 32000 }, + }, + "hermes-4-70b": { + id: "hermes-4-70b", + name: "Hermes 4 70B", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2025-08-26", + last_updated: "2025-08-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.116, output: 0.358 }, + limit: { context: 128000, output: 128000 }, + }, + "minimax-m2.5": { + id: "minimax-m2.5", + name: "MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.32, output: 1.18 }, + limit: { context: 196608, output: 196608 }, + }, + "deepseek-v3.2": { + id: "deepseek-v3.2", + name: "DeepSeek V3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.266, output: 0.444 }, + limit: { context: 163840, output: 163840 }, + }, + "intellect-3": { + id: "intellect-3", + name: "INTELLECT 3", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-11", + release_date: "2025-11-26", + last_updated: "2025-11-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.219, output: 1.202 }, + limit: { context: 128000, output: 128000 }, + }, + "glm-4.7-flash": { + id: "glm-4.7-flash", + name: "GLM-4.7-Flash", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-08-08", + last_updated: "2025-08-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.09, output: 0.53 }, + limit: { context: 203000, output: 203000 }, + }, + "gpt-oss-120b": { + id: "gpt-oss-120b", + name: "GPT Oss 120b", + family: "gpt-oss", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-01", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 128000 }, + }, + "qwen-2.5-72b-instruct": { + id: "qwen-2.5-72b-instruct", + name: "Qwen2.5 72B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-06", + release_date: "2024-09-19", + last_updated: "2024-09-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.062, output: 0.231 }, + limit: { context: 33000, output: 33000 }, + }, + "deepseek-r1-0528": { + id: "deepseek-r1-0528", + name: "DeepSeek R1 0528", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-05-28", + last_updated: "2025-05-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.585, output: 2.307 }, + limit: { context: 164000, output: 164000 }, + }, + "gpt-4.1": { + id: "gpt-4.1", + name: "GPT 4.1", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-06", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.354, output: 9.417 }, + limit: { context: 1047576, output: 32768 }, + }, + "kimi-k2-thinking": { + id: "kimi-k2-thinking", + name: "Kimi K2 Thinking", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-12", + release_date: "2025-12-08", + last_updated: "2025-12-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.656, output: 2.731 }, + limit: { context: 262000, output: 262000 }, + }, + "llama-3.1-405b-instruct": { + id: "llama-3.1-405b-instruct", + name: "Llama 3.1 405B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 128000 }, + }, + "qwen3.5-122b-a10b": { + id: "qwen3.5-122b-a10b", + name: "Qwen3.5 122B A10B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2026-01", + release_date: "2026-02-24", + last_updated: "2026-02-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.444, output: 3.106 }, + limit: { context: 262144, output: 262144 }, + }, + "llama-3.3-70b-instruct": { + id: "llama-3.3-70b-instruct", + name: "Llama 3.3 70B Instruct", + family: "llama", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.089, output: 0.275 }, + limit: { context: 131000, output: 131000 }, + }, + "mistral-large-2512": { + id: "mistral-large-2512", + name: "Mistral Large 3 2512", + family: "mistral-large", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-12", + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 1.5, cache_read: 0.05 }, + limit: { context: 256000, output: 256000 }, + }, + "qwen3.5-397b-a17b": { + id: "qwen3.5-397b-a17b", + name: "Qwen3.5 397B A17B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2026-01", + release_date: "2026-02-16", + last_updated: "2026-02-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3.6 }, + limit: { context: 250000, output: 250000 }, + }, + "deepseek-v4-flash": { + id: "deepseek-v4-flash", + name: "DeepSeek V4 Flash", + family: "deepseek-flash", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.133, output: 0.266, cache_read: 0.028 }, + limit: { context: 1048576, output: 384000 }, + }, + "deepseek-v4-pro": { + id: "deepseek-v4-pro", + name: "DeepSeek V4 Pro", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.553, output: 3.106, cache_read: 0.145 }, + limit: { context: 1048576, output: 384000 }, + }, + "qwen3-coder-30b-a3b-instruct": { + id: "qwen3-coder-30b-a3b-instruct", + name: "Qwen3 Coder 30B A3B Instruct", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-31", + last_updated: "2025-07-31", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.053, output: 0.222 }, + limit: { context: 262000, output: 262000 }, + }, + }, + }, + siliconflow: { + id: "siliconflow", + env: ["SILICONFLOW_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.siliconflow.com/v1", + name: "SiliconFlow", + doc: "https://cloud.siliconflow.com/models", + models: { + "nex-agi/DeepSeek-V3.1-Nex-N1": { + id: "nex-agi/DeepSeek-V3.1-Nex-N1", + name: "nex-agi/DeepSeek-V3.1-Nex-N1", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-01-01", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 2 }, + limit: { context: 131000, output: 131000 }, + }, + "Qwen/Qwen2.5-VL-72B-Instruct": { + id: "Qwen/Qwen2.5-VL-72B-Instruct", + name: "Qwen/Qwen2.5-VL-72B-Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-01-28", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.59, output: 0.59 }, + limit: { context: 131000, output: 4000 }, + }, + "Qwen/Qwen3-VL-32B-Thinking": { + id: "Qwen/Qwen3-VL-32B-Thinking", + name: "Qwen/Qwen3-VL-32B-Thinking", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-21", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.5 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3-30B-A3B-Thinking-2507": { + id: "Qwen/Qwen3-30B-A3B-Thinking-2507", + name: "Qwen/Qwen3-30B-A3B-Thinking-2507", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-31", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.09, output: 0.3 }, + limit: { context: 262000, output: 131000 }, + }, + "Qwen/Qwen3-VL-235B-A22B-Thinking": { + id: "Qwen/Qwen3-VL-235B-A22B-Thinking", + name: "Qwen/Qwen3-VL-235B-A22B-Thinking", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-04", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.45, output: 3.5 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen2.5-7B-Instruct": { + id: "Qwen/Qwen2.5-7B-Instruct", + name: "Qwen/Qwen2.5-7B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-09-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.05 }, + limit: { context: 33000, output: 4000 }, + }, + "Qwen/Qwen2.5-Coder-32B-Instruct": { + id: "Qwen/Qwen2.5-Coder-32B-Instruct", + name: "Qwen/Qwen2.5-Coder-32B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-11-11", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.18, output: 0.18 }, + limit: { context: 33000, output: 4000 }, + }, + "Qwen/Qwen3-VL-30B-A3B-Instruct": { + id: "Qwen/Qwen3-VL-30B-A3B-Instruct", + name: "Qwen/Qwen3-VL-30B-A3B-Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-05", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.29, output: 1 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/QwQ-32B": { + id: "Qwen/QwQ-32B", + name: "Qwen/QwQ-32B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-03-06", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.58 }, + limit: { context: 131000, output: 131000 }, + }, + "Qwen/Qwen3-235B-A22B": { + id: "Qwen/Qwen3-235B-A22B", + name: "Qwen/Qwen3-235B-A22B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-30", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.35, output: 1.42 }, + limit: { context: 131000, output: 131000 }, + }, + "Qwen/Qwen3-Omni-30B-A3B-Captioner": { + id: "Qwen/Qwen3-Omni-30B-A3B-Captioner", + name: "Qwen/Qwen3-Omni-30B-A3B-Captioner", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-04", + last_updated: "2025-11-25", + modalities: { input: ["audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 66000, output: 66000 }, + }, + "Qwen/Qwen3-VL-8B-Thinking": { + id: "Qwen/Qwen3-VL-8B-Thinking", + name: "Qwen/Qwen3-VL-8B-Thinking", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-15", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.18, output: 2 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen2.5-VL-7B-Instruct": { + id: "Qwen/Qwen2.5-VL-7B-Instruct", + name: "Qwen/Qwen2.5-VL-7B-Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-01-28", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.05 }, + limit: { context: 33000, output: 4000 }, + }, + "Qwen/Qwen3-Next-80B-A3B-Instruct": { + id: "Qwen/Qwen3-Next-80B-A3B-Instruct", + name: "Qwen/Qwen3-Next-80B-A3B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 1.4 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3-8B": { + id: "Qwen/Qwen3-8B", + name: "Qwen/Qwen3-8B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-30", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.06, output: 0.06 }, + limit: { context: 131000, output: 131000 }, + }, + "Qwen/Qwen3-30B-A3B-Instruct-2507": { + id: "Qwen/Qwen3-30B-A3B-Instruct-2507", + name: "Qwen/Qwen3-30B-A3B-Instruct-2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-30", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.09, output: 0.3 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3-235B-A22B-Instruct-2507": { + id: "Qwen/Qwen3-235B-A22B-Instruct-2507", + name: "Qwen/Qwen3-235B-A22B-Instruct-2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-23", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.09, output: 0.6 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3-32B": { + id: "Qwen/Qwen3-32B", + name: "Qwen/Qwen3-32B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-30", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.57 }, + limit: { context: 131000, output: 131000 }, + }, + "Qwen/Qwen3-Coder-30B-A3B-Instruct": { + id: "Qwen/Qwen3-Coder-30B-A3B-Instruct", + name: "Qwen/Qwen3-Coder-30B-A3B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-01", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.07, output: 0.28 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3-Omni-30B-A3B-Instruct": { + id: "Qwen/Qwen3-Omni-30B-A3B-Instruct", + name: "Qwen/Qwen3-Omni-30B-A3B-Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-04", + last_updated: "2025-11-25", + modalities: { input: ["text", "image", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 66000, output: 66000 }, + }, + "Qwen/Qwen3-14B": { + id: "Qwen/Qwen3-14B", + name: "Qwen/Qwen3-14B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-30", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.07, output: 0.28 }, + limit: { context: 131000, output: 131000 }, + }, + "Qwen/Qwen2.5-72B-Instruct-128K": { + id: "Qwen/Qwen2.5-72B-Instruct-128K", + name: "Qwen/Qwen2.5-72B-Instruct-128K", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-09-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.59, output: 0.59 }, + limit: { context: 131000, output: 4000 }, + }, + "Qwen/Qwen2.5-32B-Instruct": { + id: "Qwen/Qwen2.5-32B-Instruct", + name: "Qwen/Qwen2.5-32B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-09-19", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.18, output: 0.18 }, + limit: { context: 33000, output: 4000 }, + }, + "Qwen/Qwen3-235B-A22B-Thinking-2507": { + id: "Qwen/Qwen3-235B-A22B-Thinking-2507", + name: "Qwen/Qwen3-235B-A22B-Thinking-2507", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-28", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.13, output: 0.6 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3-Omni-30B-A3B-Thinking": { + id: "Qwen/Qwen3-Omni-30B-A3B-Thinking", + name: "Qwen/Qwen3-Omni-30B-A3B-Thinking", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-04", + last_updated: "2025-11-25", + modalities: { input: ["text", "image", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4 }, + limit: { context: 66000, output: 66000 }, + }, + "Qwen/Qwen2.5-VL-32B-Instruct": { + id: "Qwen/Qwen2.5-VL-32B-Instruct", + name: "Qwen/Qwen2.5-VL-32B-Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-03-24", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 0.27 }, + limit: { context: 131000, output: 131000 }, + }, + "Qwen/Qwen3-Next-80B-A3B-Thinking": { + id: "Qwen/Qwen3-Next-80B-A3B-Thinking", + name: "Qwen/Qwen3-Next-80B-A3B-Thinking", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-25", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.57 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3-VL-235B-A22B-Instruct": { + id: "Qwen/Qwen3-VL-235B-A22B-Instruct", + name: "Qwen/Qwen3-VL-235B-A22B-Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-04", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.5 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen2.5-14B-Instruct": { + id: "Qwen/Qwen2.5-14B-Instruct", + name: "Qwen/Qwen2.5-14B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-09-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 33000, output: 4000 }, + }, + "Qwen/Qwen3-VL-30B-A3B-Thinking": { + id: "Qwen/Qwen3-VL-30B-A3B-Thinking", + name: "Qwen/Qwen3-VL-30B-A3B-Thinking", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-11", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.29, output: 1 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3-VL-32B-Instruct": { + id: "Qwen/Qwen3-VL-32B-Instruct", + name: "Qwen/Qwen3-VL-32B-Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-21", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.6 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3-VL-8B-Instruct": { + id: "Qwen/Qwen3-VL-8B-Instruct", + name: "Qwen/Qwen3-VL-8B-Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-15", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.18, output: 0.68 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen3-Coder-480B-A35B-Instruct": { + id: "Qwen/Qwen3-Coder-480B-A35B-Instruct", + name: "Qwen/Qwen3-Coder-480B-A35B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-31", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1 }, + limit: { context: 262000, output: 262000 }, + }, + "Qwen/Qwen2.5-72B-Instruct": { + id: "Qwen/Qwen2.5-72B-Instruct", + name: "Qwen/Qwen2.5-72B-Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-09-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.59, output: 0.59 }, + limit: { context: 33000, output: 4000 }, + }, + "stepfun-ai/Step-3.5-Flash": { + id: "stepfun-ai/Step-3.5-Flash", + name: "stepfun-ai/Step-3.5-Flash", + family: "step", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 262000, output: 262000 }, + }, + "zai-org/GLM-4.5": { + id: "zai-org/GLM-4.5", + name: "zai-org/GLM-4.5", + family: "glm", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-28", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2 }, + limit: { context: 131000, output: 131000 }, + }, + "zai-org/GLM-5V-Turbo": { + id: "zai-org/GLM-5V-Turbo", + name: "zai-org/GLM-5V-Turbo", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-04-01", + last_updated: "2026-04-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.2, output: 4, cache_write: 0 }, + limit: { context: 200000, output: 131072 }, + }, + "zai-org/GLM-4.7": { + id: "zai-org/GLM-4.7", + name: "zai-org/GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 2.2 }, + limit: { context: 205000, output: 205000 }, + }, + "zai-org/GLM-5.1": { + id: "zai-org/GLM-5.1", + name: "zai-org/GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-04-08", + last_updated: "2026-04-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.4, output: 4.4, cache_write: 0 }, + limit: { context: 205000, output: 205000 }, + }, + "zai-org/GLM-4.5-Air": { + id: "zai-org/GLM-4.5-Air", + name: "zai-org/GLM-4.5-Air", + family: "glm-air", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-28", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.86 }, + limit: { context: 131000, output: 131000 }, + }, + "zai-org/GLM-5": { + id: "zai-org/GLM-5", + name: "zai-org/GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3.2 }, + limit: { context: 205000, output: 205000 }, + }, + "zai-org/GLM-4.6V": { + id: "zai-org/GLM-4.6V", + name: "zai-org/GLM-4.6V", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-12-07", + last_updated: "2025-12-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.9 }, + limit: { context: 131000, output: 131000 }, + }, + "zai-org/GLM-4.6": { + id: "zai-org/GLM-4.6", + name: "zai-org/GLM-4.6", + family: "glm", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-04", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 1.9 }, + limit: { context: 205000, output: 205000 }, + }, + "zai-org/GLM-4.5V": { + id: "zai-org/GLM-4.5V", + name: "zai-org/GLM-4.5V", + family: "glm", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-13", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.86 }, + limit: { context: 66000, output: 66000 }, + }, + "meta-llama/Meta-Llama-3.1-8B-Instruct": { + id: "meta-llama/Meta-Llama-3.1-8B-Instruct", + name: "meta-llama/Meta-Llama-3.1-8B-Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-23", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.06, output: 0.06 }, + limit: { context: 33000, output: 4000 }, + }, + "inclusionAI/Ring-flash-2.0": { + id: "inclusionAI/Ring-flash-2.0", + name: "inclusionAI/Ring-flash-2.0", + family: "ring", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-29", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.57 }, + limit: { context: 131000, output: 131000 }, + }, + "inclusionAI/Ling-mini-2.0": { + id: "inclusionAI/Ling-mini-2.0", + name: "inclusionAI/Ling-mini-2.0", + family: "ling", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-10", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.07, output: 0.28 }, + limit: { context: 131000, output: 131000 }, + }, + "inclusionAI/Ling-flash-2.0": { + id: "inclusionAI/Ling-flash-2.0", + name: "inclusionAI/Ling-flash-2.0", + family: "ling", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.57 }, + limit: { context: 131000, output: 131000 }, + }, + "tencent/Hunyuan-A13B-Instruct": { + id: "tencent/Hunyuan-A13B-Instruct", + name: "tencent/Hunyuan-A13B-Instruct", + family: "hunyuan", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-06-30", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.57 }, + limit: { context: 131000, output: 131000 }, + }, + "tencent/Hunyuan-MT-7B": { + id: "tencent/Hunyuan-MT-7B", + name: "tencent/Hunyuan-MT-7B", + family: "hunyuan", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 33000, output: 33000 }, + }, + "deepseek-ai/DeepSeek-V3.1": { + id: "deepseek-ai/DeepSeek-V3.1", + name: "deepseek-ai/DeepSeek-V3.1", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-25", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 1 }, + limit: { context: 164000, output: 164000 }, + }, + "deepseek-ai/deepseek-vl2": { + id: "deepseek-ai/deepseek-vl2", + name: "deepseek-ai/deepseek-vl2", + family: "deepseek", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-12-13", + last_updated: "2025-11-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.15 }, + limit: { context: 4000, output: 4000 }, + }, + "deepseek-ai/DeepSeek-V3": { + id: "deepseek-ai/DeepSeek-V3", + name: "deepseek-ai/DeepSeek-V3", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-12-26", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1 }, + limit: { context: 164000, output: 164000 }, + }, + "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B": { + id: "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", + name: "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-01-20", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.18, output: 0.18 }, + limit: { context: 131000, output: 131000 }, + }, + "deepseek-ai/DeepSeek-R1": { + id: "deepseek-ai/DeepSeek-R1", + name: "deepseek-ai/DeepSeek-R1", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-05-28", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 2.18 }, + limit: { context: 164000, output: 164000 }, + }, + "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B": { + id: "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B", + name: "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-01-20", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 131000, output: 131000 }, + }, + "deepseek-ai/DeepSeek-V3.2-Exp": { + id: "deepseek-ai/DeepSeek-V3.2-Exp", + name: "deepseek-ai/DeepSeek-V3.2-Exp", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-10", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 0.41 }, + limit: { context: 164000, output: 164000 }, + }, + "deepseek-ai/DeepSeek-V3.2": { + id: "deepseek-ai/DeepSeek-V3.2", + name: "deepseek-ai/DeepSeek-V3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-03", + last_updated: "2025-12-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 0.42 }, + limit: { context: 164000, output: 164000 }, + }, + "deepseek-ai/DeepSeek-V3.1-Terminus": { + id: "deepseek-ai/DeepSeek-V3.1-Terminus", + name: "deepseek-ai/DeepSeek-V3.1-Terminus", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-29", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 1 }, + limit: { context: 164000, output: 164000 }, + }, + "openai/gpt-oss-20b": { + id: "openai/gpt-oss-20b", + name: "openai/gpt-oss-20b", + family: "gpt-oss", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-13", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.04, output: 0.18 }, + limit: { context: 131000, output: 8000 }, + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "openai/gpt-oss-120b", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-13", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.45 }, + limit: { context: 131000, output: 8000 }, + }, + "baidu/ERNIE-4.5-300B-A47B": { + id: "baidu/ERNIE-4.5-300B-A47B", + name: "baidu/ERNIE-4.5-300B-A47B", + family: "ernie", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-02", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.28, output: 1.1 }, + limit: { context: 131000, output: 131000 }, + }, + "THUDM/GLM-Z1-9B-0414": { + id: "THUDM/GLM-Z1-9B-0414", + name: "THUDM/GLM-Z1-9B-0414", + family: "glm-z", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.086, output: 0.086 }, + limit: { context: 131000, output: 131000 }, + }, + "THUDM/GLM-4-9B-0414": { + id: "THUDM/GLM-4-9B-0414", + name: "THUDM/GLM-4-9B-0414", + family: "glm", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.086, output: 0.086 }, + limit: { context: 33000, output: 33000 }, + }, + "THUDM/GLM-4-32B-0414": { + id: "THUDM/GLM-4-32B-0414", + name: "THUDM/GLM-4-32B-0414", + family: "glm", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 0.27 }, + limit: { context: 33000, output: 33000 }, + }, + "THUDM/GLM-Z1-32B-0414": { + id: "THUDM/GLM-Z1-32B-0414", + name: "THUDM/GLM-Z1-32B-0414", + family: "glm-z", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-18", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.57 }, + limit: { context: 131000, output: 131000 }, + }, + "moonshotai/Kimi-K2-Thinking": { + id: "moonshotai/Kimi-K2-Thinking", + name: "moonshotai/Kimi-K2-Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-11-07", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.55, output: 2.5 }, + limit: { context: 262000, output: 262000 }, + }, + "moonshotai/Kimi-K2.6": { + id: "moonshotai/Kimi-K2.6", + name: "moonshotai/Kimi-K2.6", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4, cache_read: 0.16 }, + limit: { context: 262000, output: 262000 }, + }, + "moonshotai/Kimi-K2-Instruct": { + id: "moonshotai/Kimi-K2-Instruct", + name: "moonshotai/Kimi-K2-Instruct", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-13", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.58, output: 2.29 }, + limit: { context: 131000, output: 131000 }, + }, + "moonshotai/Kimi-K2-Instruct-0905": { + id: "moonshotai/Kimi-K2-Instruct-0905", + name: "moonshotai/Kimi-K2-Instruct-0905", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-08", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2 }, + limit: { context: 262000, output: 262000 }, + }, + "moonshotai/Kimi-K2.5": { + id: "moonshotai/Kimi-K2.5", + name: "moonshotai/Kimi-K2.5", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.45, output: 2.25 }, + limit: { context: 262000, output: 262000 }, + }, + "MiniMaxAI/MiniMax-M2.5": { + id: "MiniMaxAI/MiniMax-M2.5", + name: "MiniMaxAI/MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2026-02-15", + last_updated: "2026-02-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 197000, output: 131000 }, + }, + "MiniMaxAI/MiniMax-M2.1": { + id: "MiniMaxAI/MiniMax-M2.1", + name: "MiniMaxAI/MiniMax-M2.1", + family: "minimax", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 197000, output: 131000 }, + }, + "ByteDance-Seed/Seed-OSS-36B-Instruct": { + id: "ByteDance-Seed/Seed-OSS-36B-Instruct", + name: "ByteDance-Seed/Seed-OSS-36B-Instruct", + family: "seed", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-04", + last_updated: "2025-11-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.21, output: 0.57 }, + limit: { context: 262000, output: 262000 }, + }, + }, + }, + vercel: { + id: "vercel", + env: ["AI_GATEWAY_API_KEY"], + npm: "@ai-sdk/gateway", + name: "Vercel AI Gateway", + doc: "https://github.com/vercel/ai/tree/5eb85cc45a259553501f535b8ac79a77d0e79223/packages/gateway", + models: { + "alibaba/qwen3-coder-plus": { + id: "alibaba/qwen3-coder-plus", + name: "Qwen3 Coder Plus", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 5 }, + limit: { context: 1000000, output: 1000000 }, + }, + "alibaba/qwen3.6-27b": { + id: "alibaba/qwen3.6-27b", + name: "Qwen 3.6 27B", + family: "qwen3.6", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-22", + last_updated: "2026-05-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 3.5999999999999996 }, + limit: { context: 256000, output: 256000 }, + }, + "alibaba/qwen3-embedding-8b": { + id: "alibaba/qwen3-embedding-8b", + name: "Qwen3 Embedding 8B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-06-05", + last_updated: "2025-06-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0 }, + limit: { context: 32768, output: 32768 }, + }, + "alibaba/qwen-3-30b": { + id: "alibaba/qwen-3-30b", + name: "Qwen3-30B-A3B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.08, output: 0.29 }, + limit: { context: 40960, output: 16384 }, + }, + "alibaba/qwen-3-235b": { + id: "alibaba/qwen-3-235b", + name: "Qwen3 235B A22B Instruct 2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.13, output: 0.6 }, + limit: { context: 40960, output: 16384 }, + }, + "alibaba/qwen3.5-flash": { + id: "alibaba/qwen3.5-flash", + name: "Qwen 3.5 Flash", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-24", + last_updated: "2026-02-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.001, cache_write: 0.125 }, + limit: { context: 1000000, output: 64000 }, + }, + "alibaba/qwen3.6-plus": { + id: "alibaba/qwen3.6-plus", + name: "Qwen 3.6 Plus", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-02", + last_updated: "2026-04-03", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 3, cache_read: 0.09999999999999999, cache_write: 0.625 }, + limit: { context: 1000000, output: 64000 }, + }, + "alibaba/qwen3-max": { + id: "alibaba/qwen3-max", + name: "Qwen3 Max", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-23", + last_updated: "2025-09-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.2, output: 6 }, + limit: { context: 262144, output: 32768 }, + }, + "alibaba/qwen3-embedding-0.6b": { + id: "alibaba/qwen3-embedding-0.6b", + name: "Qwen3 Embedding 0.6B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-11-14", + last_updated: "2025-11-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.01, output: 0 }, + limit: { context: 32768, output: 32768 }, + }, + "alibaba/qwen-3-32b": { + id: "alibaba/qwen-3-32b", + name: "Qwen 3.32B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 40960, output: 16384 }, + }, + "alibaba/qwen-3.6-max-preview": { + id: "alibaba/qwen-3.6-max-preview", + name: "Qwen 3.6 Max Preview", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-20", + last_updated: "2026-04-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: true, + cost: { input: 1.3, output: 7.8, cache_read: 0.26, cache_write: 1.625 }, + limit: { context: 240000, output: 64000 }, + }, + "alibaba/qwen3-next-80b-a3b-thinking": { + id: "alibaba/qwen3-next-80b-a3b-thinking", + name: "Qwen3 Next 80B A3B Thinking", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-09", + release_date: "2025-09-12", + last_updated: "2025-09-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 1.5 }, + limit: { context: 131072, output: 65536 }, + }, + "alibaba/qwen3-vl-thinking": { + id: "alibaba/qwen3-vl-thinking", + name: "Qwen3 VL Thinking", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-09", + release_date: "2025-09-24", + last_updated: "2025-09-24", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.7, output: 8.4 }, + limit: { context: 131072, output: 129024 }, + }, + "alibaba/qwen3-235b-a22b-thinking": { + id: "alibaba/qwen3-235b-a22b-thinking", + name: "Qwen3 235B A22B Thinking 2507", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.9 }, + limit: { context: 262114, output: 262114 }, + }, + "alibaba/qwen3-next-80b-a3b-instruct": { + id: "alibaba/qwen3-next-80b-a3b-instruct", + name: "Qwen3 Next 80B A3B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-12", + last_updated: "2025-09-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.09, output: 1.1 }, + limit: { context: 262144, output: 32768 }, + }, + "alibaba/qwen3-coder-next": { + id: "alibaba/qwen3-coder-next", + name: "Qwen3 Coder Next", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-07-22", + last_updated: "2026-02-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 1.2 }, + limit: { context: 256000, output: 256000 }, + }, + "alibaba/qwen3-embedding-4b": { + id: "alibaba/qwen3-embedding-4b", + name: "Qwen3 Embedding 4B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-06-05", + last_updated: "2025-06-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.02, output: 0 }, + limit: { context: 32768, output: 32768 }, + }, + "alibaba/qwen3-max-thinking": { + id: "alibaba/qwen3-max-thinking", + name: "Qwen 3 Max Thinking", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-01", + last_updated: "2025-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.2, output: 6, cache_read: 0.24 }, + limit: { context: 256000, output: 65536 }, + }, + "alibaba/qwen3-vl-235b-a22b-instruct": { + id: "alibaba/qwen3-vl-235b-a22b-instruct", + name: "Qwen3 VL 235B A22B Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-09-24", + last_updated: "2026-05-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.39999999999999997, output: 1.5999999999999999 }, + limit: { context: 131072, output: 129024 }, + }, + "alibaba/qwen3-coder": { + id: "alibaba/qwen3-coder", + name: "Qwen3 Coder 480B A35B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.38, output: 1.53 }, + limit: { context: 262144, output: 66536 }, + }, + "alibaba/qwen3-max-preview": { + id: "alibaba/qwen3-max-preview", + name: "Qwen3 Max Preview", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-23", + last_updated: "2025-09-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.2, output: 6, cache_read: 0.24 }, + limit: { context: 262144, output: 32768 }, + }, + "alibaba/qwen3.5-plus": { + id: "alibaba/qwen3.5-plus", + name: "Qwen 3.5 Plus", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-16", + last_updated: "2026-02-19", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2.4, cache_read: 0.04, cache_write: 0.5 }, + limit: { context: 1000000, output: 64000 }, + }, + "alibaba/qwen-3-14b": { + id: "alibaba/qwen-3-14b", + name: "Qwen3-14B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.06, output: 0.24 }, + limit: { context: 40960, output: 16384 }, + }, + "alibaba/qwen3-vl-instruct": { + id: "alibaba/qwen3-vl-instruct", + name: "Qwen3 VL Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-24", + last_updated: "2025-09-24", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.7, output: 2.8 }, + limit: { context: 131072, output: 129024 }, + }, + "alibaba/qwen3-coder-30b-a3b": { + id: "alibaba/qwen3-coder-30b-a3b", + name: "Qwen 3 Coder 30B A3B Instruct", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.07, output: 0.27 }, + limit: { context: 160000, output: 32768 }, + }, + "perplexity/sonar-pro": { + id: "perplexity/sonar-pro", + name: "Sonar Pro", + family: "sonar-pro", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-09", + release_date: "2025-02-19", + last_updated: "2025-02-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 200000, output: 8000 }, + }, + "perplexity/sonar": { + id: "perplexity/sonar", + name: "Sonar", + family: "sonar", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-02", + release_date: "2025-02-19", + last_updated: "2025-02-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 1 }, + limit: { context: 127000, output: 8000 }, + }, + "perplexity/sonar-reasoning": { + id: "perplexity/sonar-reasoning", + name: "Sonar Reasoning", + family: "sonar-reasoning", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2025-09", + release_date: "2025-02-19", + last_updated: "2025-02-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5 }, + limit: { context: 127000, output: 8000 }, + }, + "perplexity/sonar-reasoning-pro": { + id: "perplexity/sonar-reasoning-pro", + name: "Sonar Reasoning Pro", + family: "sonar-reasoning", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2025-09", + release_date: "2025-02-19", + last_updated: "2025-02-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8 }, + limit: { context: 127000, output: 8000 }, + }, + "deepseek/deepseek-v3.2-thinking": { + id: "deepseek/deepseek-v3.2-thinking", + name: "DeepSeek V3.2 Thinking", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.28, output: 0.42, cache_read: 0.03 }, + limit: { context: 128000, output: 64000 }, + }, + "deepseek/deepseek-v3.2-exp": { + id: "deepseek/deepseek-v3.2-exp", + name: "DeepSeek V3.2 Exp", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-09", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 0.4 }, + limit: { context: 163840, output: 163840 }, + }, + "deepseek/deepseek-v3.1": { + id: "deepseek/deepseek-v3.1", + name: "DeepSeek-V3.1", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-08-21", + last_updated: "2025-08-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1 }, + limit: { context: 163840, output: 128000 }, + }, + "deepseek/deepseek-v4-flash": { + id: "deepseek/deepseek-v4-flash", + name: "DeepSeek V4 Flash", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-23", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.28, cache_read: 0.028 }, + limit: { context: 1000000, output: 384000 }, + }, + "deepseek/deepseek-v4-pro": { + id: "deepseek/deepseek-v4-pro", + name: "DeepSeek V4 Pro", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-23", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.74, output: 3.48, cache_read: 0.145 }, + limit: { context: 1000000, output: 384000 }, + }, + "deepseek/deepseek-v3.2": { + id: "deepseek/deepseek-v3.2", + name: "DeepSeek V3.2", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-07", + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.27, output: 0.4, cache_read: 0.22 }, + limit: { context: 163842, output: 8000 }, + }, + "deepseek/deepseek-v3": { + id: "deepseek/deepseek-v3", + name: "DeepSeek V3 0324", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2024-12-26", + last_updated: "2024-12-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.77, output: 0.77 }, + limit: { context: 163840, output: 16384 }, + }, + "deepseek/deepseek-v3.1-terminus": { + id: "deepseek/deepseek-v3.1-terminus", + name: "DeepSeek V3.1 Terminus", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-09-22", + last_updated: "2025-09-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 1 }, + limit: { context: 131072, output: 65536 }, + }, + "deepseek/deepseek-r1": { + id: "deepseek/deepseek-r1", + name: "DeepSeek-R1", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-01-20", + last_updated: "2025-05-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.35, output: 5.4 }, + limit: { context: 128000, output: 32768 }, + }, + "arcee-ai/trinity-mini": { + id: "arcee-ai/trinity-mini", + name: "Trinity Mini", + family: "trinity", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2025-12", + last_updated: "2025-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.15 }, + limit: { context: 131072, output: 131072 }, + }, + "arcee-ai/trinity-large-thinking": { + id: "arcee-ai/trinity-large-thinking", + name: "Trinity Large Thinking", + family: "trinity", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-01", + last_updated: "2026-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 0.8999999999999999 }, + limit: { context: 262100, output: 80000 }, + }, + "arcee-ai/trinity-large-preview": { + id: "arcee-ai/trinity-large-preview", + name: "Trinity Large Preview", + family: "trinity", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-01", + last_updated: "2025-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1 }, + limit: { context: 131000, output: 131000 }, + }, + "recraft/recraft-v3": { + id: "recraft/recraft-v3", + name: "Recraft V3", + family: "recraft", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-10", + last_updated: "2024-10", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 512, output: 0 }, + }, + "recraft/recraft-v2": { + id: "recraft/recraft-v2", + name: "Recraft V2", + family: "recraft", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-03", + last_updated: "2024-03", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 512, output: 0 }, + }, + "voyage/voyage-3-large": { + id: "voyage/voyage-3-large", + name: "voyage-3-large", + family: "voyage", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-09", + last_updated: "2024-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.18, output: 0 }, + limit: { context: 8192, output: 1536 }, + }, + "voyage/voyage-4-large": { + id: "voyage/voyage-4-large", + name: "voyage-4-large", + family: "voyage", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2026-03-06", + last_updated: "2026-03-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 32000, output: 0 }, + }, + "voyage/voyage-3.5-lite": { + id: "voyage/voyage-3.5-lite", + name: "voyage-3.5-lite", + family: "voyage", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-05-20", + last_updated: "2025-05-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.02, output: 0 }, + limit: { context: 8192, output: 1536 }, + }, + "voyage/voyage-code-3": { + id: "voyage/voyage-code-3", + name: "voyage-code-3", + family: "voyage", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-09", + last_updated: "2024-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.18, output: 0 }, + limit: { context: 8192, output: 1536 }, + }, + "voyage/voyage-finance-2": { + id: "voyage/voyage-finance-2", + name: "voyage-finance-2", + family: "voyage", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-03", + last_updated: "2024-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.12, output: 0 }, + limit: { context: 8192, output: 1536 }, + }, + "voyage/voyage-4-lite": { + id: "voyage/voyage-4-lite", + name: "voyage-4-lite", + family: "voyage", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2026-03-06", + last_updated: "2026-03-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 32000, output: 0 }, + }, + "voyage/voyage-4": { + id: "voyage/voyage-4", + name: "voyage-4", + family: "voyage", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2026-03-06", + last_updated: "2026-03-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 32000, output: 0 }, + }, + "voyage/voyage-code-2": { + id: "voyage/voyage-code-2", + name: "voyage-code-2", + family: "voyage", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-01", + last_updated: "2024-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.12, output: 0 }, + limit: { context: 8192, output: 1536 }, + }, + "voyage/voyage-law-2": { + id: "voyage/voyage-law-2", + name: "voyage-law-2", + family: "voyage", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-03", + last_updated: "2024-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.12, output: 0 }, + limit: { context: 8192, output: 1536 }, + }, + "voyage/voyage-3.5": { + id: "voyage/voyage-3.5", + name: "voyage-3.5", + family: "voyage", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-05-20", + last_updated: "2025-05-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.06, output: 0 }, + limit: { context: 8192, output: 1536 }, + }, + "morph/morph-v3-large": { + id: "morph/morph-v3-large", + name: "Morph v3 Large", + family: "morph", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-08-15", + last_updated: "2024-08-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.9, output: 1.9 }, + limit: { context: 32000, output: 32000 }, + }, + "morph/morph-v3-fast": { + id: "morph/morph-v3-fast", + name: "Morph v3 Fast", + family: "morph", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-08-15", + last_updated: "2024-08-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 1.2 }, + limit: { context: 16000, output: 16000 }, + }, + "zai/glm-5v-turbo": { + id: "zai/glm-5v-turbo", + name: "GLM 5V Turbo", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-01", + last_updated: "2026-04-03", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.2, output: 4, cache_read: 0.24 }, + limit: { context: 200000, output: 128000 }, + }, + "zai/glm-4.7": { + id: "zai/glm-4.7", + name: "GLM 4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.43, output: 1.75, cache_read: 0.08 }, + limit: { context: 202752, output: 120000 }, + }, + "zai/glm-5": { + id: "zai/glm-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3.2, cache_read: 0.2 }, + limit: { context: 202800, output: 131072 }, + }, + "zai/glm-4.7-flashx": { + id: "zai/glm-4.7-flashx", + name: "GLM 4.7 FlashX", + family: "glm-flash", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-01", + last_updated: "2025-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.06, output: 0.4, cache_read: 0.01 }, + limit: { context: 200000, output: 128000 }, + }, + "zai/glm-5.1": { + id: "zai/glm-5.1", + name: "GLM 5.1", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-07", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.4, output: 4.4, cache_read: 0.26 }, + limit: { context: 202752, output: 202752 }, + }, + "zai/glm-4.6v-flash": { + id: "zai/glm-4.6v-flash", + name: "GLM-4.6V-Flash", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 24000 }, + }, + "zai/glm-4.5": { + id: "zai/glm-4.5", + name: "GLM 4.5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2 }, + limit: { context: 131072, output: 131072 }, + }, + "zai/glm-4.5-air": { + id: "zai/glm-4.5-air", + name: "GLM 4.5 Air", + family: "glm-air", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 1.1 }, + limit: { context: 128000, output: 96000 }, + }, + "zai/glm-5-turbo": { + id: "zai/glm-5-turbo", + name: "GLM 5 Turbo", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-15", + last_updated: "2026-03-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.2, output: 4, cache_read: 0.24 }, + limit: { context: 202800, output: 131100 }, + }, + "zai/glm-4.5v": { + id: "zai/glm-4.5v", + name: "GLM 4.5V", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08", + release_date: "2025-08-11", + last_updated: "2025-08-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 1.8 }, + limit: { context: 66000, output: 66000 }, + }, + "zai/glm-4.6": { + id: "zai/glm-4.6", + name: "GLM 4.6", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.45, output: 1.8 }, + limit: { context: 200000, output: 96000 }, + }, + "zai/glm-4.6v": { + id: "zai/glm-4.6v", + name: "GLM-4.6V", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.9, cache_read: 0.05 }, + limit: { context: 128000, output: 24000 }, + }, + "zai/glm-4.7-flash": { + id: "zai/glm-4.7-flash", + name: "GLM 4.7 Flash", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-13", + last_updated: "2026-03-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.07, output: 0.39999999999999997 }, + limit: { context: 200000, output: 131000 }, + }, + "cohere/command-a": { + id: "cohere/command-a", + name: "Command A", + family: "command", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-03-13", + last_updated: "2025-03-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10 }, + limit: { context: 256000, output: 8000 }, + }, + "cohere/embed-v4.0": { + id: "cohere/embed-v4.0", + name: "Embed v4.0", + family: "cohere-embed", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-04-15", + last_updated: "2025-04-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.12, output: 0 }, + limit: { context: 8192, output: 1536 }, + }, + "prime-intellect/intellect-3": { + id: "prime-intellect/intellect-3", + name: "INTELLECT 3", + family: "intellect", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-11-26", + last_updated: "2025-11-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.1 }, + limit: { context: 131072, output: 131072 }, + }, + "xai/grok-4.3": { + id: "xai/grok-4.3", + name: "Grok 4.3", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-30", + last_updated: "2026-05-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 2.5, cache_read: 0.19999999999999998 }, + limit: { context: 1000000, output: 1000000 }, + }, + "xai/grok-4.20-non-reasoning": { + id: "xai/grok-4.20-non-reasoning", + name: "Grok 4.20 Non-Reasoning", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-03-09", + last_updated: "2026-03-23", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6, cache_read: 0.19999999999999998 }, + limit: { context: 2000000, output: 2000000 }, + }, + "xai/grok-4.20-non-reasoning-beta": { + id: "xai/grok-4.20-non-reasoning-beta", + name: "Grok 4.20 Beta Non-Reasoning", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-03-11", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6, cache_read: 0.19999999999999998 }, + limit: { context: 2000000, output: 2000000 }, + }, + "xai/grok-4.20-reasoning": { + id: "xai/grok-4.20-reasoning", + name: "Grok 4.20 Reasoning", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-09", + last_updated: "2026-03-23", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6, cache_read: 0.19999999999999998 }, + limit: { context: 2000000, output: 2000000 }, + }, + "xai/grok-imagine-image": { + id: "xai/grok-imagine-image", + name: "Grok Imagine Image", + family: "grok", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2026-01-28", + last_updated: "2026-02-19", + modalities: { input: ["text"], output: ["text", "image"] }, + open_weights: false, + limit: { context: 0, output: 0 }, + }, + "xai/grok-4.20-multi-agent-beta": { + id: "xai/grok-4.20-multi-agent-beta", + name: "Grok 4.20 Multi Agent Beta", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-11", + last_updated: "2026-03-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6, cache_read: 0.19999999999999998 }, + limit: { context: 2000000, output: 2000000 }, + }, + "xai/grok-imagine-image-pro": { + id: "xai/grok-imagine-image-pro", + name: "Grok Imagine Image Pro", + family: "grok", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2026-01-28", + last_updated: "2026-02-19", + modalities: { input: ["text"], output: ["text", "image"] }, + open_weights: false, + limit: { context: 0, output: 0 }, + }, + "xai/grok-4-fast-reasoning": { + id: "xai/grok-4-fast-reasoning", + name: "Grok 4 Fast Reasoning", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 256000 }, + }, + "xai/grok-4.1-fast-non-reasoning": { + id: "xai/grok-4.1-fast-non-reasoning", + name: "Grok 4.1 Fast Non-Reasoning", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + "xai/grok-4.20-reasoning-beta": { + id: "xai/grok-4.20-reasoning-beta", + name: "Grok 4.20 Beta Reasoning", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-11", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6, cache_read: 0.19999999999999998 }, + limit: { context: 2000000, output: 2000000 }, + }, + "xai/grok-4.20-multi-agent": { + id: "xai/grok-4.20-multi-agent", + name: "Grok 4.20 Multi-Agent", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-09", + last_updated: "2026-03-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6, cache_read: 0.19999999999999998 }, + limit: { context: 2000000, output: 2000000 }, + }, + "xai/grok-4.1-fast-reasoning": { + id: "xai/grok-4.1-fast-reasoning", + name: "Grok 4.1 Fast Reasoning", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + "xai/grok-4-fast-non-reasoning": { + id: "xai/grok-4-fast-non-reasoning", + name: "Grok 4 Fast (Non-Reasoning)", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-09-19", + last_updated: "2025-09-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + "xai/grok-3": { + id: "xai/grok-3", + name: "Grok 3", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.75 }, + limit: { context: 131072, output: 8192 }, + }, + "xai/grok-3-mini": { + id: "xai/grok-3-mini", + name: "Grok 3 Mini", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.5, reasoning: 0.5, cache_read: 0.075 }, + limit: { context: 131072, output: 8192 }, + }, + "xai/grok-2-vision": { + id: "xai/grok-2-vision", + name: "Grok 2 Vision", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2024-08-20", + last_updated: "2024-08-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 10, cache_read: 2 }, + limit: { context: 8192, output: 4096 }, + }, + "xai/grok-4": { + id: "xai/grok-4", + name: "Grok 4", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, reasoning: 15, cache_read: 0.75 }, + limit: { context: 256000, output: 64000 }, + }, + "xai/grok-code-fast-1": { + id: "xai/grok-code-fast-1", + name: "Grok Code Fast 1", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2025-08-28", + last_updated: "2025-08-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.5, cache_read: 0.02 }, + limit: { context: 256000, output: 10000 }, + }, + "xai/grok-3-fast": { + id: "xai/grok-3-fast", + name: "Grok 3 Fast", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 1.25 }, + limit: { context: 131072, output: 8192 }, + }, + "xai/grok-3-mini-fast": { + id: "xai/grok-3-mini-fast", + name: "Grok 3 Mini Fast", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 4, reasoning: 4, cache_read: 0.15 }, + limit: { context: 131072, output: 8192 }, + }, + "nvidia/nemotron-3-super-120b-a12b": { + id: "nvidia/nemotron-3-super-120b-a12b", + name: "NVIDIA Nemotron 3 Super 120B A12B", + family: "nemotron", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.65 }, + limit: { context: 256000, output: 32000 }, + }, + "nvidia/nemotron-3-nano-30b-a3b": { + id: "nvidia/nemotron-3-nano-30b-a3b", + name: "Nemotron 3 Nano 30B A3B", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2024-12", + last_updated: "2024-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.06, output: 0.24 }, + limit: { context: 262144, output: 262144 }, + }, + "nvidia/nemotron-nano-12b-v2-vl": { + id: "nvidia/nemotron-nano-12b-v2-vl", + name: "Nvidia Nemotron Nano 12B V2 VL", + family: "nemotron", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-12", + last_updated: "2024-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.6 }, + limit: { context: 131072, output: 131072 }, + }, + "nvidia/nemotron-nano-9b-v2": { + id: "nvidia/nemotron-nano-9b-v2", + name: "Nvidia Nemotron Nano 9B V2", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-08-18", + last_updated: "2025-08-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.04, output: 0.16 }, + limit: { context: 131072, output: 131072 }, + }, + "inception/mercury-edit-2": { + id: "inception/mercury-edit-2", + name: "Mercury Edit 2", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2026-03-30", + last_updated: "2026-03-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 0.75, cache_read: 0.025 }, + limit: { context: 128000, output: 8192 }, + }, + "inception/mercury-2": { + id: "inception/mercury-2", + name: "Mercury 2", + family: "mercury", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-24", + last_updated: "2026-03-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 0.75, cache_read: 0.024999999999999998 }, + limit: { context: 128000, output: 128000 }, + }, + "inception/mercury-coder-small": { + id: "inception/mercury-coder-small", + name: "Mercury Coder Small Beta", + family: "mercury", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-02-26", + last_updated: "2026-05-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1 }, + limit: { context: 32000, output: 16384 }, + }, + "openai/gpt-5.1-codex-max": { + id: "openai/gpt-5.1-codex-max", + name: "GPT 5.1 Codex Max", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.13 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "openai/gpt-5.2-chat": { + id: "openai/gpt-5.2-chat", + name: "GPT-5.2 Chat", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.18 }, + limit: { context: 128000, input: 111616, output: 16384 }, + }, + "openai/gpt-4o-mini-search-preview": { + id: "openai/gpt-4o-mini-search-preview", + name: "GPT 4o Mini Search Preview", + family: "gpt-mini", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + knowledge: "2023-09", + release_date: "2025-01", + last_updated: "2025-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 128000, input: 111616, output: 16384 }, + }, + "openai/codex-mini": { + id: "openai/codex-mini", + name: "Codex Mini", + family: "gpt-codex-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-05-16", + last_updated: "2025-05-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.5, output: 6, cache_read: 0.38 }, + limit: { context: 200000, input: 100000, output: 100000 }, + }, + "openai/gpt-5-chat": { + id: "openai/gpt-5-chat", + name: "GPT-5 Chat", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image", "pdf"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.13 }, + limit: { context: 128000, input: 111616, output: 16384 }, + }, + "openai/gpt-5.3-chat": { + id: "openai/gpt-5.3-chat", + name: "GPT-5.3 Chat", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-03", + last_updated: "2026-03-06", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 128000, input: 111616, output: 16384 }, + }, + "openai/gpt-5.2-pro": { + id: "openai/gpt-5.2-pro", + name: "GPT 5.2 ", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 21, output: 168 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "openai/text-embedding-3-large": { + id: "openai/text-embedding-3-large", + name: "text-embedding-3-large", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-01-25", + last_updated: "2024-01-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.13, output: 0 }, + limit: { context: 8192, input: 6656, output: 1536 }, + }, + "openai/gpt-5.5": { + id: "openai/gpt-5.5", + name: "GPT 5.5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 30, cache_read: 0.5 }, + limit: { context: 1000000, input: 872000, output: 128000 }, + }, + "openai/gpt-5.3-codex": { + id: "openai/gpt-5.3-codex", + name: "GPT 5.3 Codex", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-24", + last_updated: "2026-02-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "openai/text-embedding-ada-002": { + id: "openai/text-embedding-ada-002", + name: "text-embedding-ada-002", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2022-12-15", + last_updated: "2022-12-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0 }, + limit: { context: 8192, input: 6656, output: 1536 }, + }, + "openai/gpt-5.2": { + id: "openai/gpt-5.2", + name: "GPT-5.2", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.18 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "openai/o3-pro": { + id: "openai/o3-pro", + name: "o3 Pro", + family: "o-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-10", + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 20, output: 80 }, + limit: { context: 200000, input: 100000, output: 100000 }, + }, + "openai/gpt-5.4-mini": { + id: "openai/gpt-5.4-mini", + name: "GPT 5.4 Mini", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.75, output: 4.5, cache_read: 0.075 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "openai/gpt-5.4-nano": { + id: "openai/gpt-5.4-nano", + name: "GPT 5.4 Nano", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.19999999999999998, output: 1.25, cache_read: 0.02 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "openai/gpt-5.2-codex": { + id: "openai/gpt-5.2-codex", + name: "GPT-5.2-Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-12", + last_updated: "2025-12", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "openai/gpt-5.1-codex-mini": { + id: "openai/gpt-5.1-codex-mini", + name: "GPT-5.1 Codex mini", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-05-16", + last_updated: "2025-05-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.03 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "openai/gpt-5.1-thinking": { + id: "openai/gpt-5.1-thinking", + name: "GPT 5.1 Thinking", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-10", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image", "pdf"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.13 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "openai/gpt-5.4-pro": { + id: "openai/gpt-5.4-pro", + name: "GPT 5.4 Pro", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-05", + last_updated: "2026-03-06", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 180 }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "openai/gpt-3.5-turbo": { + id: "openai/gpt-3.5-turbo", + name: "GPT-3.5 Turbo", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2021-09", + release_date: "2023-03-01", + last_updated: "2023-03-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 1.5 }, + limit: { context: 16385, input: 12289, output: 4096 }, + }, + "openai/o3-deep-research": { + id: "openai/o3-deep-research", + name: "o3-deep-research", + family: "o", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-10", + release_date: "2024-06-26", + last_updated: "2024-06-26", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 10, output: 40, cache_read: 2.5 }, + limit: { context: 200000, input: 100000, output: 100000 }, + }, + "openai/text-embedding-3-small": { + id: "openai/text-embedding-3-small", + name: "text-embedding-3-small", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-01-25", + last_updated: "2024-01-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.02, output: 0 }, + limit: { context: 8192, input: 6656, output: 1536 }, + }, + "openai/gpt-5.4": { + id: "openai/gpt-5.4", + name: "GPT 5.4", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-05", + last_updated: "2026-03-06", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 15, cache_read: 0.25 }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "openai/gpt-oss-20b": { + id: "openai/gpt-oss-20b", + name: "GPT OSS 20B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.3 }, + limit: { context: 131072, input: 98304, output: 32768 }, + }, + "openai/gpt-5-pro": { + id: "openai/gpt-5-pro", + name: "GPT-5 pro", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image", "pdf"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 15, output: 120 }, + limit: { context: 400000, input: 128000, output: 272000 }, + }, + "openai/gpt-oss-safeguard-20b": { + id: "openai/gpt-oss-safeguard-20b", + name: "gpt-oss-safeguard-20b", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.08, output: 0.3, cache_read: 0.04 }, + limit: { context: 131072, input: 65536, output: 65536 }, + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "GPT OSS 120B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.5 }, + limit: { context: 131072, output: 131072 }, + }, + "openai/gpt-5.5-pro": { + id: "openai/gpt-5.5-pro", + name: "GPT 5.5 Pro", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 180 }, + limit: { context: 1000000, input: 872000, output: 128000 }, + }, + "openai/gpt-3.5-turbo-instruct": { + id: "openai/gpt-3.5-turbo-instruct", + name: "GPT-3.5 Turbo Instruct", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2021-09", + release_date: "2023-03-01", + last_updated: "2023-03-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.5, output: 2 }, + limit: { context: 8192, input: 4096, output: 4096 }, + }, + "openai/gpt-5.1-instant": { + id: "openai/gpt-5.1-instant", + name: "GPT-5.1 Instant", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image", "pdf"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.13 }, + limit: { context: 128000, input: 111616, output: 16384 }, + }, + "openai/gpt-5.1-codex": { + id: "openai/gpt-5.1-codex", + name: "GPT-5.1-Codex", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.13 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "openai/gpt-4.1-mini": { + id: "openai/gpt-4.1-mini", + name: "GPT-4.1 mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.6, cache_read: 0.1 }, + limit: { context: 1047576, output: 32768 }, + }, + "openai/gpt-4.1": { + id: "openai/gpt-4.1", + name: "GPT-4.1", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 1047576, output: 32768 }, + }, + "openai/gpt-5": { + id: "openai/gpt-5", + name: "GPT-5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "openai/gpt-4o": { + id: "openai/gpt-4o", + name: "GPT-4o", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-09", + release_date: "2024-05-13", + last_updated: "2024-08-06", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10, cache_read: 1.25 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/o3": { + id: "openai/o3", + name: "o3", + family: "o", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05", + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-4.1-nano": { + id: "openai/gpt-4.1-nano", + name: "GPT-4.1 nano", + family: "gpt-nano", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.03 }, + limit: { context: 1047576, output: 32768 }, + }, + "openai/gpt-5-codex": { + id: "openai/gpt-5-codex", + name: "GPT-5-Codex", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-09-15", + last_updated: "2025-09-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "openai/o3-mini": { + id: "openai/o3-mini", + name: "o3-mini", + family: "o-mini", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05", + release_date: "2024-12-20", + last_updated: "2025-01-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.55 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/o1": { + id: "openai/o1", + name: "o1", + family: "o", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2023-09", + release_date: "2024-12-05", + last_updated: "2024-12-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 60, cache_read: 7.5 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/o4-mini": { + id: "openai/o4-mini", + name: "o4-mini", + family: "o-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05", + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.28 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-4o-mini": { + id: "openai/gpt-4o-mini", + name: "GPT-4o mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-09", + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6, cache_read: 0.08 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-4-turbo": { + id: "openai/gpt-4-turbo", + name: "GPT-4 Turbo", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + knowledge: "2023-12", + release_date: "2023-11-06", + last_updated: "2024-04-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 10, output: 30 }, + limit: { context: 128000, output: 4096 }, + }, + "openai/gpt-5-nano": { + id: "openai/gpt-5-nano", + name: "GPT-5 Nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.4, cache_read: 0.005 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "openai/gpt-5-mini": { + id: "openai/gpt-5-mini", + name: "GPT-5 Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.025 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "amazon/titan-embed-text-v2": { + id: "amazon/titan-embed-text-v2", + name: "Titan Text Embeddings V2", + family: "titan-embed", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-04", + last_updated: "2024-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.02, output: 0 }, + limit: { context: 8192, output: 1536 }, + }, + "amazon/nova-2-lite": { + id: "amazon/nova-2-lite", + name: "Nova 2 Lite", + family: "nova", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2024-12-01", + last_updated: "2024-12-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5 }, + limit: { context: 1000000, output: 1000000 }, + }, + "amazon/nova-pro": { + id: "amazon/nova-pro", + name: "Nova Pro", + family: "nova-pro", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-12-03", + last_updated: "2024-12-03", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 3.2, cache_read: 0.2 }, + limit: { context: 300000, output: 8192 }, + }, + "amazon/nova-lite": { + id: "amazon/nova-lite", + name: "Nova Lite", + family: "nova-lite", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-12-03", + last_updated: "2024-12-03", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.06, output: 0.24, cache_read: 0.015 }, + limit: { context: 300000, output: 8192 }, + }, + "amazon/nova-micro": { + id: "amazon/nova-micro", + name: "Nova Micro", + family: "nova-micro", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-12-03", + last_updated: "2024-12-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.035, output: 0.14, cache_read: 0.00875 }, + limit: { context: 128000, output: 8192 }, + }, + "mistral/mistral-nemo": { + id: "mistral/mistral-nemo", + name: "Mistral Nemo", + family: "mistral-nemo", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-07-01", + last_updated: "2024-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.04, output: 0.17 }, + limit: { context: 60288, output: 16000 }, + }, + "mistral/ministral-14b": { + id: "mistral/ministral-14b", + name: "Ministral 14B", + family: "ministral", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 256000, output: 256000 }, + }, + "mistral/codestral-embed": { + id: "mistral/codestral-embed", + name: "Codestral Embed", + family: "codestral-embed", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-05-28", + last_updated: "2025-05-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0 }, + limit: { context: 8192, output: 1536 }, + }, + "mistral/mistral-medium": { + id: "mistral/mistral-medium", + name: "Mistral Medium 3.1", + family: "mistral-medium", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-05-07", + last_updated: "2025-05-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2 }, + limit: { context: 128000, output: 64000 }, + }, + "mistral/mistral-embed": { + id: "mistral/mistral-embed", + name: "Mistral Embed", + family: "mistral-embed", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2023-12-11", + last_updated: "2023-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0 }, + limit: { context: 8192, output: 1536 }, + }, + "mistral/devstral-2": { + id: "mistral/devstral-2", + name: "Devstral 2", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-12-09", + last_updated: "2025-12-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 256000, output: 256000 }, + }, + "mistral/mistral-large-3": { + id: "mistral/mistral-large-3", + name: "Mistral Large 3", + family: "mistral-large", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2025-12-02", + last_updated: "2025-12-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 1.5 }, + limit: { context: 256000, output: 256000 }, + }, + "mistral/devstral-small-2": { + id: "mistral/devstral-small-2", + name: "Devstral Small 2", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-05-07", + last_updated: "2025-05-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 256000, output: 256000 }, + }, + "mistral/devstral-small": { + id: "mistral/devstral-small", + name: "Devstral Small 1.1", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-05-07", + last_updated: "2025-05-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 128000, output: 64000 }, + }, + "mistral/ministral-8b": { + id: "mistral/ministral-8b", + name: "Ministral 8B (latest)", + family: "ministral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-10-01", + last_updated: "2024-10-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 128000, output: 128000 }, + }, + "mistral/magistral-medium": { + id: "mistral/magistral-medium", + name: "Magistral Medium (latest)", + family: "magistral-medium", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-03-17", + last_updated: "2025-03-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2, output: 5 }, + limit: { context: 128000, output: 16384 }, + }, + "mistral/mistral-small": { + id: "mistral/mistral-small", + name: "Mistral Small (latest)", + family: "mistral-small", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2026-03-16", + last_updated: "2026-03-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 256000, output: 256000 }, + }, + "mistral/magistral-small": { + id: "mistral/magistral-small", + name: "Magistral Small", + family: "magistral-small", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-06", + release_date: "2025-03-17", + last_updated: "2025-03-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 1.5 }, + limit: { context: 128000, output: 128000 }, + }, + "mistral/pixtral-12b": { + id: "mistral/pixtral-12b", + name: "Pixtral 12B", + family: "pixtral", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-09", + release_date: "2024-09-01", + last_updated: "2024-09-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.15 }, + limit: { context: 128000, output: 128000 }, + }, + "mistral/mixtral-8x22b-instruct": { + id: "mistral/mixtral-8x22b-instruct", + name: "Mixtral 8x22B", + family: "mixtral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-04-17", + last_updated: "2024-04-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2, output: 6 }, + limit: { context: 64000, output: 64000 }, + }, + "mistral/pixtral-large": { + id: "mistral/pixtral-large", + name: "Pixtral Large (latest)", + family: "pixtral", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2024-11-01", + last_updated: "2024-11-04", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 2, output: 6 }, + limit: { context: 128000, output: 128000 }, + }, + "mistral/ministral-3b": { + id: "mistral/ministral-3b", + name: "Ministral 3B (latest)", + family: "ministral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-10-01", + last_updated: "2024-10-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.04, output: 0.04 }, + limit: { context: 128000, output: 128000 }, + }, + "mistral/codestral": { + id: "mistral/codestral", + name: "Codestral (latest)", + family: "codestral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-05-29", + last_updated: "2025-01-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.9 }, + limit: { context: 256000, output: 4096 }, + }, + "meta/llama-3.2-1b": { + id: "meta/llama-3.2-1b", + name: "Llama 3.2 1B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-12", + release_date: "2024-09-18", + last_updated: "2024-09-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 128000, output: 8192 }, + }, + "meta/llama-3.1-8b": { + id: "meta/llama-3.1-8b", + name: "Llama 3.1 8B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.03, output: 0.05 }, + limit: { context: 131072, output: 16384 }, + }, + "meta/llama-3.2-90b": { + id: "meta/llama-3.2-90b", + name: "Llama 3.2 90B Vision Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-09-25", + last_updated: "2024-09-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.72, output: 0.72 }, + limit: { context: 128000, output: 8192 }, + }, + "meta/llama-3.2-3b": { + id: "meta/llama-3.2-3b", + name: "Llama 3.2 3B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-12", + release_date: "2024-09-18", + last_updated: "2024-09-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.15 }, + limit: { context: 128000, output: 8192 }, + }, + "meta/llama-3.2-11b": { + id: "meta/llama-3.2-11b", + name: "Llama 3.2 11B Vision Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-09-25", + last_updated: "2024-09-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.16, output: 0.16 }, + limit: { context: 128000, output: 8192 }, + }, + "meta/llama-3.1-70b": { + id: "meta/llama-3.1-70b", + name: "Llama 3.1 70B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 0.4 }, + limit: { context: 131072, output: 16384 }, + }, + "meta/llama-3.3-70b": { + id: "meta/llama-3.3-70b", + name: "Llama-3.3-70B-Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "meta/llama-4-maverick": { + id: "meta/llama-4-maverick", + name: "Llama-4-Maverick-17B-128E-Instruct-FP8", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "meta/llama-4-scout": { + id: "meta/llama-4-scout", + name: "Llama-4-Scout-17B-16E-Instruct-FP8", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "vercel/v0-1.5-md": { + id: "vercel/v0-1.5-md", + name: "v0-1.5-md", + family: "v0", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-06-09", + last_updated: "2025-06-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 128000, output: 32000 }, + }, + "vercel/v0-1.0-md": { + id: "vercel/v0-1.0-md", + name: "v0-1.0-md", + family: "v0", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 128000, output: 32000 }, + }, + "minimax/minimax-m2.7": { + id: "minimax/minimax-m2.7", + name: "Minimax M2.7", + family: "minimax", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.06, cache_write: 0.375 }, + limit: { context: 204800, output: 131000 }, + }, + "minimax/minimax-m2.7-highspeed": { + id: "minimax/minimax-m2.7-highspeed", + name: "MiniMax M2.7 High Speed", + family: "minimax", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.4, cache_read: 0.06, cache_write: 0.375 }, + limit: { context: 204800, output: 131100 }, + }, + "minimax/minimax-m2": { + id: "minimax/minimax-m2", + name: "MiniMax M2", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-10-27", + last_updated: "2025-10-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 1.15, cache_read: 0.03, cache_write: 0.38 }, + limit: { context: 262114, output: 262114 }, + }, + "minimax/minimax-m2.1": { + id: "minimax/minimax-m2.1", + name: "MiniMax M2.1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-10-27", + last_updated: "2025-10-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2, cache_read: 0.03, cache_write: 0.38 }, + limit: { context: 204800, output: 131072 }, + }, + "minimax/minimax-m2.1-lightning": { + id: "minimax/minimax-m2.1-lightning", + name: "MiniMax M2.1 Lightning", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-10-27", + last_updated: "2025-10-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.4, cache_read: 0.03, cache_write: 0.38 }, + limit: { context: 204800, output: 131072 }, + }, + "minimax/minimax-m2.5": { + id: "minimax/minimax-m2.5", + name: "MiniMax M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2, cache_read: 0.03, cache_write: 0.375 }, + limit: { context: 204800, output: 131000 }, + }, + "minimax/minimax-m2.5-highspeed": { + id: "minimax/minimax-m2.5-highspeed", + name: "MiniMax M2.5 High Speed", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-03-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 2.4, cache_read: 0.03, cache_write: 0.375 }, + limit: { context: 0, output: 0 }, + }, + "kwaipilot/kat-coder-pro-v1": { + id: "kwaipilot/kat-coder-pro-v1", + name: "KAT-Coder-Pro V1", + family: "kat-coder", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2025-10-24", + last_updated: "2025-10-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 256000, output: 32000 }, + }, + "kwaipilot/kat-coder-pro-v2": { + id: "kwaipilot/kat-coder-pro-v2", + name: "Kat Coder Pro V2", + family: "kat-coder", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-27", + last_updated: "2026-03-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.2, cache_read: 0.06 }, + limit: { context: 256000, output: 256000 }, + }, + "google/gemini-2.5-flash-lite-preview-09-2025": { + id: "google/gemini-2.5-flash-lite-preview-09-2025", + name: "Gemini 2.5 Flash Lite Preview 09-25", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.01 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-3.1-flash-lite-preview": { + id: "google/gemini-3.1-flash-lite-preview", + name: "Gemini 3.1 Flash Lite Preview", + family: "gemini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-03", + last_updated: "2026-03-06", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.5, cache_read: 0.025, cache_write: 1 }, + limit: { context: 1000000, output: 65000 }, + }, + "google/gemini-3-pro-image": { + id: "google/gemini-3-pro-image", + name: "Nano Banana Pro (Gemini 3 Pro Image)", + family: "gemini-pro", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-03", + release_date: "2025-09", + last_updated: "2025-09", + modalities: { input: ["text"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 2, output: 120 }, + limit: { context: 65536, output: 32768 }, + }, + "google/gemini-3.1-pro-preview": { + id: "google/gemini-3.1-pro-preview", + name: "Gemini 3.1 Pro Preview", + family: "gemini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-19", + last_updated: "2026-02-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2 }, + limit: { context: 1000000, output: 64000 }, + }, + "google/gemini-3-pro-preview": { + id: "google/gemini-3-pro-preview", + name: "Gemini 3 Pro Preview", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-11-18", + last_updated: "2025-11-18", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2, context_over_200k: { input: 4, output: 18, cache_read: 0.4 } }, + limit: { context: 1000000, output: 64000 }, + }, + "google/imagen-4.0-ultra-generate-001": { + id: "google/imagen-4.0-ultra-generate-001", + name: "Imagen 4 Ultra", + family: "imagen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-05-24", + last_updated: "2025-05-24", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 480, output: 0 }, + }, + "google/gemini-embedding-001": { + id: "google/gemini-embedding-001", + name: "Gemini Embedding 001", + family: "gemini-embedding", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-05-20", + last_updated: "2025-05-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0 }, + limit: { context: 8192, output: 1536 }, + }, + "google/gemma-4-31b-it": { + id: "google/gemma-4-31b-it", + name: "Gemma 4 31B IT", + family: "gemma", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-02", + last_updated: "2026-04-03", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.39999999999999997 }, + limit: { context: 262144, output: 131072 }, + }, + "google/gemini-2.5-flash-image": { + id: "google/gemini-2.5-flash-image", + name: "Nano Banana (Gemini 2.5 Flash Image)", + family: "gemini-flash", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-20", + last_updated: "2025-03-20", + modalities: { input: ["text"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5 }, + limit: { context: 32768, output: 32768 }, + }, + "google/text-embedding-005": { + id: "google/text-embedding-005", + name: "Text Embedding 005", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-08", + last_updated: "2024-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.03, output: 0 }, + limit: { context: 8192, output: 1536 }, + }, + "google/text-multilingual-embedding-002": { + id: "google/text-multilingual-embedding-002", + name: "Text Multilingual Embedding 002", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-03", + last_updated: "2024-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.03, output: 0 }, + limit: { context: 8192, output: 1536 }, + }, + "google/gemini-3.1-flash-image-preview": { + id: "google/gemini-3.1-flash-image-preview", + name: "Gemini 3.1 Flash Image Preview (Nano Banana 2)", + family: "gemini", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2026-02-26", + last_updated: "2026-03-06", + modalities: { input: ["text", "image"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 0.5, output: 3 }, + limit: { context: 131072, output: 32768 }, + }, + "google/gemini-3.1-flash-lite": { + id: "google/gemini-3.1-flash-lite", + name: "Gemini 3.1 Flash Lite", + family: "gemini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-05-07", + last_updated: "2026-05-08", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.5, cache_read: 0.03 }, + limit: { context: 1000000, output: 65000 }, + }, + "google/gemini-3-flash": { + id: "google/gemini-3-flash", + name: "Gemini 3 Flash", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03", + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 3, cache_read: 0.05 }, + limit: { context: 1000000, output: 64000 }, + }, + "google/imagen-4.0-generate-001": { + id: "google/imagen-4.0-generate-001", + name: "Imagen 4", + family: "imagen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 480, output: 0 }, + }, + "google/gemini-2.5-flash-preview-09-2025": { + id: "google/gemini-2.5-flash-preview-09-2025", + name: "Gemini 2.5 Flash Preview 09-25", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5, cache_read: 0.03, cache_write: 0.383 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-embedding-2": { + id: "google/gemini-embedding-2", + name: "Gemini Embedding 2", + family: "gemini-embedding", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2026-03-10", + last_updated: "2026-03-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 0, output: 0 }, + }, + "google/gemma-4-26b-a4b-it": { + id: "google/gemma-4-26b-a4b-it", + name: "Gemma 4 26B A4B IT", + family: "gemma", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-02", + last_updated: "2026-04-03", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.13, output: 0.39999999999999997 }, + limit: { context: 262144, output: 131072 }, + }, + "google/imagen-4.0-fast-generate-001": { + id: "google/imagen-4.0-fast-generate-001", + name: "Imagen 4 Fast", + family: "imagen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-06", + last_updated: "2025-06", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 480, output: 0 }, + }, + "google/gemini-2.5-flash-lite": { + id: "google/gemini-2.5-flash-lite", + name: "Gemini 2.5 Flash Lite", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.01 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-2.5-flash-image-preview": { + id: "google/gemini-2.5-flash-image-preview", + name: "Nano Banana Preview (Gemini 2.5 Flash Image Preview)", + family: "gemini-flash", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-20", + last_updated: "2025-03-20", + modalities: { input: ["text"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5 }, + limit: { context: 32768, output: 32768 }, + }, + "google/gemini-2.0-flash-lite": { + id: "google/gemini-2.0-flash-lite", + name: "Gemini 2.0 Flash Lite", + family: "gemini-flash-lite", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-06", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.075, output: 0.3 }, + limit: { context: 1048576, output: 8192 }, + }, + "google/gemini-2.5-flash": { + id: "google/gemini-2.5-flash", + name: "Gemini 2.5 Flash", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-20", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5, cache_read: 0.03, input_audio: 1 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-2.5-pro": { + id: "google/gemini-2.5-pro", + name: "Gemini 2.5 Pro", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-20", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 1.25, + output: 10, + cache_read: 0.125, + context_over_200k: { input: 2.5, output: 15, cache_read: 0.25 }, + }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-2.0-flash": { + id: "google/gemini-2.0-flash", + name: "Gemini 2.0 Flash", + family: "gemini-flash", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-06", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.025 }, + limit: { context: 1048576, output: 8192 }, + }, + "moonshotai/kimi-k2-turbo": { + id: "moonshotai/kimi-k2-turbo", + name: "Kimi K2 Turbo", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.4, output: 10 }, + limit: { context: 256000, output: 16384 }, + }, + "moonshotai/kimi-k2.5": { + id: "moonshotai/kimi-k2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-01-26", + last_updated: "2026-01-26", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 1.2 }, + limit: { context: 262144, output: 262144 }, + }, + "moonshotai/kimi-k2-thinking-turbo": { + id: "moonshotai/kimi-k2-thinking-turbo", + name: "Kimi K2 Thinking Turbo", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-11-06", + last_updated: "2025-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.15, output: 8, cache_read: 0.15 }, + limit: { context: 262114, output: 262114 }, + }, + "moonshotai/kimi-k2-0905": { + id: "moonshotai/kimi-k2-0905", + name: "Kimi K2 0905", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 2.5 }, + limit: { context: 131072, output: 16384 }, + }, + "moonshotai/kimi-k2.6": { + id: "moonshotai/kimi-k2.6", + name: "Kimi K2.6", + family: "kimi-k2.6", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-20", + last_updated: "2026-04-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4, cache_read: 0.16 }, + limit: { context: 262000, output: 262000 }, + }, + "moonshotai/kimi-k2-thinking": { + id: "moonshotai/kimi-k2-thinking", + name: "Kimi K2 Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-11-06", + last_updated: "2025-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.47, output: 2, cache_read: 0.14 }, + limit: { context: 216144, output: 216144 }, + }, + "moonshotai/kimi-k2": { + id: "moonshotai/kimi-k2", + name: "Kimi K2 Instruct", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-07-14", + last_updated: "2025-07-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3 }, + limit: { context: 131072, output: 16384 }, + status: "deprecated", + }, + "interfaze/interfaze-beta": { + id: "interfaze/interfaze-beta", + name: "Interfaze Beta", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2025-10-07", + last_updated: "2026-04-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.5, output: 3.5 }, + limit: { context: 1000000, output: 32000 }, + }, + "anthropic/claude-3.5-sonnet-20240620": { + id: "anthropic/claude-3.5-sonnet-20240620", + name: "Claude 3.5 Sonnet (2024-06-20)", + family: "claude-sonnet", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-06-20", + last_updated: "2024-06-20", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 200000, output: 8192 }, + }, + "anthropic/claude-opus-4.6": { + id: "anthropic/claude-opus-4.6", + name: "Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02", + last_updated: "2026-02", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "anthropic/claude-opus-4.7": { + id: "anthropic/claude-opus-4.7", + name: "Claude Opus 4.7", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "anthropic/claude-opus-4.5": { + id: "anthropic/claude-opus-4.5", + name: "Claude Opus 4.5", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-24", + last_updated: "2025-11-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 18.75 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-haiku-4.5": { + id: "anthropic/claude-haiku-4.5", + name: "Claude Haiku 4.5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-sonnet-4.6": { + id: "anthropic/claude-sonnet-4.6", + name: "Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-02-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 3, + output: 15, + cache_read: 0.3, + cache_write: 3.75, + context_over_200k: { input: 6, output: 22.5, cache_read: 0.6, cache_write: 7.5 }, + }, + limit: { context: 1000000, output: 128000 }, + }, + "anthropic/claude-3-opus": { + id: "anthropic/claude-3-opus", + name: "Claude Opus 3", + family: "claude-opus", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-08-31", + release_date: "2024-02-29", + last_updated: "2024-02-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 4096 }, + }, + "anthropic/claude-3.5-haiku": { + id: "anthropic/claude-3.5-haiku", + name: "Claude Haiku 3.5", + family: "claude-haiku", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07-31", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 4, cache_read: 0.08, cache_write: 1 }, + limit: { context: 200000, output: 8192 }, + }, + "anthropic/claude-opus-4": { + id: "anthropic/claude-opus-4", + name: "Claude Opus 4", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "anthropic/claude-3-haiku": { + id: "anthropic/claude-3-haiku", + name: "Claude Haiku 3", + family: "claude-haiku", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-08-31", + release_date: "2024-03-13", + last_updated: "2024-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.25, cache_read: 0.03, cache_write: 0.3 }, + limit: { context: 200000, output: 4096 }, + }, + "anthropic/claude-sonnet-4.5": { + id: "anthropic/claude-sonnet-4.5", + name: "Claude Sonnet 4.5", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-sonnet-4": { + id: "anthropic/claude-sonnet-4", + name: "Claude Sonnet 4", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-3.5-sonnet": { + id: "anthropic/claude-3.5-sonnet", + name: "Claude Sonnet 3.5 v2", + family: "claude-sonnet", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04-30", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 8192 }, + }, + "anthropic/claude-3.7-sonnet": { + id: "anthropic/claude-3.7-sonnet", + name: "Claude Sonnet 3.7", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10-31", + release_date: "2025-02-19", + last_updated: "2025-02-19", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "anthropic/claude-opus-4.1": { + id: "anthropic/claude-opus-4.1", + name: "Claude Opus 4", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "xiaomi/mimo-v2.5-pro": { + id: "xiaomi/mimo-v2.5-pro", + name: "MiMo V2.5 Pro", + family: "mimo-v2.5-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-22", + last_updated: "2026-05-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 3, cache_read: 0.19999999999999998 }, + limit: { context: 1050000, output: 131000 }, + }, + "xiaomi/mimo-v2.5": { + id: "xiaomi/mimo-v2.5", + name: "MiMo M2.5", + family: "mimo-v2.5", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-22", + last_updated: "2026-05-01", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.39999999999999997, output: 2, cache_read: 0.08 }, + limit: { context: 1050000, output: 131100 }, + }, + "xiaomi/mimo-v2-pro": { + id: "xiaomi/mimo-v2-pro", + name: "MiMo V2 Pro", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 3, cache_read: 0.19999999999999998 }, + limit: { context: 1000000, output: 128000 }, + }, + "xiaomi/mimo-v2-flash": { + id: "xiaomi/mimo-v2-flash", + name: "MiMo V2 Flash", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.29 }, + limit: { context: 262144, output: 32000 }, + }, + "bytedance/seed-1.6": { + id: "bytedance/seed-1.6", + name: "Seed 1.6", + family: "seed", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-09", + last_updated: "2025-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.05 }, + limit: { context: 256000, output: 32000 }, + }, + "bytedance/seed-1.8": { + id: "bytedance/seed-1.8", + name: "Seed 1.8", + family: "seed", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-10", + last_updated: "2025-10", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.05 }, + limit: { context: 256000, output: 64000 }, + }, + "meituan/longcat-flash-chat": { + id: "meituan/longcat-flash-chat", + name: "LongCat Flash Chat", + family: "longcat", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-08-30", + last_updated: "2025-08-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 128000, output: 8192 }, + }, + "meituan/longcat-flash-thinking": { + id: "meituan/longcat-flash-thinking", + name: "LongCat Flash Thinking", + family: "longcat", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-09-23", + last_updated: "2025-09-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 1.5 }, + limit: { context: 128000, output: 8192 }, + }, + "meituan/longcat-flash-thinking-2601": { + id: "meituan/longcat-flash-thinking-2601", + name: "LongCat Flash Thinking 2601", + family: "longcat", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + release_date: "2026-03-13", + last_updated: "2026-03-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + limit: { context: 32768, output: 32768 }, + }, + "bfl/flux-pro-1.0-fill": { + id: "bfl/flux-pro-1.0-fill", + name: "FLUX.1 Fill [pro]", + family: "flux", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-10", + last_updated: "2024-10", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 512, output: 0 }, + }, + "bfl/flux-pro-1.1": { + id: "bfl/flux-pro-1.1", + name: "FLUX1.1 [pro]", + family: "flux", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-10", + last_updated: "2024-10", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 512, output: 0 }, + }, + "bfl/flux-kontext-pro": { + id: "bfl/flux-kontext-pro", + name: "FLUX.1 Kontext Pro", + family: "flux", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-06", + last_updated: "2025-06", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 512, output: 0 }, + }, + "bfl/flux-kontext-max": { + id: "bfl/flux-kontext-max", + name: "FLUX.1 Kontext Max", + family: "flux", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-06", + last_updated: "2025-06", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 512, output: 0 }, + }, + "bfl/flux-pro-1.1-ultra": { + id: "bfl/flux-pro-1.1-ultra", + name: "FLUX1.1 [pro] Ultra", + family: "flux", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2024-11", + last_updated: "2024-11", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + limit: { context: 512, output: 0 }, + }, + }, + }, + minimax: { + id: "minimax", + env: ["MINIMAX_API_KEY"], + npm: "@ai-sdk/anthropic", + api: "https://api.minimax.io/anthropic/v1", + name: "MiniMax (minimax.io)", + doc: "https://platform.minimax.io/docs/guides/quickstart", + models: { + "MiniMax-M2": { + id: "MiniMax-M2", + name: "MiniMax-M2", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-10-27", + last_updated: "2025-10-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 196608, output: 128000 }, + }, + "MiniMax-M2.5": { + id: "MiniMax-M2.5", + name: "MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.03, cache_write: 0.375 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMax-M2.7": { + id: "MiniMax-M2.7", + name: "MiniMax-M2.7", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.06, cache_write: 0.375 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMax-M2.7-highspeed": { + id: "MiniMax-M2.7-highspeed", + name: "MiniMax-M2.7-highspeed", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.4, cache_read: 0.06, cache_write: 0.375 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMax-M2.1": { + id: "MiniMax-M2.1", + name: "MiniMax-M2.1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMax-M2.5-highspeed": { + id: "MiniMax-M2.5-highspeed", + name: "MiniMax-M2.5-highspeed", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-13", + last_updated: "2026-02-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.4, cache_read: 0.06, cache_write: 0.375 }, + limit: { context: 204800, output: 131072 }, + }, + }, + }, + llmgateway: { + id: "llmgateway", + env: ["LLMGATEWAY_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.llmgateway.io/v1", + name: "LLM Gateway", + doc: "https://llmgateway.io/docs", + models: { + "gpt-4o-mini-search-preview": { + id: "gpt-4o-mini-search-preview", + name: "GPT-4o Mini Search Preview", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2024-10-01", + last_updated: "2024-10-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 128000, output: 16384 }, + }, + "grok-4-1-fast-reasoning": { + id: "grok-4-1-fast-reasoning", + name: "Grok 4.1 Fast Reasoning", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-11-19", + last_updated: "2025-11-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + "qwen3-235b-a22b-instruct-2507": { + id: "qwen3-235b-a22b-instruct-2507", + name: "Qwen3 235B A22B Instruct (2507)", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-08", + last_updated: "2025-07-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.8, output: 2.4 }, + limit: { context: 131072, output: 8192 }, + }, + "llama-4-scout": { + id: "llama-4-scout", + name: "Llama 4 Scout", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.18, output: 0.59 }, + limit: { context: 32768, output: 16384 }, + status: "beta", + }, + "hermes-2-pro-llama-3-8b": { + id: "hermes-2-pro-llama-3-8b", + name: "Hermes 2 Pro Llama 3 8B", + family: "hermes", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2024-05-27", + last_updated: "2024-05-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.14 }, + limit: { context: 8192, output: 8192 }, + }, + "qwen-coder-plus": { + id: "qwen-coder-plus", + name: "Qwen Coder Plus", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-09-18", + last_updated: "2024-09-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 1 }, + limit: { context: 131072, output: 8192 }, + }, + auto: { + id: "auto", + name: "Auto Route", + family: "auto", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-01-01", + last_updated: "2024-01-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "glm-4.6v-flashx": { + id: "glm-4.6v-flashx", + name: "GLM-4.6V FlashX", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-08", + last_updated: "2025-12-08", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.04, output: 0.4, cache_read: 0 }, + limit: { context: 128000, output: 16000 }, + }, + "gemma-2-27b-it-together": { + id: "gemma-2-27b-it-together", + name: "Gemma 2 27B IT", + family: "gemma", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2024-06-27", + last_updated: "2024-06-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.08, output: 0.08 }, + limit: { context: 8192, output: 16384 }, + }, + "codestral-2508": { + id: "codestral-2508", + name: "Codestral", + family: "mistral", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-07-30", + last_updated: "2025-07-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.9 }, + limit: { context: 256000, output: 16384 }, + }, + "gemma-3-1b-it": { + id: "gemma-3-1b-it", + name: "Gemma 3 1B IT", + family: "gemma", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-03-12", + last_updated: "2025-03-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.08, output: 0.3 }, + limit: { context: 1000000, output: 16384 }, + }, + "glm-4-32b-0414-128k": { + id: "glm-4-32b-0414-128k", + name: "GLM-4 32B (0414-128k)", + family: "glm", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 128000, output: 16384 }, + }, + "seed-1-6-flash-250715": { + id: "seed-1-6-flash-250715", + name: "Seed 1.6 Flash (250715)", + family: "seed", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-26", + last_updated: "2025-07-26", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.3, cache_read: 0.01 }, + limit: { context: 256000, output: 8192 }, + }, + "seed-1-6-250615": { + id: "seed-1-6-250615", + name: "Seed 1.6 (250615)", + family: "seed", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-06-25", + last_updated: "2025-06-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 2, cache_read: 0.05 }, + limit: { context: 256000, output: 8192 }, + }, + "qwen3-vl-235b-a22b-thinking": { + id: "qwen3-vl-235b-a22b-thinking", + name: "Qwen3 VL 235B A22B Thinking", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-15", + last_updated: "2025-09-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.8, output: 2.4 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen3-vl-30b-a3b-thinking": { + id: "qwen3-vl-30b-a3b-thinking", + name: "Qwen3 VL 30B A3B Thinking", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-02", + last_updated: "2025-10-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen2-5-vl-32b-instruct": { + id: "qwen2-5-vl-32b-instruct", + name: "Qwen2.5 VL 32B Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-03-15", + last_updated: "2025-03-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.3 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen3-vl-8b-instruct": { + id: "qwen3-vl-8b-instruct", + name: "Qwen3 VL 8B Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-08-19", + last_updated: "2025-08-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 131072, output: 8192 }, + }, + "claude-3-7-sonnet": { + id: "claude-3-7-sonnet", + name: "Claude 3.7 Sonnet", + family: "claude", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-02-24", + last_updated: "2025-02-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3 }, + limit: { context: 200000, output: 8192 }, + }, + "gemini-pro-latest": { + id: "gemini-pro-latest", + name: "Gemini Pro Latest", + family: "gemini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-27", + last_updated: "2026-02-27", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2 }, + limit: { context: 1048576, output: 65536 }, + }, + "claude-3-5-haiku": { + id: "claude-3-5-haiku", + name: "Claude 3.5 Haiku", + family: "claude", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 4, cache_read: 0.08 }, + limit: { context: 200000, output: 8192 }, + status: "deprecated", + }, + "qwen-max-latest": { + id: "qwen-max-latest", + name: "Qwen Max Latest", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-01-25", + last_updated: "2025-01-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.6, output: 6.4 }, + limit: { context: 32768, output: 8192 }, + }, + "glm-4.6v-flash": { + id: "glm-4.6v-flash", + name: "GLM-4.6V Flash", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-08", + last_updated: "2025-12-08", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 16000 }, + status: "beta", + }, + "qwen3-30b-a3b-instruct-2507": { + id: "qwen3-30b-a3b-instruct-2507", + name: "Qwen3 30B A3B Instruct (2507)", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-08", + last_updated: "2025-07-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 131072, output: 8192 }, + }, + "minimax-text-01": { + id: "minimax-text-01", + name: "MiniMax Text 01", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-01-15", + last_updated: "2025-01-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 1.1 }, + limit: { context: 1000000, output: 131072 }, + }, + "qwen3-32b-fp8": { + id: "qwen3-32b-fp8", + name: "Qwen3 32B FP8", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-28", + last_updated: "2025-04-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 131072, output: 8192 }, + }, + "llama-4-scout-17b-instruct": { + id: "llama-4-scout-17b-instruct", + name: "Llama 4 Scout 17B Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.17, output: 0.66 }, + limit: { context: 8192, output: 2048 }, + }, + "qwen3-4b-fp8": { + id: "qwen3-4b-fp8", + name: "Qwen3 4B FP8", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-28", + last_updated: "2025-04-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.05 }, + limit: { context: 131072, output: 8192 }, + }, + "ministral-8b-2512": { + id: "ministral-8b-2512", + name: "Ministral 8B", + family: "mistral", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-12-02", + last_updated: "2025-12-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.15 }, + limit: { context: 262144, output: 8192 }, + }, + "gemma-3-27b": { + id: "gemma-3-27b", + name: "Gemma 3 27B", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-03-12", + last_updated: "2025-03-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 0.27 }, + limit: { context: 128000, output: 16384 }, + }, + "qwen3-vl-flash": { + id: "qwen3-vl-flash", + name: "Qwen3 VL Flash", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-09", + last_updated: "2025-10-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.4, cache_read: 0.01 }, + limit: { context: 1000000, output: 32000 }, + }, + "llama-3.1-70b-instruct": { + id: "llama-3.1-70b-instruct", + name: "Llama 3.1 70B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.72, output: 0.72 }, + limit: { context: 128000, output: 2048 }, + status: "beta", + }, + "seed-1-8-251228": { + id: "seed-1-8-251228", + name: "Seed 1.8 (251228)", + family: "seed", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-18", + last_updated: "2025-12-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 2, cache_read: 0.05 }, + limit: { context: 256000, output: 8192 }, + }, + "qwen3-235b-a22b-thinking-2507": { + id: "qwen3-235b-a22b-thinking-2507", + name: "Qwen3 235B A22B Thinking (2507)", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-08", + last_updated: "2025-07-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.8, output: 2.4 }, + limit: { context: 131072, output: 8192 }, + }, + "seed-1-6-250915": { + id: "seed-1-6-250915", + name: "Seed 1.6 (250915)", + family: "seed", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-15", + last_updated: "2025-09-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 2, cache_read: 0.05 }, + limit: { context: 256000, output: 8192 }, + }, + "glm-4.5-x": { + id: "glm-4.5-x", + name: "GLM-4.5 X", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2.2, output: 8.9, cache_read: 0.45 }, + limit: { context: 128000, output: 16384 }, + status: "beta", + }, + "qwen3-30b-a3b-thinking-2507": { + id: "qwen3-30b-a3b-thinking-2507", + name: "Qwen3 30B A3B Thinking (2507)", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-08", + last_updated: "2025-07-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 131072, output: 8192 }, + }, + "grok-4-fast-reasoning": { + id: "grok-4-fast-reasoning", + name: "Grok 4 Fast Reasoning", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + "deepseek-v3.1": { + id: "deepseek-v3.1", + name: "DeepSeek V3.1", + family: "deepseek", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-21", + last_updated: "2025-08-21", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.56, output: 1.68, cache_read: 0.11 }, + limit: { context: 128000, output: 32768 }, + }, + "ministral-3b-2512": { + id: "ministral-3b-2512", + name: "Ministral 3B", + family: "mistral", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-12-02", + last_updated: "2025-12-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 131072, output: 8192 }, + }, + "qwen-plus-latest": { + id: "qwen-plus-latest", + name: "Qwen Plus Latest", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-01-25", + last_updated: "2025-01-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.9 }, + limit: { context: 131072, output: 8192 }, + }, + "llama-3.1-nemotron-ultra-253b": { + id: "llama-3.1-nemotron-ultra-253b", + name: "Llama 3.1 Nemotron Ultra 253B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-04-07", + last_updated: "2025-04-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 1.8 }, + limit: { context: 128000, output: 8192 }, + }, + "llama-4-maverick-17b-instruct": { + id: "llama-4-maverick-17b-instruct", + name: "Llama 4 Maverick 17B Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.24, output: 0.97 }, + limit: { context: 8192, output: 2048 }, + }, + "grok-4-0709": { + id: "grok-4-0709", + name: "Grok 4 (0709)", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 256000, output: 256000 }, + }, + "qwen3-30b-a3b-fp8": { + id: "qwen3-30b-a3b-fp8", + name: "Qwen3 30B A3B FP8", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-28", + last_updated: "2025-04-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 131072, output: 8192 }, + }, + "minimax-m2.1-lightning": { + id: "minimax-m2.1-lightning", + name: "MiniMax M2.1 Lightning", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.12, output: 0.48 }, + limit: { context: 196608, output: 131072 }, + }, + "qwen3-max-2026-01-23": { + id: "qwen3-max-2026-01-23", + name: "Qwen3 Max (2026-01-23)", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-01-23", + last_updated: "2026-01-23", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.6 }, + limit: { context: 256000, output: 32800 }, + }, + "llama-3.2-3b-instruct": { + id: "llama-3.2-3b-instruct", + name: "Llama 3.2 3B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2024-09-18", + last_updated: "2024-09-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.05 }, + limit: { context: 32768, output: 32000 }, + }, + "qwen3-coder-next": { + id: "qwen3-coder-next", + name: "Qwen3 Coder Next", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 4 }, + limit: { context: 262144, output: 65536 }, + }, + "gpt-4o-search-preview": { + id: "gpt-4o-search-preview", + name: "GPT-4o Search Preview", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2024-10-01", + last_updated: "2024-10-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10 }, + limit: { context: 128000, output: 16384 }, + }, + custom: { + id: "custom", + name: "Custom Model", + family: "auto", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-01-01", + last_updated: "2024-01-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "qwen3-vl-30b-a3b-instruct": { + id: "qwen3-vl-30b-a3b-instruct", + name: "Qwen3 VL 30B A3B Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-10-02", + last_updated: "2025-10-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 131072, output: 8192 }, + }, + "deepseek-v3.2": { + id: "deepseek-v3.2", + name: "DeepSeek V3.2", + family: "deepseek", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.28, output: 0.42, cache_read: 0.03 }, + limit: { context: 163840, output: 16384 }, + }, + "qwen3-235b-a22b-fp8": { + id: "qwen3-235b-a22b-fp8", + name: "Qwen3 235B A22B FP8", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-04-28", + last_updated: "2025-04-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 2.5 }, + limit: { context: 131072, output: 8192 }, + }, + "gpt-oss-20b": { + id: "gpt-oss-20b", + name: "GPT OSS 20B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.5 }, + limit: { context: 131072, output: 32766 }, + }, + "kimi-k2": { + id: "kimi-k2", + name: "Kimi K2", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-11", + last_updated: "2025-07-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3, cache_read: 0.5 }, + limit: { context: 131072, output: 16384 }, + }, + "llama-3-8b-instruct": { + id: "llama-3-8b-instruct", + name: "Llama 3 8B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-04-03", + last_updated: "2025-04-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.04, output: 0.04 }, + limit: { context: 8192, output: 8192 }, + }, + "qwen3-vl-235b-a22b-instruct": { + id: "qwen3-vl-235b-a22b-instruct", + name: "Qwen3 VL 235B A22B Instruct", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-09-15", + last_updated: "2025-09-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.8, output: 2.4 }, + limit: { context: 131072, output: 8192 }, + }, + "gpt-oss-120b": { + id: "gpt-oss-120b", + name: "GPT OSS 120B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.75 }, + limit: { context: 131072, output: 32766 }, + }, + "qwen25-coder-7b": { + id: "qwen25-coder-7b", + name: "Qwen2.5 Coder 7B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2024-09-19", + last_updated: "2024-09-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.05 }, + limit: { context: 131072, output: 8192 }, + }, + "llama-3.1-8b-instruct": { + id: "llama-3.1-8b-instruct", + name: "Llama 3.1 8B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.22, output: 0.22 }, + limit: { context: 128000, output: 2048 }, + status: "beta", + }, + "llama-3-70b-instruct": { + id: "llama-3-70b-instruct", + name: "Llama 3 70B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2024-04-18", + last_updated: "2024-04-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.51, output: 0.74 }, + limit: { context: 8192, output: 8000 }, + }, + "deepseek-r1-0528": { + id: "deepseek-r1-0528", + name: "DeepSeek R1 (0528)", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-05-28", + last_updated: "2025-05-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.8, output: 2.4 }, + limit: { context: 64000, output: 16384 }, + status: "beta", + }, + "glm-4.5-airx": { + id: "glm-4.5-airx", + name: "GLM-4.5 AirX", + family: "glm", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.5, cache_read: 0.22 }, + limit: { context: 128000, output: 16384 }, + }, + "ministral-14b-2512": { + id: "ministral-14b-2512", + name: "Ministral 14B", + family: "mistral", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2025-12-02", + last_updated: "2025-12-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 262144, output: 8192 }, + }, + "llama-3.2-11b-instruct": { + id: "llama-3.2-11b-instruct", + name: "Llama 3.2 11B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + release_date: "2024-09-25", + last_updated: "2024-09-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.33 }, + limit: { context: 128000, output: 8192 }, + }, + "claude-3-opus": { + id: "claude-3-opus", + name: "Claude 3 Opus", + family: "claude", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2024-03-04", + last_updated: "2024-03-04", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5 }, + limit: { context: 200000, output: 4096 }, + }, + "minimax-m2.7": { + id: "minimax-m2.7", + name: "MiniMax-M2.7", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.06, cache_write: 0.375 }, + limit: { context: 204800, output: 131072 }, + }, + "grok-4-20-beta-0309-non-reasoning": { + id: "grok-4-20-beta-0309-non-reasoning", + name: "Grok 4.20 (Non-Reasoning)", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-03-09", + last_updated: "2026-03-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6, cache_read: 0.2, context_over_200k: { input: 4, output: 12, cache_read: 0.4 } }, + limit: { context: 2000000, output: 30000 }, + }, + "qwen3-coder-plus": { + id: "qwen3-coder-plus", + name: "Qwen3 Coder Plus", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 5 }, + limit: { context: 1048576, output: 65536 }, + }, + "claude-haiku-4-5": { + id: "claude-haiku-4-5", + name: "Claude Haiku 4.5 (latest)", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + }, + "claude-opus-4-5-20251101": { + id: "claude-opus-4-5-20251101", + name: "Claude Opus 4.5", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-01", + last_updated: "2025-11-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 64000 }, + }, + "gemini-2.5-flash-lite-preview-09-2025": { + id: "gemini-2.5-flash-lite-preview-09-2025", + name: "Gemini 2.5 Flash Lite Preview 09-25", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.025 }, + limit: { context: 1048576, output: 65536 }, + }, + "kimi-k2.5": { + id: "kimi-k2.5", + name: "Kimi K2.5", + family: "kimi-k2.5", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: false, + knowledge: "2025-01", + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3, cache_read: 0.1 }, + limit: { context: 262144, output: 262144 }, + }, + "llama-3.3-70b-instruct": { + id: "llama-3.3-70b-instruct", + name: "Llama-3.3-70B-Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "mistral-large-2512": { + id: "mistral-large-2512", + name: "Mistral Large 3", + family: "mistral-large", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2024-11-01", + last_updated: "2025-12-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 1.5 }, + limit: { context: 262144, output: 262144 }, + }, + "glm-4.7": { + id: "glm-4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2, cache_read: 0.11, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "minimax-m2.7-highspeed": { + id: "minimax-m2.7-highspeed", + name: "MiniMax-M2.7-highspeed", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.4, cache_read: 0.06, cache_write: 0.375 }, + limit: { context: 204800, output: 131072 }, + }, + "mimo-v2.5-pro": { + id: "mimo-v2.5-pro", + name: "MiMo-V2.5-Pro", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3, cache_read: 0.2, context_over_200k: { input: 2, output: 6, cache_read: 0.4 } }, + limit: { context: 1048576, output: 131072 }, + }, + "gemma-3n-e4b-it": { + id: "gemma-3n-e4b-it", + name: "Gemma 3n 4B", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2025-05-20", + last_updated: "2025-05-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 8192, output: 2000 }, + }, + "claude-3-5-sonnet-20241022": { + id: "claude-3-5-sonnet-20241022", + name: "Claude Sonnet 3.5 v2", + family: "claude-sonnet", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04-30", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 8192 }, + }, + "gpt-5.2-pro": { + id: "gpt-5.2-pro", + name: "GPT-5.2 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 21, output: 168 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "qwq-plus": { + id: "qwq-plus", + name: "QwQ Plus", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-03-05", + last_updated: "2025-03-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 2.4 }, + limit: { context: 131072, output: 8192 }, + }, + "gemini-3.1-flash-lite-preview": { + id: "gemini-3.1-flash-lite-preview", + name: "Gemini 3.1 Flash Lite Preview", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-03-03", + last_updated: "2026-03-03", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.5, cache_read: 0.025, cache_write: 1 }, + limit: { context: 1048576, output: 65536 }, + }, + "qwen-vl-plus": { + id: "qwen-vl-plus", + name: "Qwen-VL Plus", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-01-25", + last_updated: "2025-08-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.21, output: 0.63 }, + limit: { context: 131072, output: 8192 }, + }, + "glm-5": { + id: "glm-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3.2, cache_read: 0.2, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "devstral-2512": { + id: "devstral-2512", + name: "Devstral 2", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-12", + release_date: "2025-12-09", + last_updated: "2025-12-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 2 }, + limit: { context: 262144, output: 262144 }, + }, + "qwen3-32b": { + id: "qwen3-32b", + name: "Qwen3 32B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.7, output: 2.8, reasoning: 8.4 }, + limit: { context: 131072, output: 16384 }, + }, + "claude-sonnet-4-6": { + id: "claude-sonnet-4-6", + name: "Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 1000000, output: 64000 }, + }, + "glm-4.7-flashx": { + id: "glm-4.7-flashx", + name: "GLM-4.7-FlashX", + family: "glm-flash", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-01-19", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.4, cache_read: 0.01, cache_write: 0 }, + limit: { context: 200000, output: 131072 }, + }, + "gemini-3.1-pro-preview": { + id: "gemini-3.1-pro-preview", + name: "Gemini 3.1 Pro Preview", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-19", + last_updated: "2026-02-19", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2, context_over_200k: { input: 4, output: 18, cache_read: 0.4 } }, + limit: { context: 1048576, output: 65536 }, + }, + "qwen35-397b-a17b": { + id: "qwen35-397b-a17b", + name: "Qwen3.5 397B-A17B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-02-15", + last_updated: "2026-02-15", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3.6 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen-max": { + id: "qwen-max", + name: "Qwen Max", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-04-03", + last_updated: "2025-01-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.6, output: 6.4 }, + limit: { context: 32768, output: 8192 }, + }, + "gpt-5.3-chat-latest": { + id: "gpt-5.3-chat-latest", + name: "GPT-5.3 Chat (latest)", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-03-03", + last_updated: "2026-03-03", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 128000, output: 16384 }, + }, + "gemini-2.0-flash": { + id: "gemini-2.0-flash", + name: "Gemini 2.0 Flash", + family: "gemini-flash", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-06", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.025 }, + limit: { context: 1048576, output: 8192 }, + }, + "gemini-3-flash-preview": { + id: "gemini-3-flash-preview", + name: "Gemini 3 Flash Preview", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 0.5, + output: 3, + cache_read: 0.05, + context_over_200k: { input: 0.5, output: 3, cache_read: 0.05 }, + }, + limit: { context: 1048576, output: 65536 }, + }, + "qwen-plus": { + id: "qwen-plus", + name: "Qwen Plus", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-01-25", + last_updated: "2025-09-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.2, reasoning: 4 }, + limit: { context: 1000000, output: 32768 }, + }, + "gpt-5.5": { + id: "gpt-5.5", + name: "GPT-5.5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-12-01", + release_date: "2026-04-23", + last_updated: "2026-04-23", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 30, cache_read: 0.5, context_over_200k: { input: 10, output: 45, cache_read: 1 } }, + limit: { context: 1050000, input: 922000, output: 128000 }, + experimental: { + modes: { + fast: { + cost: { input: 12.5, output: 75, cache_read: 1.25 }, + provider: { body: { service_tier: "priority" } }, + }, + }, + }, + }, + "qwen3.6-35b-a3b": { + id: "qwen3.6-35b-a3b", + name: "Qwen3.6 35B-A3B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-17", + last_updated: "2026-04-17", + modalities: { input: ["text", "image", "video", "audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0.248, output: 1.485 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen-omni-turbo": { + id: "qwen-omni-turbo", + name: "Qwen-Omni Turbo", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-01-19", + last_updated: "2025-03-26", + modalities: { input: ["text", "image", "audio", "video"], output: ["text", "audio"] }, + open_weights: false, + cost: { input: 0.07, output: 0.27, input_audio: 4.44, output_audio: 8.89 }, + limit: { context: 32768, output: 2048 }, + }, + "claude-opus-4-7": { + id: "claude-opus-4-7", + name: "Claude Opus 4.7", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "gpt-5-mini": { + id: "gpt-5-mini", + name: "GPT-5 Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.025 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gpt-5-nano": { + id: "gpt-5-nano", + name: "GPT-5 Nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.4, cache_read: 0.005 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "mimo-v2-omni": { + id: "mimo-v2-omni", + name: "MiMo-V2-Omni", + family: "mimo", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2, cache_read: 0.08 }, + limit: { context: 262144, output: 131072 }, + }, + "gpt-5.3-codex": { + id: "gpt-5.3-codex", + name: "GPT-5.3 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "minimax-m2": { + id: "minimax-m2", + name: "MiniMax-M2", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-10-27", + last_updated: "2025-10-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 196608, output: 128000 }, + }, + "claude-sonnet-4-5-20250929": { + id: "claude-sonnet-4-5-20250929", + name: "Claude Sonnet 4.5", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "qwen-flash": { + id: "qwen-flash", + name: "Qwen Flash", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.4 }, + limit: { context: 1000000, output: 32768 }, + }, + "gpt-4-turbo": { + id: "gpt-4-turbo", + name: "GPT-4 Turbo", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + knowledge: "2023-12", + release_date: "2023-11-06", + last_updated: "2024-04-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 10, output: 30 }, + limit: { context: 128000, output: 4096 }, + }, + "gemini-2.5-pro": { + id: "gemini-2.5-pro", + name: "Gemini 2.5 Pro", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-20", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 1.25, + output: 10, + cache_read: 0.125, + context_over_200k: { input: 2.5, output: 15, cache_read: 0.25 }, + }, + limit: { context: 1048576, output: 65536 }, + }, + "mimo-v2.5": { + id: "mimo-v2.5", + name: "MiMo-V2.5", + family: "mimo", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: true, + cost: { + input: 0.4, + output: 2, + cache_read: 0.08, + context_over_200k: { input: 0.8, output: 4, cache_read: 0.16 }, + }, + limit: { context: 1048576, output: 131072 }, + }, + "grok-4-1-fast-non-reasoning": { + id: "grok-4-1-fast-non-reasoning", + name: "Grok 4.1 Fast (Non-Reasoning)", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-11-19", + last_updated: "2025-11-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + "sonar-pro": { + id: "sonar-pro", + name: "Sonar Pro", + family: "sonar-pro", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-09-01", + release_date: "2024-01-01", + last_updated: "2025-09-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15 }, + limit: { context: 200000, output: 8192 }, + }, + "pixtral-large-latest": { + id: "pixtral-large-latest", + name: "Pixtral Large (latest)", + family: "pixtral", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2024-11-01", + last_updated: "2024-11-04", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 2, output: 6 }, + limit: { context: 128000, output: 128000 }, + }, + "gpt-5.2": { + id: "gpt-5.2", + name: "GPT-5.2", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "grok-4-20-beta-0309-reasoning": { + id: "grok-4-20-beta-0309-reasoning", + name: "Grok 4.20 (Reasoning)", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-09", + last_updated: "2026-03-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6, cache_read: 0.2, context_over_200k: { input: 4, output: 12, cache_read: 0.4 } }, + limit: { context: 2000000, output: 30000 }, + }, + "gpt-4o-mini": { + id: "gpt-4o-mini", + name: "GPT-4o mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-09", + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6, cache_read: 0.08 }, + limit: { context: 128000, output: 16384 }, + }, + "qwen3.6-plus": { + id: "qwen3.6-plus", + name: "Qwen3.6 Plus", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.276, output: 1.651, cache_read: 0.028, cache_write: 0.344 }, + limit: { context: 1000000, output: 65536 }, + }, + "gpt-5.4-mini": { + id: "gpt-5.4-mini", + name: "GPT-5.4 mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.75, output: 4.5, cache_read: 0.075 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "qwen3-max": { + id: "qwen3-max", + name: "Qwen3 Max", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-23", + last_updated: "2025-09-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.2, output: 6 }, + limit: { context: 262144, output: 65536 }, + }, + "minimax-m2.1": { + id: "minimax-m2.1", + name: "MiniMax-M2.1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 204800, output: 131072 }, + }, + "glm-5.1": { + id: "glm-5.1", + name: "GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-03-27", + last_updated: "2026-03-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 6, output: 24, cache_read: 1.3, cache_write: 0 }, + limit: { context: 200000, output: 131072 }, + }, + "o4-mini": { + id: "o4-mini", + name: "o4-mini", + family: "o-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05", + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.28 }, + limit: { context: 200000, output: 100000 }, + }, + "gpt-5.4-nano": { + id: "gpt-5.4-nano", + name: "GPT-5.4 nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.25, cache_read: 0.02 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "glm-4.5": { + id: "glm-4.5", + name: "GLM-4.5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2, cache_read: 0.11, cache_write: 0 }, + limit: { context: 131072, output: 98304 }, + }, + "mistral-large-latest": { + id: "mistral-large-latest", + name: "Mistral Large (latest)", + family: "mistral-large", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2024-11-01", + last_updated: "2025-12-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 1.5 }, + limit: { context: 262144, output: 262144 }, + }, + "mistral-small-2506": { + id: "mistral-small-2506", + name: "Mistral Small 3.2", + family: "mistral-small", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-03", + release_date: "2025-06-20", + last_updated: "2025-06-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 128000, output: 16384 }, + }, + "gemma-3-12b-it": { + id: "gemma-3-12b-it", + name: "Gemma 3 12B", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-03-13", + last_updated: "2025-03-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 32768, output: 8192 }, + }, + "gpt-5.2-codex": { + id: "gpt-5.2-codex", + name: "GPT-5.2 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gemini-2.5-flash": { + id: "gemini-2.5-flash", + name: "Gemini 2.5 Flash", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-20", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5, cache_read: 0.03, input_audio: 1 }, + limit: { context: 1048576, output: 65536 }, + }, + "gpt-5.2-chat-latest": { + id: "gpt-5.2-chat-latest", + name: "GPT-5.2 Chat", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 128000, output: 16384 }, + }, + "gemma-3n-e2b-it": { + id: "gemma-3n-e2b-it", + name: "Gemma 3n 2B", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 8192, output: 2000 }, + }, + "gpt-5.1-codex-mini": { + id: "gpt-5.1-codex-mini", + name: "GPT-5.1 Codex mini", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.025 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "grok-4-fast": { + id: "grok-4-fast", + name: "Grok 4 Fast", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-09-19", + last_updated: "2025-09-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + "gemini-3.1-flash-lite": { + id: "gemini-3.1-flash-lite", + name: "Gemini 3.1 Flash Lite", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-05-07", + last_updated: "2026-05-07", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.5, cache_read: 0.025, cache_write: 1 }, + limit: { context: 1048576, output: 65536 }, + }, + "qwen3-next-80b-a3b-thinking": { + id: "qwen3-next-80b-a3b-thinking", + name: "Qwen3-Next 80B-A3B (Thinking)", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09", + last_updated: "2025-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 6 }, + limit: { context: 131072, output: 32768 }, + }, + "grok-code-fast-1": { + id: "grok-code-fast-1", + name: "Grok Code Fast 1", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2025-08-28", + last_updated: "2025-08-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.5, cache_read: 0.02 }, + limit: { context: 256000, output: 10000 }, + }, + "gpt-5.1": { + id: "gpt-5.1", + name: "GPT-5.1", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.13 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gemma-3-4b-it": { + id: "gemma-3-4b-it", + name: "Gemma 3 4B", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2025-03-13", + last_updated: "2025-03-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 32768, output: 8192 }, + }, + "kimi-k2-thinking-turbo": { + id: "kimi-k2-thinking-turbo", + name: "Kimi K2 Thinking Turbo", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-08", + release_date: "2025-11-06", + last_updated: "2025-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.15, output: 8, cache_read: 0.15 }, + limit: { context: 262144, output: 262144 }, + }, + o1: { + id: "o1", + name: "o1", + family: "o", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2023-09", + release_date: "2024-12-05", + last_updated: "2024-12-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 60, cache_read: 7.5 }, + limit: { context: 200000, output: 100000 }, + }, + "glm-4.5-air": { + id: "glm-4.5-air", + name: "GLM-4.5-Air", + family: "glm-air", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 1.1, cache_read: 0.03, cache_write: 0 }, + limit: { context: 131072, output: 98304 }, + }, + "gpt-5.4-pro": { + id: "gpt-5.4-pro", + name: "GPT-5.4 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 180 }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "gpt-3.5-turbo": { + id: "gpt-3.5-turbo", + name: "GPT-3.5-turbo", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + knowledge: "2021-09-01", + release_date: "2023-03-01", + last_updated: "2023-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 1.5, cache_read: 1.25 }, + limit: { context: 16385, output: 4096 }, + }, + "o3-mini": { + id: "o3-mini", + name: "o3-mini", + family: "o-mini", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05", + release_date: "2024-12-20", + last_updated: "2025-01-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.55 }, + limit: { context: 200000, output: 100000 }, + }, + "qwen-vl-max": { + id: "qwen-vl-max", + name: "Qwen-VL Max", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-04-08", + last_updated: "2025-08-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 3.2 }, + limit: { context: 131072, output: 8192 }, + }, + sonar: { + id: "sonar", + name: "Sonar", + family: "sonar", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-09-01", + release_date: "2024-01-01", + last_updated: "2025-09-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 1 }, + limit: { context: 128000, output: 4096 }, + }, + "qwen3-coder-flash": { + id: "qwen3-coder-flash", + name: "Qwen3 Coder Flash", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 1.5 }, + limit: { context: 1000000, output: 65536 }, + }, + "grok-4-3": { + id: "grok-4-3", + name: "Grok 4.3", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-05-01", + last_updated: "2026-05-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { + input: 1.25, + output: 2.5, + cache_read: 0.2, + context_over_200k: { input: 2.5, output: 5, cache_read: 0.4 }, + }, + limit: { context: 1000000, output: 30000 }, + }, + "glm-4.5v": { + id: "glm-4.5v", + name: "GLM-4.5V", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-08-11", + last_updated: "2025-08-11", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 1.8 }, + limit: { context: 64000, output: 16384 }, + }, + "deepseek-v4-flash": { + id: "deepseek-v4-flash", + name: "DeepSeek V4 Flash", + family: "deepseek-flash", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.28, cache_read: 0.028 }, + limit: { context: 1000000, output: 384000 }, + }, + "grok-4": { + id: "grok-4", + name: "Grok 4", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, reasoning: 15, cache_read: 0.75 }, + limit: { context: 256000, output: 64000 }, + }, + "qwen3-next-80b-a3b-instruct": { + id: "qwen3-next-80b-a3b-instruct", + name: "Qwen3-Next 80B-A3B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09", + last_updated: "2025-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 2 }, + limit: { context: 131072, output: 32768 }, + }, + "gpt-4": { + id: "gpt-4", + name: "GPT-4", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + knowledge: "2023-11", + release_date: "2023-11-06", + last_updated: "2024-04-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 60 }, + limit: { context: 8192, output: 8192 }, + }, + "glm-4.6": { + id: "glm-4.6", + name: "GLM-4.6", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2, cache_read: 0.11, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "kimi-k2.6": { + id: "kimi-k2.6", + name: "Kimi K2.6", + family: "kimi-k2.6", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4, cache_read: 0.16 }, + limit: { context: 262144, output: 262144 }, + }, + "glm-4.6v": { + id: "glm-4.6v", + name: "GLM-4.6V", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-08", + last_updated: "2025-12-08", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.9 }, + limit: { context: 128000, output: 32768 }, + }, + "claude-opus-4-1-20250805": { + id: "claude-opus-4-1-20250805", + name: "Claude Opus 4.1", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "gpt-5.4": { + id: "gpt-5.4", + name: "GPT-5.4", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 15, cache_read: 0.25 }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "claude-haiku-4-5-20251001": { + id: "claude-haiku-4-5-20251001", + name: "Claude Haiku 4.5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + }, + "glm-4.5-flash": { + id: "glm-4.5-flash", + name: "GLM-4.5-Flash", + family: "glm-flash", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 131072, output: 98304 }, + }, + "qwen3-vl-plus": { + id: "qwen3-vl-plus", + name: "Qwen3-VL Plus", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-23", + last_updated: "2025-09-23", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.6, reasoning: 4.8 }, + limit: { context: 262144, output: 32768 }, + }, + "grok-4-1-fast": { + id: "grok-4-1-fast", + name: "Grok 4.1 Fast", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-11-19", + last_updated: "2025-11-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + "claude-sonnet-4-20250514": { + id: "claude-sonnet-4-20250514", + name: "Claude Sonnet 4", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "qwen3-coder-480b-a35b-instruct": { + id: "qwen3-coder-480b-a35b-instruct", + name: "Qwen3-Coder 480B-A35B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.5, output: 7.5 }, + limit: { context: 262144, output: 65536 }, + }, + "claude-opus-4-6": { + id: "claude-opus-4-6", + name: "Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "deepseek-v4-pro": { + id: "deepseek-v4-pro", + name: "DeepSeek V4 Pro", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-05", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.74, output: 3.48, cache_read: 0.145 }, + limit: { context: 1000000, output: 384000 }, + }, + "gpt-4.1-nano": { + id: "gpt-4.1-nano", + name: "GPT-4.1 nano", + family: "gpt-nano", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.03 }, + limit: { context: 1047576, output: 32768 }, + }, + "claude-3-7-sonnet-20250219": { + id: "claude-3-7-sonnet-20250219", + name: "Claude Sonnet 3.7", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10-31", + release_date: "2025-02-19", + last_updated: "2025-02-19", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "qwen3-coder-30b-a3b-instruct": { + id: "qwen3-coder-30b-a3b-instruct", + name: "Qwen3-Coder 30B-A3B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.45, output: 2.25 }, + limit: { context: 262144, output: 65536 }, + }, + "minimax-m2.5": { + id: "minimax-m2.5", + name: "MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.03, cache_write: 0.375 }, + limit: { context: 204800, output: 131072 }, + }, + "mimo-v2-pro": { + id: "mimo-v2-pro", + name: "MiMo-V2-Pro", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 3, cache_read: 0.2, context_over_200k: { input: 2, output: 6, cache_read: 0.4 } }, + limit: { context: 1048576, output: 131072 }, + }, + o3: { + id: "o3", + name: "o3", + family: "o", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05", + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 200000, output: 100000 }, + }, + "gpt-5-pro": { + id: "gpt-5-pro", + name: "GPT-5 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-10-06", + last_updated: "2025-10-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 120 }, + limit: { context: 400000, input: 272000, output: 272000 }, + }, + "gpt-4o": { + id: "gpt-4o", + name: "GPT-4o", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-09", + release_date: "2024-05-13", + last_updated: "2024-08-06", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10, cache_read: 1.25 }, + limit: { context: 128000, output: 16384 }, + }, + "minimax-m2.5-highspeed": { + id: "minimax-m2.5-highspeed", + name: "MiniMax-M2.5-highspeed", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-13", + last_updated: "2026-02-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.4, cache_read: 0.06, cache_write: 0.375 }, + limit: { context: 204800, output: 131072 }, + }, + "qwen-turbo": { + id: "qwen-turbo", + name: "Qwen Turbo", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-11-01", + last_updated: "2025-04-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.2, reasoning: 0.5 }, + limit: { context: 1000000, output: 16384 }, + }, + "claude-sonnet-4-5": { + id: "claude-sonnet-4-5", + name: "Claude Sonnet 4.5 (latest)", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "gemini-2.5-flash-lite": { + id: "gemini-2.5-flash-lite", + name: "Gemini 2.5 Flash Lite", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.025 }, + limit: { context: 1048576, output: 65536 }, + }, + "gpt-5": { + id: "gpt-5", + name: "GPT-5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "glm-4.7-flash": { + id: "glm-4.7-flash", + name: "GLM-4.7-Flash", + family: "glm-flash", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-01-19", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 200000, output: 131072 }, + }, + "mimo-v2-flash": { + id: "mimo-v2-flash", + name: "MiMo-V2-Flash", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12-01", + release_date: "2025-12-16", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3, cache_read: 0.01 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen3.6-max-preview": { + id: "qwen3.6-max-preview", + name: "Qwen3.6 Max Preview", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-04-20", + last_updated: "2026-04-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.3, output: 7.8, cache_read: 0.13, cache_write: 1.625 }, + limit: { context: 262144, output: 65536 }, + }, + "gpt-5-chat-latest": { + id: "gpt-5-chat-latest", + name: "GPT-5 Chat (latest)", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: false, + structured_output: true, + temperature: true, + knowledge: "2024-09-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "claude-opus-4-20250514": { + id: "claude-opus-4-20250514", + name: "Claude Opus 4", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "qwen2-5-vl-72b-instruct": { + id: "qwen2-5-vl-72b-instruct", + name: "Qwen2.5-VL 72B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-09", + last_updated: "2024-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 2.8, output: 8.4 }, + limit: { context: 131072, output: 8192 }, + }, + "gpt-5.5-pro": { + id: "gpt-5.5-pro", + name: "GPT-5.5 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-12-01", + release_date: "2026-04-23", + last_updated: "2026-04-23", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 180, context_over_200k: { input: 60, output: 270 } }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "gpt-4.1": { + id: "gpt-4.1", + name: "GPT-4.1", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 1047576, output: 32768 }, + }, + "devstral-small-2507": { + id: "devstral-small-2507", + name: "Devstral Small", + family: "devstral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-05", + release_date: "2025-07-10", + last_updated: "2025-07-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 128000, output: 128000 }, + }, + "kimi-k2-thinking": { + id: "kimi-k2-thinking", + name: "Kimi K2 Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-08", + release_date: "2025-11-06", + last_updated: "2025-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5, cache_read: 0.15 }, + limit: { context: 262144, output: 262144 }, + }, + "gemini-2.0-flash-lite": { + id: "gemini-2.0-flash-lite", + name: "Gemini 2.0 Flash Lite", + family: "gemini-flash-lite", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-06", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.075, output: 0.3 }, + limit: { context: 1048576, output: 8192 }, + }, + "gpt-4.1-mini": { + id: "gpt-4.1-mini", + name: "GPT-4.1 mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.6, cache_read: 0.1 }, + limit: { context: 1047576, output: 32768 }, + }, + "gpt-5.1-codex": { + id: "gpt-5.1-codex", + name: "GPT-5.1 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "grok-3": { + id: "grok-3", + name: "Grok 3", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.75 }, + limit: { context: 131072, output: 8192 }, + }, + "grok-4-fast-non-reasoning": { + id: "grok-4-fast-non-reasoning", + name: "Grok 4 Fast (Non-Reasoning)", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-09-19", + last_updated: "2025-09-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + "sonar-reasoning-pro": { + id: "sonar-reasoning-pro", + name: "Sonar Reasoning Pro", + family: "sonar-reasoning", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2025-09-01", + release_date: "2024-01-01", + last_updated: "2025-09-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8 }, + limit: { context: 128000, output: 4096 }, + }, + }, + }, + "google-vertex": { + id: "google-vertex", + env: ["GOOGLE_VERTEX_PROJECT", "GOOGLE_VERTEX_LOCATION", "GOOGLE_APPLICATION_CREDENTIALS"], + npm: "@ai-sdk/google-vertex", + name: "Vertex", + doc: "https://cloud.google.com/vertex-ai/generative-ai/docs/models", + models: { + "gemini-2.0-flash": { + id: "gemini-2.0-flash", + name: "Gemini 2.0 Flash", + family: "gemini-flash", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-06", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6, cache_read: 0.025 }, + limit: { context: 1048576, output: 8192 }, + }, + "gemini-3-pro-preview": { + id: "gemini-3-pro-preview", + name: "Gemini 3 Pro Preview", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-11-18", + last_updated: "2025-11-18", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2, context_over_200k: { input: 4, output: 18, cache_read: 0.4 } }, + limit: { context: 1048576, output: 65536 }, + }, + "gemini-flash-latest": { + id: "gemini-flash-latest", + name: "Gemini Flash Latest", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5, cache_read: 0.075, cache_write: 0.383 }, + limit: { context: 1048576, output: 65536 }, + }, + "gemini-2.5-flash-lite-preview-06-17": { + id: "gemini-2.5-flash-lite-preview-06-17", + name: "Gemini 2.5 Flash Lite Preview 06-17", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.025 }, + limit: { context: 65536, output: 65536 }, + }, + "gemini-2.5-flash": { + id: "gemini-2.5-flash", + name: "Gemini 2.5 Flash", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5, cache_read: 0.075, cache_write: 0.383 }, + limit: { context: 1048576, output: 65536 }, + }, + "gemini-2.5-flash-preview-09-2025": { + id: "gemini-2.5-flash-preview-09-2025", + name: "Gemini 2.5 Flash Preview 09-25", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5, cache_read: 0.075, cache_write: 0.383 }, + limit: { context: 1048576, output: 65536 }, + }, + "zai-org/glm-5-maas": { + id: "zai-org/glm-5-maas", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3.2, cache_read: 0.1 }, + limit: { context: 202752, output: 131072 }, + provider: { + npm: "@ai-sdk/openai-compatible", + api: "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi", + }, + }, + "zai-org/glm-4.7-maas": { + id: "zai-org/glm-4.7-maas", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-01-06", + last_updated: "2026-01-06", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2 }, + limit: { context: 200000, output: 128000 }, + provider: { + npm: "@ai-sdk/openai-compatible", + api: "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi", + }, + }, + "deepseek-ai/deepseek-v3.2-maas": { + id: "deepseek-ai/deepseek-v3.2-maas", + name: "DeepSeek V3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-12-17", + last_updated: "2026-04-04", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: true, + cost: { input: 0.56, output: 1.68, cache_read: 0.056 }, + limit: { context: 163840, output: 65536 }, + provider: { + npm: "@ai-sdk/openai-compatible", + api: "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi", + }, + }, + "deepseek-ai/deepseek-v3.1-maas": { + id: "deepseek-ai/deepseek-v3.1-maas", + name: "DeepSeek V3.1", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-28", + last_updated: "2025-08-28", + modalities: { input: ["text", "pdf"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 1.7 }, + limit: { context: 163840, output: 32768 }, + provider: { + npm: "@ai-sdk/openai-compatible", + api: "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi", + }, + }, + "openai/gpt-oss-120b-maas": { + id: "openai/gpt-oss-120b-maas", + name: "GPT OSS 120B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.09, output: 0.36 }, + limit: { context: 131072, output: 32768 }, + }, + "openai/gpt-oss-20b-maas": { + id: "openai/gpt-oss-20b-maas", + name: "GPT OSS 20B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.25 }, + limit: { context: 131072, output: 32768 }, + }, + "meta/llama-3.3-70b-instruct-maas": { + id: "meta/llama-3.3-70b-instruct-maas", + name: "Llama 3.3 70B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-12", + release_date: "2025-04-29", + last_updated: "2025-04-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.72, output: 0.72 }, + limit: { context: 128000, output: 8192 }, + provider: { + npm: "@ai-sdk/openai-compatible", + api: "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi", + }, + }, + "meta/llama-4-maverick-17b-128e-instruct-maas": { + id: "meta/llama-4-maverick-17b-128e-instruct-maas", + name: "Llama 4 Maverick 17B 128E Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-04-29", + last_updated: "2025-04-29", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.35, output: 1.15 }, + limit: { context: 524288, output: 8192 }, + provider: { + npm: "@ai-sdk/openai-compatible", + api: "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi", + }, + }, + "qwen/qwen3-235b-a22b-instruct-2507-maas": { + id: "qwen/qwen3-235b-a22b-instruct-2507-maas", + name: "Qwen3 235B A22B Instruct", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-13", + last_updated: "2025-08-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.22, output: 0.88 }, + limit: { context: 262144, output: 16384 }, + provider: { + npm: "@ai-sdk/openai-compatible", + api: "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi", + }, + }, + "moonshotai/kimi-k2-thinking-maas": { + id: "moonshotai/kimi-k2-thinking-maas", + name: "Kimi K2 Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5 }, + limit: { context: 262144, output: 262144 }, + provider: { + npm: "@ai-sdk/openai-compatible", + api: "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi", + }, + }, + "gemini-flash-lite-latest": { + id: "gemini-flash-lite-latest", + name: "Gemini Flash-Lite Latest", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.025 }, + limit: { context: 1048576, output: 65536 }, + }, + "gemini-2.5-pro-preview-05-06": { + id: "gemini-2.5-pro-preview-05-06", + name: "Gemini 2.5 Pro Preview 05-06", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-05-06", + last_updated: "2025-05-06", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.31 }, + limit: { context: 1048576, output: 65536 }, + }, + "claude-haiku-4-5@20251001": { + id: "claude-haiku-4-5@20251001", + name: "Claude Haiku 4.5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + provider: { npm: "@ai-sdk/google-vertex/anthropic" }, + }, + "gemini-3.1-pro-preview-customtools": { + id: "gemini-3.1-pro-preview-customtools", + name: "Gemini 3.1 Pro Preview Custom Tools", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-19", + last_updated: "2026-02-19", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2, context_over_200k: { input: 4, output: 18, cache_read: 0.4 } }, + limit: { context: 1048576, output: 65536 }, + }, + "claude-sonnet-4-6@default": { + id: "claude-sonnet-4-6@default", + name: "Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 3, + output: 15, + cache_read: 0.3, + cache_write: 3.75, + context_over_200k: { input: 6, output: 22.5, cache_read: 0.6, cache_write: 7.5 }, + }, + limit: { context: 200000, output: 64000 }, + provider: { npm: "@ai-sdk/google-vertex/anthropic" }, + }, + "gemini-2.5-flash-lite-preview-09-2025": { + id: "gemini-2.5-flash-lite-preview-09-2025", + name: "Gemini 2.5 Flash Lite Preview 09-25", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.025 }, + limit: { context: 1048576, output: 65536 }, + }, + "claude-3-5-haiku@20241022": { + id: "claude-3-5-haiku@20241022", + name: "Claude Haiku 3.5", + family: "claude-haiku", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07-31", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 4, cache_read: 0.08, cache_write: 1 }, + limit: { context: 200000, output: 8192 }, + provider: { npm: "@ai-sdk/google-vertex/anthropic" }, + }, + "gemini-3.1-flash-lite-preview": { + id: "gemini-3.1-flash-lite-preview", + name: "Gemini 3.1 Flash Lite Preview", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-03-03", + last_updated: "2026-03-03", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.5, cache_read: 0.025, cache_write: 1 }, + limit: { context: 1048576, output: 65536 }, + }, + "claude-3-5-sonnet@20241022": { + id: "claude-3-5-sonnet@20241022", + name: "Claude Sonnet 3.5 v2", + family: "claude-sonnet", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04-30", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 8192 }, + provider: { npm: "@ai-sdk/google-vertex/anthropic" }, + }, + "gemini-3.1-pro-preview": { + id: "gemini-3.1-pro-preview", + name: "Gemini 3.1 Pro Preview", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-19", + last_updated: "2026-02-19", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2, context_over_200k: { input: 4, output: 18, cache_read: 0.4 } }, + limit: { context: 1048576, output: 65536 }, + }, + "claude-opus-4-1@20250805": { + id: "claude-opus-4-1@20250805", + name: "Claude Opus 4.1", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + provider: { npm: "@ai-sdk/google-vertex/anthropic" }, + }, + "gemini-3-flash-preview": { + id: "gemini-3-flash-preview", + name: "Gemini 3 Flash Preview", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 0.5, + output: 3, + cache_read: 0.05, + context_over_200k: { input: 0.5, output: 3, cache_read: 0.05 }, + }, + limit: { context: 1048576, output: 65536 }, + }, + "gemini-2.5-flash-preview-05-20": { + id: "gemini-2.5-flash-preview-05-20", + name: "Gemini 2.5 Flash Preview 05-20", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-05-20", + last_updated: "2025-05-20", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6, cache_read: 0.0375 }, + limit: { context: 1048576, output: 65536 }, + }, + "gemini-embedding-001": { + id: "gemini-embedding-001", + name: "Gemini Embedding 001", + family: "gemini", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + knowledge: "2025-05", + release_date: "2025-05-20", + last_updated: "2025-05-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0 }, + limit: { context: 2048, output: 3072 }, + }, + "gemini-2.5-pro": { + id: "gemini-2.5-pro", + name: "Gemini 2.5 Pro", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-20", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 1.25, + output: 10, + cache_read: 0.125, + context_over_200k: { input: 2.5, output: 15, cache_read: 0.25 }, + }, + limit: { context: 1048576, output: 65536 }, + }, + "gemini-2.5-pro-preview-06-05": { + id: "gemini-2.5-pro-preview-06-05", + name: "Gemini 2.5 Pro Preview 06-05", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-06-05", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.31 }, + limit: { context: 1048576, output: 65536 }, + }, + "claude-sonnet-4@20250514": { + id: "claude-sonnet-4@20250514", + name: "Claude Sonnet 4", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + provider: { npm: "@ai-sdk/google-vertex/anthropic" }, + }, + "gemini-3.1-flash-lite": { + id: "gemini-3.1-flash-lite", + name: "Gemini 3.1 Flash Lite", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-05-07", + last_updated: "2026-05-07", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.5, cache_read: 0.025, cache_write: 1 }, + limit: { context: 1048576, output: 65536 }, + }, + "claude-3-7-sonnet@20250219": { + id: "claude-3-7-sonnet@20250219", + name: "Claude Sonnet 3.7", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10-31", + release_date: "2025-02-19", + last_updated: "2025-02-19", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + provider: { npm: "@ai-sdk/google-vertex/anthropic" }, + }, + "claude-opus-4@20250514": { + id: "claude-opus-4@20250514", + name: "Claude Opus 4", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + provider: { npm: "@ai-sdk/google-vertex/anthropic" }, + }, + "claude-opus-4-5@20251101": { + id: "claude-opus-4-5@20251101", + name: "Claude Opus 4.5", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-01", + last_updated: "2025-11-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 64000 }, + provider: { npm: "@ai-sdk/google-vertex/anthropic" }, + }, + "gemini-2.5-flash-preview-04-17": { + id: "gemini-2.5-flash-preview-04-17", + name: "Gemini 2.5 Flash Preview 04-17", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-04-17", + last_updated: "2025-04-17", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6, cache_read: 0.0375 }, + limit: { context: 1048576, output: 65536 }, + }, + "claude-sonnet-4-5@20250929": { + id: "claude-sonnet-4-5@20250929", + name: "Claude Sonnet 4.5", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + provider: { npm: "@ai-sdk/google-vertex/anthropic" }, + }, + "gemini-2.5-flash-lite": { + id: "gemini-2.5-flash-lite", + name: "Gemini 2.5 Flash Lite", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.025 }, + limit: { context: 1048576, output: 65536 }, + }, + "claude-opus-4-6@default": { + id: "claude-opus-4-6@default", + name: "Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 5, + output: 25, + cache_read: 0.5, + cache_write: 6.25, + context_over_200k: { input: 10, output: 37.5, cache_read: 1, cache_write: 12.5 }, + }, + limit: { context: 1000000, output: 128000 }, + provider: { npm: "@ai-sdk/google-vertex/anthropic" }, + }, + "gemini-2.0-flash-lite": { + id: "gemini-2.0-flash-lite", + name: "Gemini 2.0 Flash Lite", + family: "gemini-flash-lite", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-06", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.075, output: 0.3 }, + limit: { context: 1048576, output: 8192 }, + }, + "claude-opus-4-7@default": { + id: "claude-opus-4-7@default", + name: "Claude Opus 4.7", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 5, + output: 25, + cache_read: 0.5, + cache_write: 6.25, + context_over_200k: { input: 10, output: 37.5, cache_read: 1, cache_write: 12.5 }, + }, + limit: { context: 1000000, output: 128000 }, + provider: { npm: "@ai-sdk/google-vertex/anthropic" }, + }, + }, + }, + "cloudflare-workers-ai": { + id: "cloudflare-workers-ai", + env: ["CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/ai/v1", + name: "Cloudflare Workers AI", + doc: "https://developers.cloudflare.com/workers-ai/models/", + models: { + "@cf/zai-org/glm-4.7-flash": { + id: "@cf/zai-org/glm-4.7-flash", + name: "GLM-4.7-Flash", + family: "glm-flash", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-01-19", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.06, output: 0.4 }, + limit: { context: 131072, output: 131072 }, + }, + "@cf/nvidia/nemotron-3-120b-a12b": { + id: "@cf/nvidia/nemotron-3-120b-a12b", + name: "Nemotron 3 Super 120B", + family: "nemotron", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-03-11", + last_updated: "2026-03-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 1.5 }, + limit: { context: 256000, output: 256000 }, + }, + "@cf/openai/gpt-oss-20b": { + id: "@cf/openai/gpt-oss-20b", + name: "GPT OSS 20B", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.3 }, + limit: { context: 128000, output: 16384 }, + }, + "@cf/openai/gpt-oss-120b": { + id: "@cf/openai/gpt-oss-120b", + name: "GPT OSS 120B", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.35, output: 0.75 }, + limit: { context: 128000, output: 16384 }, + }, + "@cf/meta/llama-4-scout-17b-16e-instruct": { + id: "@cf/meta/llama-4-scout-17b-16e-instruct", + name: "Llama 4 Scout 17B 16E Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.27, output: 0.85 }, + limit: { context: 128000, output: 16384 }, + }, + "@cf/google/gemma-4-26b-a4b-it": { + id: "@cf/google/gemma-4-26b-a4b-it", + name: "Gemma 4 26B A4B IT", + family: "gemma", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-12-15", + last_updated: "2025-12-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 256000, output: 16384 }, + }, + "@cf/moonshotai/kimi-k2.5": { + id: "@cf/moonshotai/kimi-k2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3, cache_read: 0.1 }, + limit: { context: 256000, output: 256000 }, + }, + "@cf/moonshotai/kimi-k2.6": { + id: "@cf/moonshotai/kimi-k2.6", + name: "Kimi K2.6", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-20", + last_updated: "2026-04-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4, cache_read: 0.16 }, + limit: { context: 256000, output: 256000 }, + }, + }, + }, + groq: { + id: "groq", + env: ["GROQ_API_KEY"], + npm: "@ai-sdk/groq", + name: "Groq", + doc: "https://console.groq.com/docs/models", + models: { + "gemma2-9b-it": { + id: "gemma2-9b-it", + name: "Gemma 2 9B", + family: "gemma", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-06", + release_date: "2024-06-27", + last_updated: "2024-06-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 8192, output: 8192 }, + status: "deprecated", + }, + "mistral-saba-24b": { + id: "mistral-saba-24b", + name: "Mistral Saba 24B", + family: "mistral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-02-06", + last_updated: "2025-02-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.79, output: 0.79 }, + limit: { context: 32768, output: 32768 }, + status: "deprecated", + }, + "deepseek-r1-distill-llama-70b": { + id: "deepseek-r1-distill-llama-70b", + name: "DeepSeek R1 Distill Llama 70B", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.75, output: 0.99 }, + limit: { context: 131072, output: 8192 }, + status: "deprecated", + }, + "llama-guard-3-8b": { + id: "llama-guard-3-8b", + name: "Llama Guard 3 8B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 8192, output: 8192 }, + status: "deprecated", + }, + "llama-3.3-70b-versatile": { + id: "llama-3.3-70b-versatile", + name: "Llama 3.3 70B Versatile", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.59, output: 0.79 }, + limit: { context: 131072, output: 32768 }, + }, + "allam-2-7b": { + id: "allam-2-7b", + name: "ALLaM-2-7b", + family: "allam", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-09", + release_date: "2024-09", + last_updated: "2024-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 4096, output: 4096 }, + }, + "whisper-large-v3": { + id: "whisper-large-v3", + name: "Whisper Large V3", + family: "whisper", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-09", + release_date: "2023-09-01", + last_updated: "2025-09-05", + modalities: { input: ["audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 448, output: 448 }, + }, + "llama-3.1-8b-instant": { + id: "llama-3.1-8b-instant", + name: "Llama 3.1 8B Instant", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.08 }, + limit: { context: 131072, output: 131072 }, + }, + "llama3-70b-8192": { + id: "llama3-70b-8192", + name: "Llama 3 70B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-03", + release_date: "2024-04-18", + last_updated: "2024-04-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.59, output: 0.79 }, + limit: { context: 8192, output: 8192 }, + status: "deprecated", + }, + "qwen-qwq-32b": { + id: "qwen-qwq-32b", + name: "Qwen QwQ 32B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-09", + release_date: "2024-11-27", + last_updated: "2024-11-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.29, output: 0.39 }, + limit: { context: 131072, output: 16384 }, + status: "deprecated", + }, + "whisper-large-v3-turbo": { + id: "whisper-large-v3-turbo", + name: "Whisper Large v3 Turbo", + family: "whisper", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2024-10-01", + last_updated: "2024-10-01", + modalities: { input: ["audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 448, output: 448 }, + }, + "llama3-8b-8192": { + id: "llama3-8b-8192", + name: "Llama 3 8B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-03", + release_date: "2024-04-18", + last_updated: "2024-04-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.08 }, + limit: { context: 8192, output: 8192 }, + status: "deprecated", + }, + "canopylabs/orpheus-arabic-saudi": { + id: "canopylabs/orpheus-arabic-saudi", + name: "Orpheus Arabic Saudi", + family: "canopylabs", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-12-16", + release_date: "2025-12-16", + last_updated: "2025-12-16", + modalities: { input: ["text"], output: ["audio"] }, + open_weights: false, + cost: { input: 40, output: 0 }, + limit: { context: 4000, output: 50000 }, + }, + "canopylabs/orpheus-v1-english": { + id: "canopylabs/orpheus-v1-english", + name: "Orpheus V1 English", + family: "canopylabs", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2025-12-19", + release_date: "2025-12-19", + last_updated: "2025-12-19", + modalities: { input: ["text"], output: ["audio"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 4000, output: 50000 }, + }, + "meta-llama/llama-4-scout-17b-16e-instruct": { + id: "meta-llama/llama-4-scout-17b-16e-instruct", + name: "Llama 4 Scout 17B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.11, output: 0.34 }, + limit: { context: 131072, output: 8192 }, + }, + "meta-llama/llama-prompt-guard-2-22m": { + id: "meta-llama/llama-prompt-guard-2-22m", + name: "Llama Prompt Guard 2 22M", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2024-10-01", + last_updated: "2024-10-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.03 }, + limit: { context: 512, output: 512 }, + }, + "meta-llama/llama-guard-4-12b": { + id: "meta-llama/llama-guard-4-12b", + name: "Llama Guard 4 12B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.2 }, + limit: { context: 131072, output: 1024 }, + status: "deprecated", + }, + "meta-llama/llama-4-maverick-17b-128e-instruct": { + id: "meta-llama/llama-4-maverick-17b-128e-instruct", + name: "Llama 4 Maverick 17B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.6 }, + limit: { context: 131072, output: 8192 }, + status: "deprecated", + }, + "meta-llama/llama-prompt-guard-2-86m": { + id: "meta-llama/llama-prompt-guard-2-86m", + name: "Llama Prompt Guard 2 86M", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2024-10-01", + last_updated: "2024-10-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.04, output: 0.04 }, + limit: { context: 512, output: 512 }, + }, + "openai/gpt-oss-20b": { + id: "openai/gpt-oss-20b", + name: "GPT OSS 20B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.075, output: 0.3 }, + limit: { context: 131072, output: 65536 }, + }, + "openai/gpt-oss-safeguard-20b": { + id: "openai/gpt-oss-safeguard-20b", + name: "Safety GPT OSS 20B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-03-05", + last_updated: "2025-03-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.075, output: 0.3, cache_read: 0.037 }, + limit: { context: 131072, output: 65536 }, + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "GPT OSS 120B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 131072, output: 65536 }, + }, + "qwen/qwen3-32b": { + id: "qwen/qwen3-32b", + name: "Qwen3 32B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-11-08", + release_date: "2024-12-23", + last_updated: "2024-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.29, output: 0.59 }, + limit: { context: 131072, output: 40960 }, + }, + "groq/compound": { + id: "groq/compound", + name: "Compound", + family: "groq", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-09-04", + release_date: "2025-09-04", + last_updated: "2025-09-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 8192 }, + }, + "groq/compound-mini": { + id: "groq/compound-mini", + name: "Compound Mini", + family: "groq", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-09-04", + release_date: "2025-09-04", + last_updated: "2025-09-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 8192 }, + }, + "moonshotai/kimi-k2-instruct": { + id: "moonshotai/kimi-k2-instruct", + name: "Kimi K2 Instruct", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-07-14", + last_updated: "2025-07-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3 }, + limit: { context: 131072, output: 16384 }, + status: "deprecated", + }, + "moonshotai/kimi-k2-instruct-0905": { + id: "moonshotai/kimi-k2-instruct-0905", + name: "Kimi K2 Instruct 0905", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3 }, + limit: { context: 262144, output: 16384 }, + }, + }, + }, + azure: { + id: "azure", + env: ["AZURE_RESOURCE_NAME", "AZURE_API_KEY"], + npm: "@ai-sdk/azure", + name: "Azure", + doc: "https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models", + models: { + "mistral-nemo": { + id: "mistral-nemo", + name: "Mistral Nemo", + family: "mistral-nemo", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.15 }, + limit: { context: 128000, output: 128000 }, + }, + "gpt-5.2-chat": { + id: "gpt-5.2-chat", + name: "GPT-5.2 Chat", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 128000, output: 16384 }, + }, + "codex-mini": { + id: "codex-mini", + name: "Codex Mini", + family: "gpt-codex-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-04", + release_date: "2025-05-16", + last_updated: "2025-05-16", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.5, output: 6, cache_read: 0.375 }, + limit: { context: 200000, output: 100000 }, + }, + "phi-4-multimodal": { + id: "phi-4-multimodal", + name: "Phi-4-multimodal", + family: "phi", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text", "image", "audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0.08, output: 0.32, input_audio: 4 }, + limit: { context: 128000, output: 4096 }, + }, + "phi-3.5-mini-instruct": { + id: "phi-3.5-mini-instruct", + name: "Phi-3.5-mini-instruct", + family: "phi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-08-20", + last_updated: "2024-08-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.13, output: 0.52 }, + limit: { context: 128000, output: 4096 }, + }, + "llama-4-scout-17b-16e-instruct": { + id: "llama-4-scout-17b-16e-instruct", + name: "Llama 4 Scout 17B 16E Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.78 }, + limit: { context: 128000, output: 8192 }, + }, + "grok-4-1-fast-reasoning": { + id: "grok-4-1-fast-reasoning", + name: "Grok 4.1 Fast (Reasoning)", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-06-27", + last_updated: "2025-06-27", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 128000, input: 128000, output: 8192 }, + status: "beta", + }, + "phi-3-medium-4k-instruct": { + id: "phi-3-medium-4k-instruct", + name: "Phi-3-medium-instruct (4k)", + family: "phi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-04-23", + last_updated: "2024-04-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.17, output: 0.68 }, + limit: { context: 4096, output: 1024 }, + }, + "ministral-3b": { + id: "ministral-3b", + name: "Ministral 3B", + family: "ministral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-03", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.04, output: 0.04 }, + limit: { context: 128000, output: 8192 }, + }, + "claude-haiku-4-5": { + id: "claude-haiku-4-5", + name: "Claude Haiku 4.5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-02-31", + release_date: "2025-11-18", + last_updated: "2025-11-18", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + provider: { + npm: "@ai-sdk/anthropic", + api: "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1", + }, + }, + "meta-llama-3.1-8b-instruct": { + id: "meta-llama-3.1-8b-instruct", + name: "Meta-Llama-3.1-8B-Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.61 }, + limit: { context: 128000, output: 32768 }, + }, + "kimi-k2.5": { + id: "kimi-k2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-06", + last_updated: "2026-02-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3 }, + limit: { context: 262144, output: 262144 }, + provider: { + npm: "@ai-sdk/openai-compatible", + api: "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/models", + shape: "completions", + }, + }, + "llama-3.3-70b-instruct": { + id: "llama-3.3-70b-instruct", + name: "Llama-3.3-70B-Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.71, output: 0.71 }, + limit: { context: 128000, output: 32768 }, + }, + "deepseek-v3-0324": { + id: "deepseek-v3-0324", + name: "DeepSeek-V3-0324", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-03-24", + last_updated: "2025-03-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.14, output: 4.56 }, + limit: { context: 131072, output: 131072 }, + }, + "gpt-5-chat": { + id: "gpt-5-chat", + name: "GPT-5 Chat", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: false, + temperature: false, + knowledge: "2024-10-24", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.13 }, + limit: { context: 128000, output: 16384 }, + }, + "phi-3.5-moe-instruct": { + id: "phi-3.5-moe-instruct", + name: "Phi-3.5-MoE-instruct", + family: "phi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-08-20", + last_updated: "2024-08-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.16, output: 0.64 }, + limit: { context: 128000, output: 4096 }, + }, + "gpt-5.3-chat": { + id: "gpt-5.3-chat", + name: "GPT-5.3 Chat", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-03", + last_updated: "2026-03-03", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 128000, output: 16384 }, + }, + "o1-mini": { + id: "o1-mini", + name: "o1-mini", + family: "o-mini", + attachment: false, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2023-09", + release_date: "2024-09-12", + last_updated: "2024-09-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.55 }, + limit: { context: 128000, output: 65536 }, + }, + "text-embedding-3-large": { + id: "text-embedding-3-large", + name: "text-embedding-3-large", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + release_date: "2024-01-25", + last_updated: "2024-01-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.13, output: 0 }, + limit: { context: 8191, output: 3072 }, + }, + "phi-3-mini-128k-instruct": { + id: "phi-3-mini-128k-instruct", + name: "Phi-3-mini-instruct (128k)", + family: "phi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-04-23", + last_updated: "2024-04-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.13, output: 0.52 }, + limit: { context: 128000, output: 4096 }, + }, + "phi-4-reasoning": { + id: "phi-4-reasoning", + name: "Phi-4-reasoning", + family: "phi", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.125, output: 0.5 }, + limit: { context: 32000, output: 4096 }, + }, + "gpt-5-mini": { + id: "gpt-5-mini", + name: "GPT-5 Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.03 }, + limit: { context: 272000, output: 128000 }, + }, + "gpt-5-nano": { + id: "gpt-5-nano", + name: "GPT-5 Nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.4, cache_read: 0.01 }, + limit: { context: 272000, output: 128000 }, + }, + "meta-llama-3-70b-instruct": { + id: "meta-llama-3-70b-instruct", + name: "Meta-Llama-3-70B-Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-12", + release_date: "2024-04-18", + last_updated: "2024-04-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.68, output: 3.54 }, + limit: { context: 8192, output: 2048 }, + }, + "phi-3-small-8k-instruct": { + id: "phi-3-small-8k-instruct", + name: "Phi-3-small-instruct (8k)", + family: "phi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-04-23", + last_updated: "2024-04-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 8192, output: 2048 }, + }, + "gpt-5.3-codex": { + id: "gpt-5.3-codex", + name: "GPT-5.3 Codex", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-02-24", + last_updated: "2026-02-24", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, output: 128000 }, + }, + "text-embedding-ada-002": { + id: "text-embedding-ada-002", + name: "text-embedding-ada-002", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + release_date: "2022-12-15", + last_updated: "2022-12-15", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0 }, + limit: { context: 8192, output: 1536 }, + }, + "llama-3.2-90b-vision-instruct": { + id: "llama-3.2-90b-vision-instruct", + name: "Llama-3.2-90B-Vision-Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-09-25", + last_updated: "2024-09-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 2.04, output: 2.04 }, + limit: { context: 128000, output: 8192 }, + }, + "deepseek-r1": { + id: "deepseek-r1", + name: "DeepSeek-R1", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2024-07", + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.35, output: 5.4 }, + limit: { context: 163840, output: 163840 }, + }, + "grok-4-1-fast-non-reasoning": { + id: "grok-4-1-fast-non-reasoning", + name: "Grok 4.1 Fast (Non-Reasoning)", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-06-27", + last_updated: "2025-06-27", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 128000, input: 128000, output: 8192 }, + status: "beta", + }, + "deepseek-v3.2-speciale": { + id: "deepseek-v3.2-speciale", + name: "DeepSeek-V3.2-Speciale", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2024-07", + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.58, output: 1.68 }, + limit: { context: 128000, output: 128000 }, + }, + "gpt-5.2": { + id: "gpt-5.2", + name: "GPT-5.2", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "mistral-large-2411": { + id: "mistral-large-2411", + name: "Mistral Large 24.11", + family: "mistral-large", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-09", + release_date: "2024-11-01", + last_updated: "2024-11-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6 }, + limit: { context: 128000, output: 32768 }, + }, + "claude-opus-4-1": { + id: "claude-opus-4-1", + name: "Claude Opus 4.1", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-18", + last_updated: "2025-11-18", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + provider: { + npm: "@ai-sdk/anthropic", + api: "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1", + }, + }, + "cohere-command-a": { + id: "cohere-command-a", + name: "Command A", + family: "command-a", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-06-01", + release_date: "2025-03-13", + last_updated: "2025-03-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.5, output: 10 }, + limit: { context: 256000, output: 8000 }, + }, + "llama-3.2-11b-vision-instruct": { + id: "llama-3.2-11b-vision-instruct", + name: "Llama-3.2-11B-Vision-Instruct", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-09-25", + last_updated: "2024-09-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.37, output: 0.37 }, + limit: { context: 128000, output: 8192 }, + }, + "meta-llama-3.1-405b-instruct": { + id: "meta-llama-3.1-405b-instruct", + name: "Meta-Llama-3.1-405B-Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 5.33, output: 16 }, + limit: { context: 128000, output: 32768 }, + }, + "gpt-5.1-chat": { + id: "gpt-5.1-chat", + name: "GPT-5.1 Chat", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-14", + last_updated: "2025-11-14", + modalities: { input: ["text", "image", "audio"], output: ["text", "image", "audio"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 128000, output: 16384 }, + }, + "gpt-4-turbo-vision": { + id: "gpt-4-turbo-vision", + name: "GPT-4 Turbo Vision", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-11", + release_date: "2023-11-06", + last_updated: "2024-04-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 10, output: 30 }, + limit: { context: 128000, output: 4096 }, + }, + "gpt-5.2-codex": { + id: "gpt-5.2-codex", + name: "GPT-5.2 Codex", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-01-14", + last_updated: "2026-01-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.75, output: 14, cache_read: 0.175 }, + limit: { context: 400000, output: 128000 }, + }, + "cohere-embed-v-4-0": { + id: "cohere-embed-v-4-0", + name: "Embed v4", + family: "cohere-embed", + attachment: true, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2025-04-15", + last_updated: "2025-04-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.12, output: 0 }, + limit: { context: 128000, output: 1536 }, + }, + "gpt-5.1-codex-mini": { + id: "gpt-5.1-codex-mini", + name: "GPT-5.1 Codex Mini", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-14", + last_updated: "2025-11-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.025 }, + limit: { context: 400000, output: 128000 }, + }, + "gpt-3.5-turbo-0125": { + id: "gpt-3.5-turbo-0125", + name: "GPT-3.5 Turbo 0125", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2021-08", + release_date: "2024-01-25", + last_updated: "2024-01-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 1.5 }, + limit: { context: 16384, output: 16384 }, + }, + "o1-preview": { + id: "o1-preview", + name: "o1-preview", + family: "o", + attachment: false, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2023-09", + release_date: "2024-09-12", + last_updated: "2024-09-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 16.5, output: 66, cache_read: 8.25 }, + limit: { context: 128000, output: 32768 }, + }, + "cohere-embed-v3-multilingual": { + id: "cohere-embed-v3-multilingual", + name: "Embed v3 Multilingual", + family: "cohere-embed", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2023-11-07", + last_updated: "2023-11-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0 }, + limit: { context: 512, output: 1024 }, + }, + "grok-4-20-non-reasoning": { + id: "grok-4-20-non-reasoning", + name: "Grok 4.20 (Non-Reasoning)", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-09", + release_date: "2026-04-08", + last_updated: "2026-04-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6 }, + limit: { context: 262000, output: 8192 }, + status: "beta", + }, + "gpt-5.1": { + id: "gpt-5.1", + name: "GPT-5.1", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-14", + last_updated: "2025-11-14", + modalities: { input: ["text", "image", "audio"], output: ["text", "image", "audio"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 272000, output: 128000 }, + }, + "grok-4-fast-reasoning": { + id: "grok-4-fast-reasoning", + name: "Grok 4 Fast (Reasoning)", + family: "grok", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-09-19", + last_updated: "2025-09-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + o1: { + id: "o1", + name: "o1", + family: "o", + attachment: false, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2023-09", + release_date: "2024-12-05", + last_updated: "2024-12-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 60, cache_read: 7.5 }, + limit: { context: 200000, output: 100000 }, + }, + "mistral-small-2503": { + id: "mistral-small-2503", + name: "Mistral Small 3.1", + family: "mistral-small", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-09", + release_date: "2025-03-01", + last_updated: "2025-03-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.3 }, + limit: { context: 128000, output: 32768 }, + }, + "model-router": { + id: "model-router", + name: "Model Router", + family: "model-router", + attachment: true, + reasoning: false, + tool_call: true, + release_date: "2025-05-19", + last_updated: "2025-11-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "gpt-3.5-turbo-1106": { + id: "gpt-3.5-turbo-1106", + name: "GPT-3.5 Turbo 1106", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2021-08", + release_date: "2023-11-06", + last_updated: "2023-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 2 }, + limit: { context: 16384, output: 16384 }, + }, + "text-embedding-3-small": { + id: "text-embedding-3-small", + name: "text-embedding-3-small", + family: "text-embedding", + attachment: false, + reasoning: false, + tool_call: false, + release_date: "2024-01-25", + last_updated: "2024-01-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.02, output: 0 }, + limit: { context: 8191, output: 1536 }, + }, + "deepseek-v3.1": { + id: "deepseek-v3.1", + name: "DeepSeek-V3.1", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-08-21", + last_updated: "2025-08-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.56, output: 1.68 }, + limit: { context: 131072, output: 131072 }, + }, + "claude-opus-4-5": { + id: "claude-opus-4-5", + name: "Claude Opus 4.5", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-24", + last_updated: "2025-08-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 64000 }, + provider: { + npm: "@ai-sdk/anthropic", + api: "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1", + }, + }, + "phi-3-mini-4k-instruct": { + id: "phi-3-mini-4k-instruct", + name: "Phi-3-mini-instruct (4k)", + family: "phi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-04-23", + last_updated: "2024-04-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.13, output: 0.52 }, + limit: { context: 4096, output: 1024 }, + }, + "meta-llama-3.1-70b-instruct": { + id: "meta-llama-3.1-70b-instruct", + name: "Meta-Llama-3.1-70B-Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.68, output: 3.54 }, + limit: { context: 128000, output: 32768 }, + }, + "phi-4-mini-reasoning": { + id: "phi-4-mini-reasoning", + name: "Phi-4-mini-reasoning", + family: "phi", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.075, output: 0.3 }, + limit: { context: 128000, output: 4096 }, + }, + "gpt-4": { + id: "gpt-4", + name: "GPT-4", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-11", + release_date: "2023-03-14", + last_updated: "2023-03-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 60, output: 120 }, + limit: { context: 8192, output: 8192 }, + }, + "meta-llama-3-8b-instruct": { + id: "meta-llama-3-8b-instruct", + name: "Meta-Llama-3-8B-Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-12", + release_date: "2024-04-18", + last_updated: "2024-04-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.61 }, + limit: { context: 8192, output: 2048 }, + }, + "kimi-k2.6": { + id: "kimi-k2.6", + name: "Kimi K2.6", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4 }, + limit: { context: 262144, output: 262144 }, + provider: { + npm: "@ai-sdk/openai-compatible", + api: "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/models", + shape: "completions", + }, + }, + "gpt-5-codex": { + id: "gpt-5-codex", + name: "GPT-5-Codex", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-09-15", + last_updated: "2025-09-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.13 }, + limit: { context: 400000, output: 128000 }, + }, + "phi-4-mini": { + id: "phi-4-mini", + name: "Phi-4-mini", + family: "phi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.075, output: 0.3 }, + limit: { context: 128000, output: 4096 }, + }, + "grok-4-20-reasoning": { + id: "grok-4-20-reasoning", + name: "Grok 4.20 (Reasoning)", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-09", + release_date: "2026-04-08", + last_updated: "2026-04-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 6 }, + limit: { context: 262000, output: 8192 }, + status: "beta", + }, + "gpt-3.5-turbo-0301": { + id: "gpt-3.5-turbo-0301", + name: "GPT-3.5 Turbo 0301", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2021-08", + release_date: "2023-03-01", + last_updated: "2023-03-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.5, output: 2 }, + limit: { context: 4096, output: 4096 }, + }, + "claude-opus-4-6": { + id: "claude-opus-4-6", + name: "Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 5, + output: 25, + cache_read: 0.5, + cache_write: 6.25, + context_over_200k: { input: 10, output: 37.5, cache_read: 1, cache_write: 12.5 }, + }, + limit: { context: 200000, output: 128000 }, + provider: { + npm: "@ai-sdk/anthropic", + api: "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1", + }, + }, + "phi-3-small-128k-instruct": { + id: "phi-3-small-128k-instruct", + name: "Phi-3-small-instruct (128k)", + family: "phi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-04-23", + last_updated: "2024-04-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 128000, output: 4096 }, + }, + "deepseek-v3.2": { + id: "deepseek-v3.2", + name: "DeepSeek-V3.2", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.58, output: 1.68 }, + limit: { context: 128000, output: 128000 }, + }, + "phi-3-medium-128k-instruct": { + id: "phi-3-medium-128k-instruct", + name: "Phi-3-medium-instruct (128k)", + family: "phi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-04-23", + last_updated: "2024-04-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.17, output: 0.68 }, + limit: { context: 128000, output: 4096 }, + }, + "gpt-3.5-turbo-0613": { + id: "gpt-3.5-turbo-0613", + name: "GPT-3.5 Turbo 0613", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2021-08", + release_date: "2023-06-13", + last_updated: "2023-06-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 4 }, + limit: { context: 16384, output: 16384 }, + }, + "claude-sonnet-4-5": { + id: "claude-sonnet-4-5", + name: "Claude Sonnet 4.5", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-11-18", + last_updated: "2025-11-18", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + provider: { + npm: "@ai-sdk/anthropic", + api: "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1", + }, + }, + "phi-4": { + id: "phi-4", + name: "Phi-4", + family: "phi", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.125, output: 0.5 }, + limit: { context: 128000, output: 4096 }, + }, + "gpt-5": { + id: "gpt-5", + name: "GPT-5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.13 }, + limit: { context: 272000, output: 128000 }, + }, + "gpt-4-32k": { + id: "gpt-4-32k", + name: "GPT-4 32K", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-11", + release_date: "2023-03-14", + last_updated: "2023-03-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 60, output: 120 }, + limit: { context: 32768, output: 32768 }, + }, + "cohere-embed-v3-english": { + id: "cohere-embed-v3-english", + name: "Embed v3 English", + family: "cohere-embed", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2023-11-07", + last_updated: "2023-11-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0 }, + limit: { context: 512, output: 1024 }, + }, + "phi-4-reasoning-plus": { + id: "phi-4-reasoning-plus", + name: "Phi-4-reasoning-plus", + family: "phi", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2023-10", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.125, output: 0.5 }, + limit: { context: 32000, output: 4096 }, + }, + "mistral-medium-2505": { + id: "mistral-medium-2505", + name: "Mistral Medium 3", + family: "mistral-medium", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-05", + release_date: "2025-05-07", + last_updated: "2025-05-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2 }, + limit: { context: 128000, output: 128000 }, + }, + "gpt-3.5-turbo-instruct": { + id: "gpt-3.5-turbo-instruct", + name: "GPT-3.5 Turbo Instruct", + family: "gpt", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2021-08", + release_date: "2023-09-21", + last_updated: "2023-09-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.5, output: 2 }, + limit: { context: 4096, output: 4096 }, + }, + "deepseek-r1-0528": { + id: "deepseek-r1-0528", + name: "DeepSeek-R1-0528", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-05-28", + last_updated: "2025-05-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.35, output: 5.4 }, + limit: { context: 163840, output: 163840 }, + }, + "kimi-k2-thinking": { + id: "kimi-k2-thinking", + name: "Kimi K2 Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-11-06", + last_updated: "2025-12-02", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5, cache_read: 0.15 }, + limit: { context: 262144, output: 262144 }, + }, + "gpt-5.1-codex": { + id: "gpt-5.1-codex", + name: "GPT-5.1 Codex", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-14", + last_updated: "2025-11-14", + modalities: { input: ["text", "image", "audio"], output: ["text", "image", "audio"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "codestral-2501": { + id: "codestral-2501", + name: "Codestral 25.01", + family: "codestral", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-03", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.9 }, + limit: { context: 256000, output: 256000 }, + }, + "llama-4-maverick-17b-128e-instruct-fp8": { + id: "llama-4-maverick-17b-128e-instruct-fp8", + name: "Llama 4 Maverick 17B 128E Instruct FP8", + family: "llama", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-04-05", + last_updated: "2025-04-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.25, output: 1 }, + limit: { context: 128000, output: 8192 }, + }, + "mai-ds-r1": { + id: "mai-ds-r1", + name: "MAI-DS-R1", + family: "mai", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2024-06", + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.35, output: 5.4 }, + limit: { context: 128000, output: 8192 }, + }, + "gpt-5.1-codex-max": { + id: "gpt-5.1-codex-max", + name: "GPT-5.1 Codex Max", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-11-13", + last_updated: "2025-11-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "claude-sonnet-4-6": { + id: "claude-sonnet-4-6", + name: "Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 1000000, output: 64000 }, + provider: { + npm: "@ai-sdk/anthropic", + api: "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1", + }, + }, + "gpt-5.5": { + id: "gpt-5.5", + name: "GPT-5.5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-12-01", + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 30, cache_read: 0.5, context_over_200k: { input: 10, output: 45, cache_read: 1 } }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "gpt-4-turbo": { + id: "gpt-4-turbo", + name: "GPT-4 Turbo", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2023-11-06", + last_updated: "2024-04-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 10, output: 30 }, + limit: { context: 128000, output: 4096 }, + }, + "gpt-4o-mini": { + id: "gpt-4o-mini", + name: "GPT-4o mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-09", + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6, cache_read: 0.08 }, + limit: { context: 128000, output: 16384 }, + }, + "gpt-5.4-mini": { + id: "gpt-5.4-mini", + name: "GPT-5.4 Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.75, output: 4.5, cache_read: 0.075 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "cohere-command-r-08-2024": { + id: "cohere-command-r-08-2024", + name: "Command R", + family: "command-r", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-06-01", + release_date: "2024-08-30", + last_updated: "2024-08-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 128000, output: 4000 }, + }, + "o4-mini": { + id: "o4-mini", + name: "o4-mini", + family: "o-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05", + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.28 }, + limit: { context: 200000, output: 100000 }, + }, + "gpt-5.4-nano": { + id: "gpt-5.4-nano", + name: "GPT-5.4 Nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.25, cache_read: 0.02 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "grok-code-fast-1": { + id: "grok-code-fast-1", + name: "Grok Code Fast 1", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2025-08-28", + last_updated: "2025-08-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 1.5, cache_read: 0.02 }, + limit: { context: 256000, output: 10000 }, + }, + "gpt-5.4-pro": { + id: "gpt-5.4-pro", + name: "GPT-5.4 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 30, output: 180, context_over_200k: { input: 60, output: 270 } }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "o3-mini": { + id: "o3-mini", + name: "o3-mini", + family: "o-mini", + attachment: false, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05", + release_date: "2024-12-20", + last_updated: "2025-01-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1.1, output: 4.4, cache_read: 0.55 }, + limit: { context: 200000, output: 100000 }, + }, + "grok-4": { + id: "grok-4", + name: "Grok 4", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, reasoning: 15, cache_read: 0.75 }, + limit: { context: 256000, output: 64000 }, + }, + "gpt-5.4": { + id: "gpt-5.4", + name: "GPT-5.4", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 2.5, + output: 15, + cache_read: 0.25, + context_over_200k: { input: 5, output: 22.5, cache_read: 0.5 }, + }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "gpt-4.1-nano": { + id: "gpt-4.1-nano", + name: "GPT-4.1 nano", + family: "gpt-nano", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.03 }, + limit: { context: 1047576, output: 32768 }, + }, + "grok-3-mini": { + id: "grok-3-mini", + name: "Grok 3 Mini", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 0.5, reasoning: 0.5, cache_read: 0.075 }, + limit: { context: 131072, output: 8192 }, + }, + o3: { + id: "o3", + name: "o3", + family: "o", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2024-05", + release_date: "2025-04-16", + last_updated: "2025-04-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 200000, output: 100000 }, + }, + "gpt-5-pro": { + id: "gpt-5-pro", + name: "GPT-5 Pro", + family: "gpt-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2025-10-06", + last_updated: "2025-10-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 120 }, + limit: { context: 400000, output: 272000 }, + }, + "gpt-4o": { + id: "gpt-4o", + name: "GPT-4o", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-09", + release_date: "2024-05-13", + last_updated: "2024-08-06", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2.5, output: 10, cache_read: 1.25 }, + limit: { context: 128000, output: 16384 }, + }, + "cohere-command-r-plus-08-2024": { + id: "cohere-command-r-plus-08-2024", + name: "Command R+", + family: "command-r", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-06-01", + release_date: "2024-08-30", + last_updated: "2024-08-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.5, output: 10 }, + limit: { context: 128000, output: 4000 }, + }, + "gpt-4.1": { + id: "gpt-4.1", + name: "GPT-4.1", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 1047576, output: 32768 }, + }, + "gpt-4.1-mini": { + id: "gpt-4.1-mini", + name: "GPT-4.1 mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 1.6, cache_read: 0.1 }, + limit: { context: 1047576, output: 32768 }, + }, + "grok-3": { + id: "grok-3", + name: "Grok 3", + family: "grok", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-11", + release_date: "2025-02-17", + last_updated: "2025-02-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.75 }, + limit: { context: 131072, output: 8192 }, + }, + "grok-4-fast-non-reasoning": { + id: "grok-4-fast-non-reasoning", + name: "Grok 4 Fast (Non-Reasoning)", + family: "grok", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-09-19", + last_updated: "2025-09-19", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.2, output: 0.5, cache_read: 0.05 }, + limit: { context: 2000000, output: 30000 }, + }, + }, + }, + fastrouter: { + id: "fastrouter", + env: ["FASTROUTER_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://go.fastrouter.ai/api/v1", + name: "FastRouter", + doc: "https://fastrouter.ai/models", + models: { + "x-ai/grok-4": { + id: "x-ai/grok-4", + name: "Grok 4", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.75, cache_write: 15 }, + limit: { context: 256000, output: 64000 }, + }, + "deepseek-ai/deepseek-r1-distill-llama-70b": { + id: "deepseek-ai/deepseek-r1-distill-llama-70b", + name: "DeepSeek R1 Distill Llama 70B", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2025-01-23", + last_updated: "2025-01-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.14 }, + limit: { context: 131072, output: 131072 }, + }, + "openai/gpt-5-mini": { + id: "openai/gpt-5-mini", + name: "GPT-5 Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10-01", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2, cache_read: 0.025 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-5-nano": { + id: "openai/gpt-5-nano", + name: "GPT-5 Nano", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10-01", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.4, cache_read: 0.005 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-oss-20b": { + id: "openai/gpt-oss-20b", + name: "GPT OSS 20B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.2 }, + limit: { context: 131072, output: 65536 }, + }, + "openai/gpt-5": { + id: "openai/gpt-5", + name: "GPT-5", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10-01", + release_date: "2025-08-07", + last_updated: "2025-08-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.125 }, + limit: { context: 400000, output: 128000 }, + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "GPT OSS 120B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 131072, output: 32768 }, + }, + "z-ai/glm-5": { + id: "z-ai/glm-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 3.15 }, + limit: { context: 204800, output: 131072 }, + }, + "qwen/qwen3-coder": { + id: "qwen/qwen3-coder", + name: "Qwen3 Coder", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 262144, output: 66536 }, + }, + "google/gemini-2.5-pro": { + id: "google/gemini-2.5-pro", + name: "Gemini 2.5 Pro", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.31 }, + limit: { context: 1048576, output: 65536 }, + }, + "google/gemini-2.5-flash": { + id: "google/gemini-2.5-flash", + name: "Gemini 2.5 Flash", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5, cache_read: 0.0375 }, + limit: { context: 1048576, output: 65536 }, + }, + "moonshotai/kimi-k2": { + id: "moonshotai/kimi-k2", + name: "Kimi K2", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-07-11", + last_updated: "2025-07-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 2.2 }, + limit: { context: 131072, output: 32768 }, + }, + "openai/gpt-4.1": { + id: "openai/gpt-4.1", + name: "GPT-4.1", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 8, cache_read: 0.5 }, + limit: { context: 1047576, output: 32768 }, + }, + "anthropic/claude-opus-4.1": { + id: "anthropic/claude-opus-4.1", + name: "Claude Opus 4.1", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "anthropic/claude-sonnet-4": { + id: "anthropic/claude-sonnet-4", + name: "Claude Sonnet 4", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + }, + }, + stackit: { + id: "stackit", + env: ["STACKIT_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.openai-compat.model-serving.eu01.onstackit.cloud/v1", + name: "STACKIT", + doc: "https://docs.stackit.cloud/products/data-and-ai/ai-model-serving/basics/available-shared-models", + models: { + "Qwen/Qwen3-VL-Embedding-8B": { + id: "Qwen/Qwen3-VL-Embedding-8B", + name: "Qwen3-VL Embedding 8B", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: false, + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.09, output: 0.09 }, + limit: { context: 32000, output: 4096 }, + }, + "Qwen/Qwen3-VL-235B-A22B-Instruct-FP8": { + id: "Qwen/Qwen3-VL-235B-A22B-Instruct-FP8", + name: "Qwen3-VL 235B", + family: "qwen", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2024-11-01", + last_updated: "2024-11-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 1.64, output: 1.91 }, + limit: { context: 218000, output: 8192 }, + }, + "neuralmagic/Meta-Llama-3.1-8B-Instruct-FP8": { + id: "neuralmagic/Meta-Llama-3.1-8B-Instruct-FP8", + name: "Llama 3.1 8B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.16, output: 0.27 }, + limit: { context: 128000, output: 8192 }, + }, + "neuralmagic/Mistral-Nemo-Instruct-2407-FP8": { + id: "neuralmagic/Mistral-Nemo-Instruct-2407-FP8", + name: "Mistral Nemo", + family: "mistral", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2024-07-01", + last_updated: "2024-07-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.49, output: 0.71 }, + limit: { context: 128000, output: 8192 }, + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "GPT-OSS 120B", + family: "gpt", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.49, output: 0.71 }, + limit: { context: 131000, output: 8192 }, + }, + "cortecs/Llama-3.3-70B-Instruct-FP8-Dynamic": { + id: "cortecs/Llama-3.3-70B-Instruct-FP8-Dynamic", + name: "Llama 3.3 70B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2024-12-05", + last_updated: "2024-12-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.49, output: 0.71 }, + limit: { context: 128000, output: 8192 }, + }, + "google/gemma-3-27b-it": { + id: "google/gemma-3-27b-it", + name: "Gemma 3 27B", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + release_date: "2025-05-17", + last_updated: "2025-05-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.49, output: 0.71 }, + limit: { context: 37000, output: 8192 }, + }, + "intfloat/e5-mistral-7b-instruct": { + id: "intfloat/e5-mistral-7b-instruct", + name: "E5 Mistral 7B", + family: "mistral", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: false, + release_date: "2023-12-11", + last_updated: "2023-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.02, output: 0.02 }, + limit: { context: 4096, output: 4096 }, + }, + }, + }, + "tencent-coding-plan": { + id: "tencent-coding-plan", + env: ["TENCENT_CODING_PLAN_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.lkeap.cloud.tencent.com/coding/v3", + name: "Tencent Coding Plan (China)", + doc: "https://cloud.tencent.com/document/product/1772/128947", + models: { + "kimi-k2.5": { + id: "kimi-k2.5", + name: "Kimi-K2.5", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-01", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 262144, output: 32768 }, + }, + "glm-5": { + id: "glm-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 202752, output: 16384 }, + }, + "hunyuan-turbos": { + id: "hunyuan-turbos", + name: "Hunyuan-TurboS", + family: "hunyuan", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-03-08", + last_updated: "2026-03-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 131072, output: 16384 }, + }, + "hunyuan-t1": { + id: "hunyuan-t1", + name: "Hunyuan-T1", + family: "hunyuan", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-03-08", + last_updated: "2026-03-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 131072, output: 16384 }, + }, + "hunyuan-2.0-instruct": { + id: "hunyuan-2.0-instruct", + name: "Tencent HY 2.0 Instruct", + family: "hunyuan", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-03-08", + last_updated: "2026-03-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 131072, output: 16384 }, + }, + "minimax-m2.5": { + id: "minimax-m2.5", + name: "MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 204800, output: 32768 }, + }, + "tc-code-latest": { + id: "tc-code-latest", + name: "Auto", + family: "auto", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-03-08", + last_updated: "2026-03-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 131072, output: 16384 }, + }, + "hunyuan-2.0-thinking": { + id: "hunyuan-2.0-thinking", + name: "Tencent HY 2.0 Think", + family: "hunyuan", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-03-08", + last_updated: "2026-03-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 131072, output: 16384 }, + }, + }, + }, + "privatemode-ai": { + id: "privatemode-ai", + env: ["PRIVATEMODE_API_KEY", "PRIVATEMODE_ENDPOINT"], + npm: "@ai-sdk/openai-compatible", + api: "http://localhost:8080/v1", + name: "Privatemode AI", + doc: "https://docs.privatemode.ai/api/overview", + models: { + "gemma-3-27b": { + id: "gemma-3-27b", + name: "Gemma 3 27B", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-08", + release_date: "2025-03-12", + last_updated: "2025-03-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 8192 }, + }, + "whisper-large-v3": { + id: "whisper-large-v3", + name: "Whisper large-v3", + family: "whisper", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + knowledge: "2023-09", + release_date: "2023-09-01", + last_updated: "2023-09-01", + modalities: { input: ["audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 0, output: 4096 }, + }, + "qwen3-embedding-4b": { + id: "qwen3-embedding-4b", + name: "Qwen3-Embedding 4B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + structured_output: false, + temperature: true, + knowledge: "2025-06", + release_date: "2025-06-06", + last_updated: "2025-06-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 32000, output: 2560 }, + }, + "gpt-oss-120b": { + id: "gpt-oss-120b", + name: "gpt-oss-120b", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-08", + release_date: "2025-08-04", + last_updated: "2025-08-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 128000 }, + }, + "qwen3-coder-30b-a3b": { + id: "qwen3-coder-30b-a3b", + name: "Qwen3-Coder 30B-A3B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04", + last_updated: "2025-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 32768 }, + }, + }, + }, + google: { + id: "google", + env: ["GOOGLE_GENERATIVE_AI_API_KEY", "GEMINI_API_KEY"], + npm: "@ai-sdk/google", + name: "Google", + doc: "https://ai.google.dev/gemini-api/docs/models", + models: { + "gemini-flash-lite-latest": { + id: "gemini-flash-lite-latest", + name: "Gemini Flash-Lite Latest", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.025 }, + limit: { context: 1048576, output: 65536 }, + }, + "gemini-2.5-pro-preview-05-06": { + id: "gemini-2.5-pro-preview-05-06", + name: "Gemini 2.5 Pro Preview 05-06", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-05-06", + last_updated: "2025-05-06", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.31 }, + limit: { context: 1048576, output: 65536 }, + }, + "gemini-live-2.5-flash-preview-native-audio": { + id: "gemini-live-2.5-flash-preview-native-audio", + name: "Gemini Live 2.5 Flash Preview Native Audio", + family: "gemini-flash", + attachment: false, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2025-01", + release_date: "2025-06-17", + last_updated: "2025-09-18", + modalities: { input: ["text", "audio", "video"], output: ["text", "audio"] }, + open_weights: false, + cost: { input: 0.5, output: 2, input_audio: 3, output_audio: 12 }, + limit: { context: 131072, output: 65536 }, + }, + "gemini-3.1-pro-preview-customtools": { + id: "gemini-3.1-pro-preview-customtools", + name: "Gemini 3.1 Pro Preview Custom Tools", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-19", + last_updated: "2026-02-19", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2, context_over_200k: { input: 4, output: 18, cache_read: 0.4 } }, + limit: { context: 1048576, output: 65536 }, + }, + "gemini-2.5-flash-lite-preview-09-2025": { + id: "gemini-2.5-flash-lite-preview-09-2025", + name: "Gemini 2.5 Flash Lite Preview 09-25", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.025 }, + limit: { context: 1048576, output: 65536 }, + }, + "gemini-1.5-flash": { + id: "gemini-1.5-flash", + name: "Gemini 1.5 Flash", + family: "gemini-flash", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-05-14", + last_updated: "2024-05-14", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.075, output: 0.3, cache_read: 0.01875 }, + limit: { context: 1000000, output: 8192 }, + }, + "gemini-1.5-pro": { + id: "gemini-1.5-pro", + name: "Gemini 1.5 Pro", + family: "gemini-pro", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-02-15", + last_updated: "2024-02-15", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 5, cache_read: 0.3125 }, + limit: { context: 1000000, output: 8192 }, + }, + "gemma-3n-e4b-it": { + id: "gemma-3n-e4b-it", + name: "Gemma 3n 4B", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2025-05-20", + last_updated: "2025-05-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 8192, output: 2000 }, + }, + "gemini-3.1-flash-lite-preview": { + id: "gemini-3.1-flash-lite-preview", + name: "Gemini 3.1 Flash Lite Preview", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-03-03", + last_updated: "2026-03-03", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.5, cache_read: 0.025, cache_write: 1 }, + limit: { context: 1048576, output: 65536 }, + }, + "gemini-3.1-pro-preview": { + id: "gemini-3.1-pro-preview", + name: "Gemini 3.1 Pro Preview", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-19", + last_updated: "2026-02-19", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2, context_over_200k: { input: 4, output: 18, cache_read: 0.4 } }, + limit: { context: 1048576, output: 65536 }, + }, + "gemini-2.0-flash": { + id: "gemini-2.0-flash", + name: "Gemini 2.0 Flash", + family: "gemini-flash", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-06", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.025 }, + limit: { context: 1048576, output: 8192 }, + }, + "gemini-3-flash-preview": { + id: "gemini-3-flash-preview", + name: "Gemini 3 Flash Preview", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 0.5, + output: 3, + cache_read: 0.05, + context_over_200k: { input: 0.5, output: 3, cache_read: 0.05 }, + }, + limit: { context: 1048576, output: 65536 }, + }, + "gemini-2.5-flash-preview-tts": { + id: "gemini-2.5-flash-preview-tts", + name: "Gemini 2.5 Flash Preview TTS", + family: "gemini-flash", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + knowledge: "2025-01", + release_date: "2025-05-01", + last_updated: "2025-05-01", + modalities: { input: ["text"], output: ["audio"] }, + open_weights: false, + cost: { input: 0.5, output: 10 }, + limit: { context: 8000, output: 16000 }, + }, + "gemini-3-pro-preview": { + id: "gemini-3-pro-preview", + name: "Gemini 3 Pro Preview", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-11-18", + last_updated: "2025-11-18", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 2, output: 12, cache_read: 0.2, context_over_200k: { input: 4, output: 18, cache_read: 0.4 } }, + limit: { context: 1000000, output: 64000 }, + }, + "gemini-2.5-flash-preview-05-20": { + id: "gemini-2.5-flash-preview-05-20", + name: "Gemini 2.5 Flash Preview 05-20", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-05-20", + last_updated: "2025-05-20", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6, cache_read: 0.0375 }, + limit: { context: 1048576, output: 65536 }, + }, + "gemini-embedding-001": { + id: "gemini-embedding-001", + name: "Gemini Embedding 001", + family: "gemini", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + knowledge: "2025-05", + release_date: "2025-05-20", + last_updated: "2025-05-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0 }, + limit: { context: 2048, output: 3072 }, + }, + "gemini-2.5-pro": { + id: "gemini-2.5-pro", + name: "Gemini 2.5 Pro", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-20", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { + input: 1.25, + output: 10, + cache_read: 0.125, + context_over_200k: { input: 2.5, output: 15, cache_read: 0.25 }, + }, + limit: { context: 1048576, output: 65536 }, + }, + "gemini-flash-latest": { + id: "gemini-flash-latest", + name: "Gemini Flash Latest", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5, cache_read: 0.075, input_audio: 1 }, + limit: { context: 1048576, output: 65536 }, + }, + "gemma-4-31b-it": { + id: "gemma-4-31b-it", + name: "Gemma 4 31B", + family: "gemma", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + limit: { context: 256000, output: 8192 }, + }, + "gemini-2.5-pro-preview-06-05": { + id: "gemini-2.5-pro-preview-06-05", + name: "Gemini 2.5 Pro Preview 06-05", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-06-05", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1.25, output: 10, cache_read: 0.31 }, + limit: { context: 1048576, output: 65536 }, + }, + "gemini-2.5-flash-image": { + id: "gemini-2.5-flash-image", + name: "Gemini 2.5 Flash Image", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2025-06", + release_date: "2025-08-26", + last_updated: "2025-08-26", + modalities: { input: ["text", "image"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 0.3, output: 30, cache_read: 0.075 }, + limit: { context: 32768, output: 32768 }, + }, + "gemini-2.5-flash-lite-preview-06-17": { + id: "gemini-2.5-flash-lite-preview-06-17", + name: "Gemini 2.5 Flash Lite Preview 06-17", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.025, input_audio: 0.3 }, + limit: { context: 1048576, output: 65536 }, + }, + "gemma-3-12b-it": { + id: "gemma-3-12b-it", + name: "Gemma 3 12B", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + structured_output: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-03-13", + last_updated: "2025-03-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 32768, output: 8192 }, + }, + "gemini-2.5-flash": { + id: "gemini-2.5-flash", + name: "Gemini 2.5 Flash", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-03-20", + last_updated: "2025-06-05", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5, cache_read: 0.03, input_audio: 1 }, + limit: { context: 1048576, output: 65536 }, + }, + "gemma-3n-e2b-it": { + id: "gemma-3n-e2b-it", + name: "Gemma 3n 2B", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2025-07-09", + last_updated: "2025-07-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 8192, output: 2000 }, + }, + "gemini-3.1-flash-image-preview": { + id: "gemini-3.1-flash-image-preview", + name: "Gemini 3.1 Flash Image (Preview)", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2025-01", + release_date: "2026-02-26", + last_updated: "2026-02-26", + modalities: { input: ["text", "image", "pdf"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 0.25, output: 60 }, + limit: { context: 131072, output: 32768 }, + }, + "gemini-3.1-flash-lite": { + id: "gemini-3.1-flash-lite", + name: "Gemini 3.1 Flash Lite", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-05-07", + last_updated: "2026-05-07", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.5, cache_read: 0.025, cache_write: 1 }, + limit: { context: 1048576, output: 65536 }, + }, + "gemma-3-4b-it": { + id: "gemma-3-4b-it", + name: "Gemma 3 4B", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + knowledge: "2024-10", + release_date: "2025-03-13", + last_updated: "2025-03-13", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 32768, output: 8192 }, + }, + "gemini-2.5-flash-preview-04-17": { + id: "gemini-2.5-flash-preview-04-17", + name: "Gemini 2.5 Flash Preview 04-17", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-04-17", + last_updated: "2025-04-17", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.15, output: 0.6, cache_read: 0.0375 }, + limit: { context: 1048576, output: 65536 }, + }, + "gemini-2.5-pro-preview-tts": { + id: "gemini-2.5-pro-preview-tts", + name: "Gemini 2.5 Pro Preview TTS", + family: "gemini-flash", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + knowledge: "2025-01", + release_date: "2025-05-01", + last_updated: "2025-05-01", + modalities: { input: ["text"], output: ["audio"] }, + open_weights: false, + cost: { input: 1, output: 20 }, + limit: { context: 8000, output: 16000 }, + }, + "gemini-2.5-flash-preview-09-2025": { + id: "gemini-2.5-flash-preview-09-2025", + name: "Gemini 2.5 Flash Preview 09-25", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-09-25", + last_updated: "2025-09-25", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.3, output: 2.5, cache_read: 0.075, input_audio: 1 }, + limit: { context: 1048576, output: 65536 }, + }, + "gemma-3-27b-it": { + id: "gemma-3-27b-it", + name: "Gemma 3 27B", + family: "gemma", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-03-12", + last_updated: "2025-03-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 8192 }, + }, + "gemma-4-26b-a4b-it": { + id: "gemma-4-26b-a4b-it", + name: "Gemma 4 26B", + family: "gemma", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + limit: { context: 256000, output: 8192 }, + }, + "gemini-2.5-flash-lite": { + id: "gemini-2.5-flash-lite", + name: "Gemini 2.5 Flash Lite", + family: "gemini-flash-lite", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-06-17", + last_updated: "2025-06-17", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.1, output: 0.4, cache_read: 0.025 }, + limit: { context: 1048576, output: 65536 }, + }, + "gemini-2.5-flash-image-preview": { + id: "gemini-2.5-flash-image-preview", + name: "Gemini 2.5 Flash Image (Preview)", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2025-06", + release_date: "2025-08-26", + last_updated: "2025-08-26", + modalities: { input: ["text", "image"], output: ["text", "image"] }, + open_weights: false, + cost: { input: 0.3, output: 30, cache_read: 0.075 }, + limit: { context: 32768, output: 32768 }, + }, + "gemini-1.5-flash-8b": { + id: "gemini-1.5-flash-8b", + name: "Gemini 1.5 Flash-8B", + family: "gemini-flash", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2024-10-03", + last_updated: "2024-10-03", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.0375, output: 0.15, cache_read: 0.01 }, + limit: { context: 1000000, output: 8192 }, + }, + "gemini-live-2.5-flash": { + id: "gemini-live-2.5-flash", + name: "Gemini Live 2.5 Flash", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-09-01", + last_updated: "2025-09-01", + modalities: { input: ["text", "image", "audio", "video"], output: ["text", "audio"] }, + open_weights: false, + cost: { input: 0.5, output: 2, input_audio: 3, output_audio: 12 }, + limit: { context: 128000, output: 8000 }, + }, + "gemini-2.0-flash-lite": { + id: "gemini-2.0-flash-lite", + name: "Gemini 2.0 Flash Lite", + family: "gemini-flash-lite", + attachment: true, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2024-06", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.075, output: 0.3 }, + limit: { context: 1048576, output: 8192 }, + }, + }, + }, + drun: { + id: "drun", + env: ["DRUN_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://chat.d.run/v1", + name: "D.Run (China)", + doc: "https://www.d.run", + models: { + "public/deepseek-r1": { + id: "public/deepseek-r1", + name: "DeepSeek R1", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.55, output: 2.2 }, + limit: { context: 131072, output: 32000 }, + }, + "public/minimax-m25": { + id: "public/minimax-m25", + name: "MiniMax M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_details" }, + temperature: true, + release_date: "2025-03-01", + last_updated: "2025-03-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.29, output: 1.16 }, + limit: { context: 204800, output: 131072 }, + }, + "public/deepseek-v3": { + id: "public/deepseek-v3", + name: "DeepSeek V3", + family: "deepseek", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2024-12-26", + last_updated: "2024-12-26", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.28, output: 1.1 }, + limit: { context: 131072, output: 8192 }, + }, + }, + }, + moonshotai: { + id: "moonshotai", + env: ["MOONSHOT_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.moonshot.ai/v1", + name: "Moonshot AI", + doc: "https://platform.moonshot.ai/docs/api/chat", + models: { + "kimi-k2-0905-preview": { + id: "kimi-k2-0905-preview", + name: "Kimi K2 0905", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5, cache_read: 0.15 }, + limit: { context: 262144, output: 262144 }, + }, + "kimi-k2.5": { + id: "kimi-k2.5", + name: "Kimi K2.5", + family: "kimi-k2.5", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: false, + knowledge: "2025-01", + release_date: "2026-01", + last_updated: "2026-01", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3, cache_read: 0.1 }, + limit: { context: 262144, output: 262144 }, + }, + "kimi-k2-thinking-turbo": { + id: "kimi-k2-thinking-turbo", + name: "Kimi K2 Thinking Turbo", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-08", + release_date: "2025-11-06", + last_updated: "2025-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.15, output: 8, cache_read: 0.15 }, + limit: { context: 262144, output: 262144 }, + }, + "kimi-k2.6": { + id: "kimi-k2.6", + name: "Kimi K2.6", + family: "kimi-k2.6", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4, cache_read: 0.16 }, + limit: { context: 262144, output: 262144 }, + }, + "kimi-k2-turbo-preview": { + id: "kimi-k2-turbo-preview", + name: "Kimi K2 Turbo", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-09-05", + last_updated: "2025-09-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.4, output: 10, cache_read: 0.6 }, + limit: { context: 262144, output: 262144 }, + }, + "kimi-k2-0711-preview": { + id: "kimi-k2-0711-preview", + name: "Kimi K2 0711", + family: "kimi", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-07-14", + last_updated: "2025-07-14", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5, cache_read: 0.15 }, + limit: { context: 131072, output: 16384 }, + }, + "kimi-k2-thinking": { + id: "kimi-k2-thinking", + name: "Kimi K2 Thinking", + family: "kimi-thinking", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-08", + release_date: "2025-11-06", + last_updated: "2025-11-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.5, cache_read: 0.15 }, + limit: { context: 262144, output: 262144 }, + }, + }, + }, + berget: { + id: "berget", + env: ["BERGET_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.berget.ai/v1", + name: "Berget.AI", + doc: "https://api.berget.ai", + models: { + "zai-org/GLM-4.7": { + id: "zai-org/GLM-4.7", + name: "GLM 4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-12", + release_date: "2026-01-19", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.77, output: 2.75 }, + limit: { context: 128000, output: 8192 }, + }, + "mistralai/Mistral-Small-3.2-24B-Instruct-2506": { + id: "mistralai/Mistral-Small-3.2-24B-Instruct-2506", + name: "Mistral Small 3.2 24B Instruct 2506", + family: "mistral-small", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-09", + release_date: "2025-10-01", + last_updated: "2025-10-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.33, output: 0.33 }, + limit: { context: 32000, output: 8192 }, + }, + "mistralai/Mistral-Medium-3.5-128B": { + id: "mistralai/Mistral-Medium-3.5-128B", + name: "Mistral Medium 3.5 128B", + family: "mistral-medium", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2026-04", + release_date: "2026-04-29", + last_updated: "2026-04-29", + modalities: { input: ["image", "text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.65, output: 5.5 }, + limit: { context: 262144, output: 131072 }, + }, + "meta-llama/Llama-3.3-70B-Instruct": { + id: "meta-llama/Llama-3.3-70B-Instruct", + name: "Llama 3.3 70B Instruct", + family: "llama", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2023-12", + release_date: "2025-04-27", + last_updated: "2025-04-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.99, output: 0.99 }, + limit: { context: 128000, output: 8192 }, + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "GPT-OSS-120B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-08", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.44, output: 0.99 }, + limit: { context: 128000, output: 8192 }, + }, + "google/gemma-4-31B-it": { + id: "google/gemma-4-31B-it", + name: "Gemma 4 31B Instruct", + family: "gemma", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-12", + release_date: "2026-04-02", + last_updated: "2026-04-02", + modalities: { input: ["audio", "image", "text", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.275, output: 0.55 }, + limit: { context: 128000, output: 8192 }, + }, + }, + }, + "github-models": { + id: "github-models", + env: ["GITHUB_TOKEN"], + npm: "@ai-sdk/openai-compatible", + api: "https://models.github.ai/inference", + name: "GitHub Models", + doc: "https://docs.github.com/en/github-models", + models: { + "deepseek/deepseek-v3-0324": { + id: "deepseek/deepseek-v3-0324", + name: "DeepSeek-V3-0324", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-06", + release_date: "2025-03-24", + last_updated: "2025-03-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 8192 }, + }, + "deepseek/deepseek-r1": { + id: "deepseek/deepseek-r1", + name: "DeepSeek-R1", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-06", + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 65536, output: 8192 }, + }, + "deepseek/deepseek-r1-0528": { + id: "deepseek/deepseek-r1-0528", + name: "DeepSeek-R1-0528", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-06", + release_date: "2025-05-28", + last_updated: "2025-05-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 65536, output: 8192 }, + }, + "ai21-labs/ai21-jamba-1.5-mini": { + id: "ai21-labs/ai21-jamba-1.5-mini", + name: "AI21 Jamba 1.5 Mini", + family: "jamba", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-03", + release_date: "2024-08-29", + last_updated: "2024-08-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 256000, output: 4096 }, + }, + "ai21-labs/ai21-jamba-1.5-large": { + id: "ai21-labs/ai21-jamba-1.5-large", + name: "AI21 Jamba 1.5 Large", + family: "jamba", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-03", + release_date: "2024-08-29", + last_updated: "2024-08-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 256000, output: 4096 }, + }, + "microsoft/phi-3.5-mini-instruct": { + id: "microsoft/phi-3.5-mini-instruct", + name: "Phi-3.5-mini instruct (128k)", + family: "phi", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-08-20", + last_updated: "2024-08-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "microsoft/phi-3-medium-4k-instruct": { + id: "microsoft/phi-3-medium-4k-instruct", + name: "Phi-3-medium instruct (4k)", + family: "phi", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-04-23", + last_updated: "2024-04-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 4096, output: 1024 }, + }, + "microsoft/phi-3.5-moe-instruct": { + id: "microsoft/phi-3.5-moe-instruct", + name: "Phi-3.5-MoE instruct (128k)", + family: "phi", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-08-20", + last_updated: "2024-08-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "microsoft/phi-3-mini-128k-instruct": { + id: "microsoft/phi-3-mini-128k-instruct", + name: "Phi-3-mini instruct (128k)", + family: "phi", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-04-23", + last_updated: "2024-04-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "microsoft/phi-4-mini-instruct": { + id: "microsoft/phi-4-mini-instruct", + name: "Phi-4-mini-instruct", + family: "phi", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "microsoft/phi-4-reasoning": { + id: "microsoft/phi-4-reasoning", + name: "Phi-4-Reasoning", + family: "phi", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "microsoft/phi-3-small-8k-instruct": { + id: "microsoft/phi-3-small-8k-instruct", + name: "Phi-3-small instruct (8k)", + family: "phi", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-04-23", + last_updated: "2024-04-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 8192, output: 2048 }, + }, + "microsoft/phi-3.5-vision-instruct": { + id: "microsoft/phi-3.5-vision-instruct", + name: "Phi-3.5-vision instruct (128k)", + family: "phi", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-08-20", + last_updated: "2024-08-20", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "microsoft/phi-3-mini-4k-instruct": { + id: "microsoft/phi-3-mini-4k-instruct", + name: "Phi-3-mini instruct (4k)", + family: "phi", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-04-23", + last_updated: "2024-04-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 4096, output: 1024 }, + }, + "microsoft/phi-4-mini-reasoning": { + id: "microsoft/phi-4-mini-reasoning", + name: "Phi-4-mini-reasoning", + family: "phi", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "microsoft/phi-3-small-128k-instruct": { + id: "microsoft/phi-3-small-128k-instruct", + name: "Phi-3-small instruct (128k)", + family: "phi", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-04-23", + last_updated: "2024-04-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "microsoft/phi-3-medium-128k-instruct": { + id: "microsoft/phi-3-medium-128k-instruct", + name: "Phi-3-medium instruct (128k)", + family: "phi", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-04-23", + last_updated: "2024-04-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "microsoft/phi-4": { + id: "microsoft/phi-4", + name: "Phi-4", + family: "phi", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 16000, output: 4096 }, + }, + "microsoft/phi-4-multimodal-instruct": { + id: "microsoft/phi-4-multimodal-instruct", + name: "Phi-4-multimodal-instruct", + family: "phi", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-12-11", + last_updated: "2024-12-11", + modalities: { input: ["text", "image", "audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "microsoft/mai-ds-r1": { + id: "microsoft/mai-ds-r1", + name: "MAI-DS-R1", + family: "mai", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-06", + release_date: "2025-01-20", + last_updated: "2025-01-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 65536, output: 8192 }, + }, + "cohere/cohere-command-r-08-2024": { + id: "cohere/cohere-command-r-08-2024", + name: "Cohere Command R 08-2024", + family: "command-r", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-03", + release_date: "2024-08-01", + last_updated: "2024-08-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "cohere/cohere-command-a": { + id: "cohere/cohere-command-a", + name: "Cohere Command A", + family: "command-a", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-03", + release_date: "2024-11-01", + last_updated: "2024-11-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "cohere/cohere-command-r-plus": { + id: "cohere/cohere-command-r-plus", + name: "Cohere Command R+", + family: "command-r", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-03", + release_date: "2024-04-04", + last_updated: "2024-08-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "cohere/cohere-command-r": { + id: "cohere/cohere-command-r", + name: "Cohere Command R", + family: "command-r", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-03", + release_date: "2024-03-11", + last_updated: "2024-08-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "cohere/cohere-command-r-plus-08-2024": { + id: "cohere/cohere-command-r-plus-08-2024", + name: "Cohere Command R+ 08-2024", + family: "command-r", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-03", + release_date: "2024-08-01", + last_updated: "2024-08-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 4096 }, + }, + "xai/grok-3-mini": { + id: "xai/grok-3-mini", + name: "Grok 3 Mini", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-12-09", + last_updated: "2024-12-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 8192 }, + }, + "xai/grok-3": { + id: "xai/grok-3", + name: "Grok 3", + family: "grok", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2024-12-09", + last_updated: "2024-12-09", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 8192 }, + }, + "openai/o1-mini": { + id: "openai/o1-mini", + name: "OpenAI o1-mini", + family: "o-mini", + attachment: false, + reasoning: true, + tool_call: false, + temperature: false, + knowledge: "2023-10", + release_date: "2024-09-12", + last_updated: "2024-12-17", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 65536 }, + }, + "openai/gpt-4o-mini": { + id: "openai/gpt-4o-mini", + name: "GPT-4o mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text", "image", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/o4-mini": { + id: "openai/o4-mini", + name: "OpenAI o4-mini", + family: "o-mini", + attachment: false, + reasoning: true, + tool_call: false, + temperature: false, + knowledge: "2024-04", + release_date: "2025-01-31", + last_updated: "2025-01-31", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/o1-preview": { + id: "openai/o1-preview", + name: "OpenAI o1-preview", + family: "o", + attachment: false, + reasoning: true, + tool_call: false, + temperature: false, + knowledge: "2023-10", + release_date: "2024-09-12", + last_updated: "2024-09-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 32768 }, + }, + "openai/o1": { + id: "openai/o1", + name: "OpenAI o1", + family: "o", + attachment: false, + reasoning: true, + tool_call: false, + temperature: false, + knowledge: "2023-10", + release_date: "2024-09-12", + last_updated: "2024-12-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/o3-mini": { + id: "openai/o3-mini", + name: "OpenAI o3-mini", + family: "o-mini", + attachment: false, + reasoning: true, + tool_call: false, + temperature: false, + knowledge: "2024-04", + release_date: "2025-01-31", + last_updated: "2025-01-31", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-4.1-nano": { + id: "openai/gpt-4.1-nano", + name: "GPT-4.1-nano", + family: "gpt-nano", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/o3": { + id: "openai/o3", + name: "OpenAI o3", + family: "o", + attachment: false, + reasoning: true, + tool_call: false, + temperature: false, + knowledge: "2024-04", + release_date: "2025-01-31", + last_updated: "2025-01-31", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 200000, output: 100000 }, + }, + "openai/gpt-4o": { + id: "openai/gpt-4o", + name: "GPT-4o", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-10", + release_date: "2024-05-13", + last_updated: "2024-05-13", + modalities: { input: ["text", "image", "audio"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-4.1": { + id: "openai/gpt-4.1", + name: "GPT-4.1", + family: "gpt", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "openai/gpt-4.1-mini": { + id: "openai/gpt-4.1-mini", + name: "GPT-4.1-mini", + family: "gpt-mini", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04", + release_date: "2025-04-14", + last_updated: "2025-04-14", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 16384 }, + }, + "meta/llama-4-scout-17b-16e-instruct": { + id: "meta/llama-4-scout-17b-16e-instruct", + name: "Llama 4 Scout 17B 16E Instruct", + family: "llama", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-01-31", + last_updated: "2025-01-31", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 8192 }, + }, + "meta/meta-llama-3.1-8b-instruct": { + id: "meta/meta-llama-3.1-8b-instruct", + name: "Meta-Llama-3.1-8B-Instruct", + family: "llama", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 32768 }, + }, + "meta/llama-3.3-70b-instruct": { + id: "meta/llama-3.3-70b-instruct", + name: "Llama-3.3-70B-Instruct", + family: "llama", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 32768 }, + }, + "meta/meta-llama-3-70b-instruct": { + id: "meta/meta-llama-3-70b-instruct", + name: "Meta-Llama-3-70B-Instruct", + family: "llama", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-04-18", + last_updated: "2024-04-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 8192, output: 2048 }, + }, + "meta/llama-3.2-90b-vision-instruct": { + id: "meta/llama-3.2-90b-vision-instruct", + name: "Llama-3.2-90B-Vision-Instruct", + family: "llama", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-09-25", + last_updated: "2024-09-25", + modalities: { input: ["text", "image", "audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 8192 }, + }, + "meta/llama-3.2-11b-vision-instruct": { + id: "meta/llama-3.2-11b-vision-instruct", + name: "Llama-3.2-11B-Vision-Instruct", + family: "llama", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-09-25", + last_updated: "2024-09-25", + modalities: { input: ["text", "image", "audio"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 8192 }, + }, + "meta/meta-llama-3.1-405b-instruct": { + id: "meta/meta-llama-3.1-405b-instruct", + name: "Meta-Llama-3.1-405B-Instruct", + family: "llama", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 32768 }, + }, + "meta/meta-llama-3.1-70b-instruct": { + id: "meta/meta-llama-3.1-70b-instruct", + name: "Meta-Llama-3.1-70B-Instruct", + family: "llama", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-07-23", + last_updated: "2024-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 32768 }, + }, + "meta/meta-llama-3-8b-instruct": { + id: "meta/meta-llama-3-8b-instruct", + name: "Meta-Llama-3-8B-Instruct", + family: "llama", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-04-18", + last_updated: "2024-04-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 8192, output: 2048 }, + }, + "meta/llama-4-maverick-17b-128e-instruct-fp8": { + id: "meta/llama-4-maverick-17b-128e-instruct-fp8", + name: "Llama 4 Maverick 17B 128E Instruct FP8", + family: "llama", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-12", + release_date: "2025-01-31", + last_updated: "2025-01-31", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 8192 }, + }, + "core42/jais-30b-chat": { + id: "core42/jais-30b-chat", + name: "JAIS 30b Chat", + family: "jais", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2023-03", + release_date: "2023-08-30", + last_updated: "2023-08-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 8192, output: 2048 }, + }, + "mistral-ai/mistral-nemo": { + id: "mistral-ai/mistral-nemo", + name: "Mistral Nemo", + family: "mistral-nemo", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-03", + release_date: "2024-07-18", + last_updated: "2024-07-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 8192 }, + }, + "mistral-ai/ministral-3b": { + id: "mistral-ai/ministral-3b", + name: "Ministral 3B", + family: "ministral", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-03", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 8192 }, + }, + "mistral-ai/mistral-large-2411": { + id: "mistral-ai/mistral-large-2411", + name: "Mistral Large 24.11", + family: "mistral-large", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-09", + release_date: "2024-11-01", + last_updated: "2024-11-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 32768 }, + }, + "mistral-ai/mistral-small-2503": { + id: "mistral-ai/mistral-small-2503", + name: "Mistral Small 3.1", + family: "mistral-small", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-09", + release_date: "2025-03-01", + last_updated: "2025-03-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 32768 }, + }, + "mistral-ai/mistral-medium-2505": { + id: "mistral-ai/mistral-medium-2505", + name: "Mistral Medium 3 (25.05)", + family: "mistral-medium", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-09", + release_date: "2025-05-01", + last_updated: "2025-05-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 128000, output: 32768 }, + }, + "mistral-ai/codestral-2501": { + id: "mistral-ai/codestral-2501", + name: "Codestral 25.01", + family: "codestral", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-03", + release_date: "2025-01-01", + last_updated: "2025-01-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 32000, output: 8192 }, + }, + }, + }, + neuralwatt: { + id: "neuralwatt", + env: ["NEURALWATT_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.neuralwatt.com/v1", + name: "Neuralwatt", + doc: "https://portal.neuralwatt.com/docs", + models: { + "glm-5-fast": { + id: "glm-5-fast", + name: "GLM 5 Fast", + family: "glm", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-04-07", + last_updated: "2026-04-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.1, output: 3.6 }, + limit: { context: 200000, output: 200000 }, + }, + "kimi-k2.6-fast": { + id: "kimi-k2.6-fast", + name: "Kimi K2.6 Fast", + family: "kimi", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.69, output: 3.22 }, + limit: { context: 262144, output: 262144 }, + }, + "qwen3.5-397b-fast": { + id: "qwen3.5-397b-fast", + name: "Qwen3.5 397B Fast", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-02-01", + last_updated: "2026-02-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.69, output: 4.14 }, + limit: { context: 262144, output: 262144 }, + }, + "glm-5.1-fast": { + id: "glm-5.1-fast", + name: "GLM 5.1 Fast", + family: "glm", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-04-07", + last_updated: "2026-04-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.1, output: 3.6 }, + limit: { context: 200000, output: 200000 }, + }, + "qwen3.6-35b-fast": { + id: "qwen3.6-35b-fast", + name: "Qwen3.6 35B Fast", + family: "qwen3.6", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-04-01", + last_updated: "2026-04-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.1 }, + limit: { context: 131072, output: 131072 }, + }, + "kimi-k2.5-fast": { + id: "kimi-k2.5-fast", + name: "Kimi K2.5 Fast", + family: "kimi", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.52, output: 2.59 }, + limit: { context: 262144, output: 262144 }, + }, + "Qwen/Qwen3.5-397B-A17B-FP8": { + id: "Qwen/Qwen3.5-397B-A17B-FP8", + name: "Qwen3.5 397B A17B FP8", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-01", + last_updated: "2026-02-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.69, output: 4.14 }, + limit: { context: 262144, output: 262144 }, + }, + "Qwen/Qwen3.6-35B-A3B": { + id: "Qwen/Qwen3.6-35B-A3B", + name: "Qwen3.6 35B A3B", + family: "qwen3.6", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-04-01", + last_updated: "2026-04-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.05, output: 0.1 }, + limit: { context: 131072, output: 131072 }, + }, + "zai-org/GLM-5.1-FP8": { + id: "zai-org/GLM-5.1-FP8", + name: "GLM 5.1 FP8", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-04-07", + last_updated: "2026-04-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.1, output: 3.6 }, + limit: { context: 200000, output: 200000 }, + }, + "mistralai/Devstral-Small-2-24B-Instruct-2512": { + id: "mistralai/Devstral-Small-2-24B-Instruct-2512", + name: "Devstral Small 2 24B Instruct 2512", + family: "devstral", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-12-09", + last_updated: "2025-12-09", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.12, output: 0.35 }, + limit: { context: 262144, output: 262144 }, + }, + "openai/gpt-oss-20b": { + id: "openai/gpt-oss-20b", + name: "GPT OSS 20B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.03, output: 0.16 }, + limit: { context: 16384, output: 16384 }, + }, + "moonshotai/Kimi-K2.6": { + id: "moonshotai/Kimi-K2.6", + name: "Kimi K2.6", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.69, output: 3.22 }, + limit: { context: 262144, output: 262144 }, + }, + "moonshotai/Kimi-K2.5": { + id: "moonshotai/Kimi-K2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.52, output: 2.59 }, + limit: { context: 262144, output: 262144 }, + }, + "MiniMaxAI/MiniMax-M2.5": { + id: "MiniMaxAI/MiniMax-M2.5", + name: "MiniMax M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.35, output: 1.38 }, + limit: { context: 196608, output: 196608 }, + }, + }, + }, + togetherai: { + id: "togetherai", + env: ["TOGETHER_API_KEY"], + npm: "@ai-sdk/togetherai", + name: "Together AI", + doc: "https://docs.together.ai/docs/serverless-models", + models: { + "essentialai/Rnj-1-Instruct": { + id: "essentialai/Rnj-1-Instruct", + name: "Rnj-1 Instruct", + family: "rnj", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-12-05", + last_updated: "2025-12-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.15 }, + limit: { context: 32768, output: 32768 }, + }, + "Qwen/Qwen3.5-397B-A17B": { + id: "Qwen/Qwen3.5-397B-A17B", + name: "Qwen3.5 397B A17B", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-16", + last_updated: "2026-02-16", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 3.6 }, + limit: { context: 262144, output: 130000 }, + }, + "Qwen/Qwen3.6-Plus": { + id: "Qwen/Qwen3.6-Plus", + name: "Qwen3.6 Plus", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-30", + last_updated: "2026-04-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 3 }, + limit: { context: 1000000, output: 500000 }, + }, + "Qwen/Qwen3-Coder-Next-FP8": { + id: "Qwen/Qwen3-Coder-Next-FP8", + name: "Qwen3 Coder Next FP8", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2026-02-03", + release_date: "2026-02-03", + last_updated: "2026-02-03", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 1.2 }, + limit: { context: 262144, output: 262144 }, + }, + "Qwen/Qwen3-235B-A22B-Instruct-2507-tput": { + id: "Qwen/Qwen3-235B-A22B-Instruct-2507-tput", + name: "Qwen3 235B A22B Instruct 2507 FP8", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-07-25", + last_updated: "2025-07-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.6 }, + limit: { context: 262144, output: 262144 }, + }, + "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8": { + id: "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8", + name: "Qwen3 Coder 480B A35B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-23", + last_updated: "2025-07-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2, output: 2 }, + limit: { context: 262144, output: 262144 }, + }, + "zai-org/GLM-5.1": { + id: "zai-org/GLM-5.1", + name: "GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-11", + release_date: "2026-04-07", + last_updated: "2026-04-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.4, output: 4.4 }, + limit: { context: 202752, output: 131072 }, + }, + "meta-llama/Llama-3.3-70B-Instruct-Turbo": { + id: "meta-llama/Llama-3.3-70B-Instruct-Turbo", + name: "Llama 3.3 70B", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-12", + release_date: "2024-12-06", + last_updated: "2024-12-06", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.88, output: 0.88 }, + limit: { context: 131072, output: 131072 }, + }, + "deepseek-ai/DeepSeek-V3": { + id: "deepseek-ai/DeepSeek-V3", + name: "DeepSeek V3", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-07", + release_date: "2025-01-20", + last_updated: "2025-05-29", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1.25, output: 1.25 }, + limit: { context: 131072, output: 131072 }, + }, + "deepseek-ai/DeepSeek-R1": { + id: "deepseek-ai/DeepSeek-R1", + name: "DeepSeek R1", + family: "deepseek-thinking", + attachment: false, + reasoning: true, + tool_call: false, + temperature: true, + knowledge: "2024-07", + release_date: "2024-12-26", + last_updated: "2025-03-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 3, output: 7 }, + limit: { context: 163839, output: 163839 }, + }, + "deepseek-ai/DeepSeek-V3-1": { + id: "deepseek-ai/DeepSeek-V3-1", + name: "DeepSeek V3.1", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08", + release_date: "2025-08-21", + last_updated: "2025-08-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 1.7 }, + limit: { context: 131072, output: 131072 }, + }, + "deepseek-ai/DeepSeek-V4-Pro": { + id: "deepseek-ai/DeepSeek-V4-Pro", + name: "DeepSeek V4 Pro", + family: "deepseek", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-04-24", + last_updated: "2026-04-24", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 2.1, output: 4.4, cache_read: 0.2 }, + limit: { context: 512000, output: 384000 }, + }, + "openai/gpt-oss-120b": { + id: "openai/gpt-oss-120b", + name: "GPT OSS 120B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 131072, output: 131072 }, + }, + "google/gemma-4-31B-it": { + id: "google/gemma-4-31B-it", + name: "Gemma 4 31B Instruct", + family: "gemma", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-07", + last_updated: "2026-04-07", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.5 }, + limit: { context: 262144, output: 131072 }, + }, + "moonshotai/Kimi-K2.6": { + id: "moonshotai/Kimi-K2.6", + name: "Kimi K2.6", + family: "kimi-k2.6", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 1.2, output: 4.5, cache_read: 0.2 }, + limit: { context: 262144, output: 131000 }, + }, + "moonshotai/Kimi-K2.5": { + id: "moonshotai/Kimi-K2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: true, + temperature: true, + knowledge: "2026-01", + release_date: "2026-01-27", + last_updated: "2026-01-27", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.5, output: 2.8 }, + limit: { context: 262144, output: 262144 }, + }, + "MiniMaxAI/MiniMax-M2.5": { + id: "MiniMaxAI/MiniMax-M2.5", + name: "MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.06 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMaxAI/MiniMax-M2.7": { + id: "MiniMaxAI/MiniMax-M2.7", + name: "MiniMax-M2.7", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.06 }, + limit: { context: 202752, output: 131072 }, + }, + }, + }, + "qihang-ai": { + id: "qihang-ai", + env: ["QIHANG_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.qhaigc.net/v1", + name: "QiHang", + doc: "https://www.qhaigc.net/docs", + models: { + "claude-opus-4-5-20251101": { + id: "claude-opus-4-5-20251101", + name: "Claude Opus 4.5", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03", + release_date: "2025-11-01", + last_updated: "2025-11-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.71, output: 3.57 }, + limit: { context: 200000, output: 32000 }, + }, + "gemini-3-flash-preview": { + id: "gemini-3-flash-preview", + name: "Gemini 3 Flash Preview", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.07, output: 0.43, context_over_200k: { input: 0.07, output: 0.43 } }, + limit: { context: 1048576, output: 65536 }, + }, + "gpt-5-mini": { + id: "gpt-5-mini", + name: "GPT-5-Mini", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-09-30", + release_date: "2025-09-15", + last_updated: "2025-09-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.04, output: 0.29 }, + limit: { context: 200000, output: 64000 }, + }, + "gemini-3-pro-preview": { + id: "gemini-3-pro-preview", + name: "Gemini 3 Pro Preview", + family: "gemini-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-11", + release_date: "2025-11-19", + last_updated: "2025-11-19", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.57, output: 3.43 }, + limit: { context: 1000000, output: 65000 }, + }, + "claude-sonnet-4-5-20250929": { + id: "claude-sonnet-4-5-20250929", + name: "Claude Sonnet 4.5", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.43, output: 2.14 }, + limit: { context: 200000, output: 64000 }, + }, + "gpt-5.2": { + id: "gpt-5.2", + name: "GPT-5.2", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 2 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gpt-5.2-codex": { + id: "gpt-5.2-codex", + name: "GPT-5.2 Codex", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2025-12-11", + last_updated: "2025-12-11", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 1.14 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "gemini-2.5-flash": { + id: "gemini-2.5-flash", + name: "Gemini 2.5 Flash", + family: "gemini-flash", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2025-12-17", + last_updated: "2025-12-17", + modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.09, output: 0.71, context_over_200k: { input: 0.09, output: 0.71 } }, + limit: { context: 1048576, output: 65536 }, + }, + "claude-haiku-4-5-20251001": { + id: "claude-haiku-4-5-20251001", + name: "Claude Haiku 4.5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-10-01", + last_updated: "2025-10-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.14, output: 0.71 }, + limit: { context: 200000, output: 64000 }, + }, + }, + }, + "tencent-tokenhub": { + id: "tencent-tokenhub", + env: ["TENCENT_TOKENHUB_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://tokenhub.tencentmaas.com/v1", + name: "Tencent TokenHub", + doc: "https://cloud.tencent.com/document/product/1823/130050", + models: { + "hy3-preview": { + id: "hy3-preview", + name: "Hy3 preview", + family: "Hy", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-04-20", + last_updated: "2026-04-20", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 256000, output: 64000 }, + }, + }, + }, + anthropic: { + id: "anthropic", + env: ["ANTHROPIC_API_KEY"], + npm: "@ai-sdk/anthropic", + name: "Anthropic", + doc: "https://docs.anthropic.com/en/docs/about-claude/models", + models: { + "claude-3-sonnet-20240229": { + id: "claude-3-sonnet-20240229", + name: "Claude Sonnet 3", + family: "claude-sonnet", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-08-31", + release_date: "2024-03-04", + last_updated: "2024-03-04", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 0.3 }, + limit: { context: 200000, output: 4096 }, + }, + "claude-haiku-4-5": { + id: "claude-haiku-4-5", + name: "Claude Haiku 4.5 (latest)", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + }, + "claude-opus-4-5-20251101": { + id: "claude-opus-4-5-20251101", + name: "Claude Opus 4.5", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-01", + last_updated: "2025-11-01", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 64000 }, + }, + "claude-3-opus-20240229": { + id: "claude-3-opus-20240229", + name: "Claude Opus 3", + family: "claude-opus", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-08-31", + release_date: "2024-02-29", + last_updated: "2024-02-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 4096 }, + }, + "claude-3-5-haiku-20241022": { + id: "claude-3-5-haiku-20241022", + name: "Claude Haiku 3.5", + family: "claude-haiku", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07-31", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 4, cache_read: 0.08, cache_write: 1 }, + limit: { context: 200000, output: 8192 }, + }, + "claude-3-5-sonnet-20241022": { + id: "claude-3-5-sonnet-20241022", + name: "Claude Sonnet 3.5 v2", + family: "claude-sonnet", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04-30", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 8192 }, + }, + "claude-sonnet-4-6": { + id: "claude-sonnet-4-6", + name: "Claude Sonnet 4.6", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 1000000, output: 64000 }, + }, + "claude-opus-4-0": { + id: "claude-opus-4-0", + name: "Claude Opus 4 (latest)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "claude-opus-4-7": { + id: "claude-opus-4-7", + name: "Claude Opus 4.7", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + }, + "claude-3-haiku-20240307": { + id: "claude-3-haiku-20240307", + name: "Claude Haiku 3", + family: "claude-haiku", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2023-08-31", + release_date: "2024-03-13", + last_updated: "2024-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.25, output: 1.25, cache_read: 0.03, cache_write: 0.3 }, + limit: { context: 200000, output: 4096 }, + }, + "claude-sonnet-4-5-20250929": { + id: "claude-sonnet-4-5-20250929", + name: "Claude Sonnet 4.5", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "claude-3-5-haiku-latest": { + id: "claude-3-5-haiku-latest", + name: "Claude Haiku 3.5 (latest)", + family: "claude-haiku", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-07-31", + release_date: "2024-10-22", + last_updated: "2024-10-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 4, cache_read: 0.08, cache_write: 1 }, + limit: { context: 200000, output: 8192 }, + }, + "claude-opus-4-1": { + id: "claude-opus-4-1", + name: "Claude Opus 4.1 (latest)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "claude-sonnet-4-0": { + id: "claude-sonnet-4-0", + name: "Claude Sonnet 4 (latest)", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "claude-3-5-sonnet-20240620": { + id: "claude-3-5-sonnet-20240620", + name: "Claude Sonnet 3.5", + family: "claude-sonnet", + attachment: true, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2024-04-30", + release_date: "2024-06-20", + last_updated: "2024-06-20", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 8192 }, + }, + "claude-opus-4-5": { + id: "claude-opus-4-5", + name: "Claude Opus 4.5 (latest)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-11-24", + last_updated: "2025-11-24", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 200000, output: 64000 }, + }, + "claude-opus-4-1-20250805": { + id: "claude-opus-4-1-20250805", + name: "Claude Opus 4.1", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + "claude-haiku-4-5-20251001": { + id: "claude-haiku-4-5-20251001", + name: "Claude Haiku 4.5", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2025-10-15", + last_updated: "2025-10-15", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + limit: { context: 200000, output: 64000 }, + }, + "claude-sonnet-4-20250514": { + id: "claude-sonnet-4-20250514", + name: "Claude Sonnet 4", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "claude-opus-4-6": { + id: "claude-opus-4-6", + name: "Claude Opus 4.6", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-03-13", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }, + limit: { context: 1000000, output: 128000 }, + experimental: { + modes: { + fast: { + cost: { input: 30, output: 150, cache_read: 3, cache_write: 37.5 }, + provider: { body: { speed: "fast" }, headers: { "anthropic-beta": "fast-mode-2026-02-01" } }, + }, + }, + }, + }, + "claude-3-7-sonnet-20250219": { + id: "claude-3-7-sonnet-20250219", + name: "Claude Sonnet 3.7", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10-31", + release_date: "2025-02-19", + last_updated: "2025-02-19", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "claude-sonnet-4-5": { + id: "claude-sonnet-4-5", + name: "Claude Sonnet 4.5 (latest)", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2025-09-29", + last_updated: "2025-09-29", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 }, + limit: { context: 200000, output: 64000 }, + }, + "claude-opus-4-20250514": { + id: "claude-opus-4-20250514", + name: "Claude Opus 4", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2025-05-22", + last_updated: "2025-05-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 }, + limit: { context: 200000, output: 32000 }, + }, + }, + }, + modelscope: { + id: "modelscope", + env: ["MODELSCOPE_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api-inference.modelscope.cn/v1", + name: "ModelScope", + doc: "https://modelscope.cn/docs/model-service/API-Inference/intro", + models: { + "Qwen/Qwen3-30B-A3B-Thinking-2507": { + id: "Qwen/Qwen3-30B-A3B-Thinking-2507", + name: "Qwen3 30B A3B Thinking 2507", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-30", + last_updated: "2025-07-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 32768 }, + }, + "Qwen/Qwen3-30B-A3B-Instruct-2507": { + id: "Qwen/Qwen3-30B-A3B-Instruct-2507", + name: "Qwen3 30B A3B Instruct 2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-30", + last_updated: "2025-07-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 16384 }, + }, + "Qwen/Qwen3-235B-A22B-Instruct-2507": { + id: "Qwen/Qwen3-235B-A22B-Instruct-2507", + name: "Qwen3 235B A22B Instruct 2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-04-28", + last_updated: "2025-07-21", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 131072 }, + }, + "Qwen/Qwen3-Coder-30B-A3B-Instruct": { + id: "Qwen/Qwen3-Coder-30B-A3B-Instruct", + name: "Qwen3 Coder 30B A3B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-31", + last_updated: "2025-07-31", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 65536 }, + }, + "Qwen/Qwen3-235B-A22B-Thinking-2507": { + id: "Qwen/Qwen3-235B-A22B-Thinking-2507", + name: "Qwen3-235B-A22B-Thinking-2507", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-25", + last_updated: "2025-07-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 262144, output: 131072 }, + }, + "ZhipuAI/GLM-4.5": { + id: "ZhipuAI/GLM-4.5", + name: "GLM-4.5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 131072, output: 98304 }, + }, + "ZhipuAI/GLM-4.6": { + id: "ZhipuAI/GLM-4.6", + name: "GLM-4.6", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07", + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 202752, output: 98304 }, + }, + }, + }, + "hpc-ai": { + id: "hpc-ai", + env: ["HPC_AI_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.hpc-ai.com/inference/v1", + name: "HPC-AI", + doc: "https://www.hpc-ai.com/doc/docs/quickstart/", + models: { + "zai-org/glm-5.1": { + id: "zai-org/glm-5.1", + name: "GLM 5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-04-08", + last_updated: "2026-04-08", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.66, output: 2, cache_read: 0.12 }, + limit: { context: 202000, output: 202000 }, + }, + "minimax/minimax-m2.5": { + id: "minimax/minimax-m2.5", + name: "MiniMax M2.5", + family: "minimax-m2.5", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: false, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-03-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.14, output: 0.56, cache_read: 0.014 }, + limit: { context: 1000000, output: 131072 }, + }, + "moonshotai/kimi-k2.5": { + id: "moonshotai/kimi-k2.5", + name: "Kimi K2.5", + family: "kimi", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: false, + knowledge: "2025-01-01", + release_date: "2026-01-01", + last_updated: "2026-03-25", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.21, output: 1, cache_read: 0.03 }, + limit: { context: 262144, output: 262144 }, + }, + }, + }, + gitlab: { + id: "gitlab", + env: ["GITLAB_TOKEN"], + npm: "gitlab-ai-provider", + name: "GitLab Duo", + doc: "https://docs.gitlab.com/user/duo_agent_platform/", + models: { + "duo-chat-gpt-5-4-nano": { + id: "duo-chat-gpt-5-4-nano", + name: "Agentic Chat (GPT-5.4 Nano)", + family: "gpt-nano", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "duo-chat-gpt-5-mini": { + id: "duo-chat-gpt-5-mini", + name: "Agentic Chat (GPT-5 Mini)", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-05-30", + release_date: "2026-01-22", + last_updated: "2026-01-22", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "duo-chat-sonnet-4-6": { + id: "duo-chat-sonnet-4-6", + name: "Agentic Chat (Claude Sonnet 4.6)", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-08-31", + release_date: "2026-02-17", + last_updated: "2026-02-17", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 1000000, output: 64000 }, + }, + "duo-chat-gpt-5-2": { + id: "duo-chat-gpt-5-2", + name: "Agentic Chat (GPT-5.2)", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-01-23", + last_updated: "2026-01-23", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "duo-chat-gpt-5-codex": { + id: "duo-chat-gpt-5-codex", + name: "Agentic Chat (GPT-5 Codex)", + family: "gpt-codex", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2026-01-22", + last_updated: "2026-01-22", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "duo-chat-gpt-5-1": { + id: "duo-chat-gpt-5-1", + name: "Agentic Chat (GPT-5.1)", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2024-09-30", + release_date: "2026-01-22", + last_updated: "2026-01-22", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "duo-chat-gpt-5-2-codex": { + id: "duo-chat-gpt-5-2-codex", + name: "Agentic Chat (GPT-5.2 Codex)", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-01-22", + last_updated: "2026-01-22", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "duo-chat-sonnet-4-5": { + id: "duo-chat-sonnet-4-5", + name: "Agentic Chat (Claude Sonnet 4.5)", + family: "claude-sonnet", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-07-31", + release_date: "2026-01-08", + last_updated: "2026-01-08", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 200000, output: 64000 }, + }, + "duo-chat-gpt-5-4": { + id: "duo-chat-gpt-5-4", + name: "Agentic Chat (GPT-5.4)", + family: "gpt", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-05", + last_updated: "2026-03-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 1050000, input: 922000, output: 128000 }, + }, + "duo-chat-haiku-4-5": { + id: "duo-chat-haiku-4-5", + name: "Agentic Chat (Claude Haiku 4.5)", + family: "claude-haiku", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-02-28", + release_date: "2026-01-08", + last_updated: "2026-01-08", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 200000, output: 64000 }, + }, + "duo-chat-gpt-5-3-codex": { + id: "duo-chat-gpt-5-3-codex", + name: "Agentic Chat (GPT-5.3 Codex)", + family: "gpt-codex", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "duo-chat-gpt-5-4-mini": { + id: "duo-chat-gpt-5-4-mini", + name: "Agentic Chat (GPT-5.4 Mini)", + family: "gpt-mini", + attachment: true, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: false, + knowledge: "2025-08-31", + release_date: "2026-03-17", + last_updated: "2026-03-17", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0 }, + limit: { context: 400000, input: 272000, output: 128000 }, + }, + "duo-chat-opus-4-7": { + id: "duo-chat-opus-4-7", + name: "Agentic Chat (Claude Opus 4.7)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: false, + knowledge: "2026-01-31", + release_date: "2026-04-16", + last_updated: "2026-04-16", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 1000000, output: 64000 }, + }, + "duo-chat-opus-4-5": { + id: "duo-chat-opus-4-5", + name: "Agentic Chat (Claude Opus 4.5)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-03-31", + release_date: "2026-01-08", + last_updated: "2026-01-08", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 200000, output: 64000 }, + }, + "duo-chat-opus-4-6": { + id: "duo-chat-opus-4-6", + name: "Agentic Chat (Claude Opus 4.6)", + family: "claude-opus", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-05-31", + release_date: "2026-02-05", + last_updated: "2026-02-05", + modalities: { input: ["text", "image", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 1000000, output: 64000 }, + }, + }, + }, + xiaomi: { + id: "xiaomi", + env: ["XIAOMI_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.xiaomimimo.com/v1", + name: "Xiaomi", + doc: "https://platform.xiaomimimo.com/#/docs", + models: { + "mimo-v2.5-pro": { + id: "mimo-v2.5-pro", + name: "MiMo-V2.5-Pro", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3, cache_read: 0.2, context_over_200k: { input: 2, output: 6, cache_read: 0.4 } }, + limit: { context: 1048576, output: 131072 }, + }, + "mimo-v2-omni": { + id: "mimo-v2-omni", + name: "MiMo-V2-Omni", + family: "mimo", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0.4, output: 2, cache_read: 0.08 }, + limit: { context: 262144, output: 131072 }, + }, + "mimo-v2.5": { + id: "mimo-v2.5", + name: "MiMo-V2.5", + family: "mimo", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: true, + cost: { + input: 0.4, + output: 2, + cache_read: 0.08, + context_over_200k: { input: 0.8, output: 4, cache_read: 0.16 }, + }, + limit: { context: 1048576, output: 131072 }, + }, + "mimo-v2-pro": { + id: "mimo-v2-pro", + name: "MiMo-V2-Pro", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 3, cache_read: 0.2, context_over_200k: { input: 2, output: 6, cache_read: 0.4 } }, + limit: { context: 1048576, output: 131072 }, + }, + "mimo-v2-flash": { + id: "mimo-v2-flash", + name: "MiMo-V2-Flash", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12-01", + release_date: "2025-12-16", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.3, cache_read: 0.01 }, + limit: { context: 262144, output: 65536 }, + }, + }, + }, + clarifai: { + id: "clarifai", + env: ["CLARIFAI_PAT"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.clarifai.com/v2/ext/openai/v1", + name: "Clarifai", + doc: "https://docs.clarifai.com/compute/inference/", + models: { + "arcee_ai/AFM/models/trinity-mini": { + id: "arcee_ai/AFM/models/trinity-mini", + name: "Trinity Mini", + family: "trinity-mini", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2024-10", + release_date: "2025-12", + last_updated: "2026-02-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.045, output: 0.15 }, + limit: { context: 131072, output: 131072 }, + }, + "mistralai/completion/models/Ministral-3-14B-Reasoning-2512": { + id: "mistralai/completion/models/Ministral-3-14B-Reasoning-2512", + name: "Ministral 3 14B Reasoning 2512", + family: "ministral", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-12", + release_date: "2025-12-01", + last_updated: "2025-12-12", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 2.5, output: 1.7 }, + limit: { context: 262144, output: 262144 }, + }, + "mistralai/completion/models/Ministral-3-3B-Reasoning-2512": { + id: "mistralai/completion/models/Ministral-3-3B-Reasoning-2512", + name: "Ministral 3 3B Reasoning 2512", + family: "ministral", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-12", + last_updated: "2026-02-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 1.039, output: 0.54825 }, + limit: { context: 262144, output: 262144 }, + }, + "deepseek-ai/deepseek-ocr/models/DeepSeek-OCR": { + id: "deepseek-ai/deepseek-ocr/models/DeepSeek-OCR", + name: "DeepSeek OCR", + family: "deepseek", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-10-20", + last_updated: "2026-02-25", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 0.7 }, + limit: { context: 8192, output: 8192 }, + }, + "openai/chat-completion/models/gpt-oss-20b": { + id: "openai/chat-completion/models/gpt-oss-20b", + name: "GPT OSS 20B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-12-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.045, output: 0.18 }, + limit: { context: 131072, output: 16384 }, + }, + "openai/chat-completion/models/gpt-oss-120b-high-throughput": { + id: "openai/chat-completion/models/gpt-oss-120b-high-throughput", + name: "GPT OSS 120B High Throughput", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2026-02-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.09, output: 0.36 }, + limit: { context: 131072, output: 16384 }, + }, + "minimaxai/chat-completion/models/MiniMax-M2_5-high-throughput": { + id: "minimaxai/chat-completion/models/MiniMax-M2_5-high-throughput", + name: "MiniMax-M2.5 High Throughput", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 204800, output: 131072 }, + }, + "qwen/qwenCoder/models/Qwen3-Coder-30B-A3B-Instruct": { + id: "qwen/qwenCoder/models/Qwen3-Coder-30B-A3B-Instruct", + name: "Qwen3 Coder 30B A3B Instruct", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-31", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.11458, output: 0.74812 }, + limit: { context: 262144, output: 65536 }, + }, + "qwen/qwenLM/models/Qwen3-30B-A3B-Thinking-2507": { + id: "qwen/qwenLM/models/Qwen3-30B-A3B-Thinking-2507", + name: "Qwen3 30B A3B Thinking 2507", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-31", + last_updated: "2026-02-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.36, output: 1.3 }, + limit: { context: 262144, output: 131072 }, + }, + "qwen/qwenLM/models/Qwen3-30B-A3B-Instruct-2507": { + id: "qwen/qwenLM/models/Qwen3-30B-A3B-Instruct-2507", + name: "Qwen3 30B A3B Instruct 2507", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: true, + structured_output: true, + temperature: true, + release_date: "2025-07-30", + last_updated: "2026-02-25", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.5 }, + limit: { context: 262144, output: 262144 }, + }, + "clarifai/main/models/mm-poly-8b": { + id: "clarifai/main/models/mm-poly-8b", + name: "MM Poly 8B", + family: "mm-poly", + attachment: true, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2025-06", + last_updated: "2026-02-25", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: false, + cost: { input: 0.658, output: 1.11 }, + limit: { context: 32768, output: 4096 }, + }, + "moonshotai/chat-completion/models/Kimi-K2_6": { + id: "moonshotai/chat-completion/models/Kimi-K2_6", + name: "Kimi K2.6", + family: "kimi-k2.6", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + knowledge: "2025-01", + release_date: "2026-04-21", + last_updated: "2026-04-21", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.95, output: 4 }, + limit: { context: 262144, output: 262144 }, + }, + }, + }, + "minimax-cn": { + id: "minimax-cn", + env: ["MINIMAX_API_KEY"], + npm: "@ai-sdk/anthropic", + api: "https://api.minimaxi.com/anthropic/v1", + name: "MiniMax (minimaxi.com)", + doc: "https://platform.minimaxi.com/docs/guides/quickstart", + models: { + "MiniMax-M2": { + id: "MiniMax-M2", + name: "MiniMax-M2", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-10-27", + last_updated: "2025-10-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 196608, output: 128000 }, + }, + "MiniMax-M2.5": { + id: "MiniMax-M2.5", + name: "MiniMax-M2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-12", + last_updated: "2026-02-12", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.03, cache_write: 0.375 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMax-M2.7": { + id: "MiniMax-M2.7", + name: "MiniMax-M2.7", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2, cache_read: 0.06, cache_write: 0.375 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMax-M2.7-highspeed": { + id: "MiniMax-M2.7-highspeed", + name: "MiniMax-M2.7-highspeed", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.4, cache_read: 0.06, cache_write: 0.375 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMax-M2.1": { + id: "MiniMax-M2.1", + name: "MiniMax-M2.1", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-12-23", + last_updated: "2025-12-23", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 204800, output: 131072 }, + }, + "MiniMax-M2.5-highspeed": { + id: "MiniMax-M2.5-highspeed", + name: "MiniMax-M2.5-highspeed", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-13", + last_updated: "2026-02-13", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.4, cache_read: 0.06, cache_write: 0.375 }, + limit: { context: 204800, output: 131072 }, + }, + }, + }, + "regolo-ai": { + id: "regolo-ai", + env: ["REGOLO_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.regolo.ai/v1", + name: "Regolo AI", + doc: "https://docs.regolo.ai/", + models: { + "mistral-small3.2": { + id: "mistral-small3.2", + name: "Mistral Small 3.2", + family: "mistral-small", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-01-31", + last_updated: "2025-01-31", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.5, output: 2.2 }, + limit: { context: 120000, output: 120000 }, + }, + "qwen3-embedding-8b": { + id: "qwen3-embedding-8b", + name: "Qwen3-Embedding-8B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2026-02-01", + last_updated: "2026-02-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.1, output: 0.1 }, + limit: { context: 32768, output: 8192 }, + }, + "llama-3.3-70b-instruct": { + id: "llama-3.3-70b-instruct", + name: "Llama 3.3 70B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-04-28", + last_updated: "2025-04-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.6, output: 2.7 }, + limit: { context: 128000, output: 16384 }, + }, + "qwen3-reranker-4b": { + id: "qwen3-reranker-4b", + name: "Qwen3-Reranker-4B", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: false, + release_date: "2026-02-01", + last_updated: "2026-02-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.12, output: 0.12 }, + limit: { context: 32768, output: 8192 }, + }, + "mistral-small-4-119b": { + id: "mistral-small-4-119b", + name: "Mistral Small 4 119B", + family: "mistral-small", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-15", + last_updated: "2026-03-15", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: false, + cost: { input: 0.75, output: 3 }, + limit: { context: 256000, output: 16384 }, + }, + "qwen3.5-122b": { + id: "qwen3.5-122b", + name: "Qwen3.5-122B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-01", + last_updated: "2026-02-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.9, output: 3.6 }, + limit: { context: 262144, output: 16384 }, + }, + "qwen-image": { + id: "qwen-image", + name: "Qwen-Image", + family: "qwen", + attachment: false, + reasoning: false, + tool_call: false, + temperature: true, + release_date: "2026-03-01", + last_updated: "2026-03-01", + modalities: { input: ["text"], output: ["image"] }, + open_weights: false, + cost: { input: 0.5, output: 2 }, + limit: { context: 8192, output: 4096 }, + }, + "qwen3-coder-next": { + id: "qwen3-coder-next", + name: "Qwen3-Coder-Next", + family: "qwen", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-01", + last_updated: "2026-03-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 1.2 }, + limit: { context: 262144, output: 16384 }, + }, + "minimax-m2.5": { + id: "minimax-m2.5", + name: "MiniMax 2.5", + family: "minimax", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-10", + last_updated: "2026-03-10", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.8, output: 3.5 }, + limit: { context: 190000, output: 64000 }, + }, + "gpt-oss-20b": { + id: "gpt-oss-20b", + name: "GPT-OSS-20B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-03-01", + last_updated: "2026-03-01", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.4, output: 1.8 }, + limit: { context: 128000, output: 16384 }, + }, + "qwen3.5-9b": { + id: "qwen3.5-9b", + name: "Qwen3.5-9B", + family: "qwen", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2026-02-01", + last_updated: "2026-02-01", + modalities: { input: ["text", "image"], output: ["text"] }, + open_weights: true, + cost: { input: 0.15, output: 0.6 }, + limit: { context: 262144, output: 8192 }, + }, + "gpt-oss-120b": { + id: "gpt-oss-120b", + name: "GPT-OSS-120B", + family: "gpt-oss", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-08-05", + last_updated: "2025-08-05", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 1, output: 4.2 }, + limit: { context: 128000, output: 16384 }, + }, + "llama-3.1-8b-instruct": { + id: "llama-3.1-8b-instruct", + name: "Llama 3.1 8B Instruct", + family: "llama", + attachment: false, + reasoning: false, + tool_call: true, + temperature: true, + release_date: "2025-04-07", + last_updated: "2025-04-07", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0.05, output: 0.25 }, + limit: { context: 120000, output: 120000 }, + }, + }, + }, + "xiaomi-token-plan-ams": { + id: "xiaomi-token-plan-ams", + env: ["XIAOMI_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://token-plan-ams.xiaomimimo.com/v1", + name: "Xiaomi Token Plan (Europe)", + doc: "https://platform.xiaomimimo.com/#/docs", + models: { + "mimo-v2-tts": { + id: "mimo-v2-tts", + name: "MiMo-V2-TTS", + family: "mimo", + attachment: false, + reasoning: false, + tool_call: false, + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["audio"] }, + open_weights: true, + cost: { input: 0, output: 0 }, + limit: { context: 8192, output: 16384 }, + }, + "mimo-v2-flash": { + id: "mimo-v2-flash", + name: "MiMo-V2-Flash", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12-01", + release_date: "2025-12-16", + last_updated: "2026-02-04", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0 }, + limit: { context: 262144, output: 65536 }, + }, + "mimo-v2-pro": { + id: "mimo-v2-pro", + name: "MiMo-V2-Pro", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0, context_over_200k: { input: 2, output: 6, cache_read: 0.4 } }, + limit: { context: 1048576, output: 131072 }, + }, + "mimo-v2.5": { + id: "mimo-v2.5", + name: "MiMo-V2.5", + family: "mimo", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text", "image", "audio", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, context_over_200k: { input: 0, output: 0, cache_read: 0 } }, + limit: { context: 1048576, output: 131072 }, + }, + "mimo-v2-omni": { + id: "mimo-v2-omni", + name: "MiMo-V2-Omni", + family: "mimo", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-03-18", + last_updated: "2026-03-18", + modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, cache_read: 0 }, + limit: { context: 262144, output: 131072 }, + }, + "mimo-v2.5-pro": { + id: "mimo-v2.5-pro", + name: "MiMo-V2.5-Pro", + family: "mimo", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2024-12", + release_date: "2026-04-22", + last_updated: "2026-04-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, context_over_200k: { input: 0, output: 0, cache_read: 0 } }, + limit: { context: 1048576, output: 131072 }, + }, + }, + }, + zhipuai: { + id: "zhipuai", + env: ["ZHIPU_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://open.bigmodel.cn/api/paas/v4", + name: "Zhipu AI", + doc: "https://docs.z.ai/guides/overview/pricing", + models: { + "glm-5v-turbo": { + id: "glm-5v-turbo", + name: "GLM-5V-Turbo", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-04-01", + last_updated: "2026-04-01", + modalities: { input: ["text", "image", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 5, output: 22, cache_read: 1.2, cache_write: 0 }, + limit: { context: 200000, output: 131072 }, + }, + "glm-5": { + id: "glm-5", + name: "GLM-5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + release_date: "2026-02-11", + last_updated: "2026-02-11", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 1, output: 3.2, cache_read: 0.2, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "glm-5.1": { + id: "glm-5.1", + name: "GLM-5.1", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + structured_output: true, + temperature: true, + release_date: "2026-03-27", + last_updated: "2026-03-27", + modalities: { input: ["text"], output: ["text"] }, + open_weights: false, + cost: { input: 6, output: 24, cache_read: 1.3, cache_write: 0 }, + limit: { context: 200000, output: 131072 }, + }, + "glm-4.7-flash": { + id: "glm-4.7-flash", + name: "GLM-4.7-Flash", + family: "glm-flash", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-01-19", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 200000, output: 131072 }, + }, + "glm-4.5-flash": { + id: "glm-4.5-flash", + name: "GLM-4.5-Flash", + family: "glm-flash", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + limit: { context: 131072, output: 98304 }, + }, + "glm-4.6v": { + id: "glm-4.6v", + name: "GLM-4.6V", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-08", + last_updated: "2025-12-08", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.3, output: 0.9 }, + limit: { context: 128000, output: 32768 }, + }, + "glm-4.6": { + id: "glm-4.6", + name: "GLM-4.6", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-09-30", + last_updated: "2025-09-30", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2, cache_read: 0.11, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + "glm-4.5v": { + id: "glm-4.5v", + name: "GLM-4.5V", + family: "glm", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-08-11", + last_updated: "2025-08-11", + modalities: { input: ["text", "image", "video"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 1.8 }, + limit: { context: 64000, output: 16384 }, + }, + "glm-4.5-air": { + id: "glm-4.5-air", + name: "GLM-4.5-Air", + family: "glm-air", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.2, output: 1.1, cache_read: 0.03, cache_write: 0 }, + limit: { context: 131072, output: 98304 }, + }, + "glm-4.5": { + id: "glm-4.5", + name: "GLM-4.5", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2025-07-28", + last_updated: "2025-07-28", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2, cache_read: 0.11, cache_write: 0 }, + limit: { context: 131072, output: 98304 }, + }, + "glm-4.7-flashx": { + id: "glm-4.7-flashx", + name: "GLM-4.7-FlashX", + family: "glm-flash", + attachment: false, + reasoning: true, + tool_call: true, + temperature: true, + knowledge: "2025-04", + release_date: "2026-01-19", + last_updated: "2026-01-19", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.07, output: 0.4, cache_read: 0.01, cache_write: 0 }, + limit: { context: 200000, output: 131072 }, + }, + "glm-4.7": { + id: "glm-4.7", + name: "GLM-4.7", + family: "glm", + attachment: false, + reasoning: true, + tool_call: true, + interleaved: { field: "reasoning_content" }, + temperature: true, + knowledge: "2025-04", + release_date: "2025-12-22", + last_updated: "2025-12-22", + modalities: { input: ["text"], output: ["text"] }, + open_weights: true, + cost: { input: 0.6, output: 2.2, cache_read: 0.11, cache_write: 0 }, + limit: { context: 204800, output: 131072 }, + }, + }, + }, + nova: { + id: "nova", + env: ["NOVA_API_KEY"], + npm: "@ai-sdk/openai-compatible", + api: "https://api.nova.amazon.com/v1", + name: "Nova", + doc: "https://nova.amazon.com/dev/documentation", + models: { + "nova-2-lite-v1": { + id: "nova-2-lite-v1", + name: "Nova 2 Lite", + family: "nova-lite", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-12-01", + last_updated: "2025-12-01", + modalities: { input: ["text", "image", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, reasoning: 0 }, + limit: { context: 1000000, output: 64000 }, + }, + "nova-2-pro-v1": { + id: "nova-2-pro-v1", + name: "Nova 2 Pro", + family: "nova-pro", + attachment: true, + reasoning: true, + tool_call: true, + temperature: true, + release_date: "2025-12-03", + last_updated: "2026-01-03", + modalities: { input: ["text", "image", "video", "pdf"], output: ["text"] }, + open_weights: false, + cost: { input: 0, output: 0, reasoning: 0 }, + limit: { context: 1000000, output: 64000 }, + }, + }, + }, +} diff --git a/packages/core/src/models.ts b/packages/core/src/models.ts new file mode 100644 index 00000000000..4a771b045ea --- /dev/null +++ b/packages/core/src/models.ts @@ -0,0 +1,232 @@ +import path from "path" +import { Context, Duration, Effect, Layer, Option, Schedule, Schema } from "effect" +import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http" +import { Global } from "./global" +import { Flag } from "./flag/flag" +import { Flock } from "./util/flock" +import { Hash } from "./util/hash" +import { AppFileSystem } from "./filesystem" +import { InstallationChannel, InstallationVersion } from "./installation/version" + +export const CatalogModelStatus = Schema.Literals(["alpha", "beta", "deprecated"]) +export type CatalogModelStatus = typeof CatalogModelStatus.Type + +const USER_AGENT = `opencode/${InstallationChannel}/${InstallationVersion}/${Flag.KILO_CLIENT}` + +const CostTier = Schema.Struct({ + input: Schema.Finite, + output: Schema.Finite, + cache_read: Schema.optional(Schema.Finite), + cache_write: Schema.optional(Schema.Finite), + tier: Schema.Struct({ + type: Schema.Literal("context"), + size: Schema.Finite, + }), +}) + +const Cost = Schema.Struct({ + input: Schema.Finite, + output: Schema.Finite, + cache_read: Schema.optional(Schema.Finite), + cache_write: Schema.optional(Schema.Finite), + tiers: Schema.optional(Schema.Array(CostTier)), + context_over_200k: Schema.optional( + Schema.Struct({ + input: Schema.Finite, + output: Schema.Finite, + cache_read: Schema.optional(Schema.Finite), + cache_write: Schema.optional(Schema.Finite), + }), + ), +}) + +export const Model = Schema.Struct({ + id: Schema.String, + name: Schema.String, + family: Schema.optional(Schema.String), + release_date: Schema.String, + attachment: Schema.Boolean, + reasoning: Schema.Boolean, + temperature: Schema.Boolean, + tool_call: Schema.Boolean, + interleaved: Schema.optional( + Schema.Union([ + Schema.Literal(true), + Schema.Struct({ + field: Schema.Literals(["reasoning_content", "reasoning_details"]), + }), + ]), + ), + cost: Schema.optional(Cost), + limit: Schema.Struct({ + context: Schema.Finite, + input: Schema.optional(Schema.Finite), + output: Schema.Finite, + }), + modalities: Schema.optional( + Schema.Struct({ + input: Schema.Array(Schema.Literals(["text", "audio", "image", "video", "pdf"])), + output: Schema.Array(Schema.Literals(["text", "audio", "image", "video", "pdf"])), + }), + ), + // kilocode_change start - preserve Kilo catalog metadata + recommendedIndex: Schema.optional(Schema.Finite), + prompt: Schema.optional(Schema.String), + isFree: Schema.optional(Schema.Boolean), + ai_sdk_provider: Schema.optional(Schema.String), + // kilocode_change end + experimental: Schema.optional( + Schema.Struct({ + modes: Schema.optional( + Schema.Record( + Schema.String, + Schema.Struct({ + cost: Schema.optional(Cost), + provider: Schema.optional( + Schema.Struct({ + body: Schema.optional(Schema.Record(Schema.String, Schema.MutableJson)), + headers: Schema.optional(Schema.Record(Schema.String, Schema.String)), + }), + ), + }), + ), + ), + }), + ), + status: Schema.optional(CatalogModelStatus), + provider: Schema.optional( + Schema.Struct({ npm: Schema.optional(Schema.String), api: Schema.optional(Schema.String) }), + ), +}) +export type Model = Schema.Schema.Type + +export const Provider = Schema.Struct({ + api: Schema.optional(Schema.String), + name: Schema.String, + env: Schema.Array(Schema.String), + id: Schema.String, + npm: Schema.optional(Schema.String), + models: Schema.Record(Schema.String, Model), +}) + +export type Provider = Schema.Schema.Type + +export interface Interface { + readonly get: () => Effect.Effect> + readonly refresh: (force?: boolean) => Effect.Effect +} + +export class Service extends Context.Service()("@opencode/ModelsDev") {} + +type Requirements = AppFileSystem.Service | HttpClient.HttpClient + +export const layer: Layer.Layer = Layer.effect( + Service, + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const http = HttpClient.filterStatusOk( + (yield* HttpClient.HttpClient).pipe( + HttpClient.retryTransient({ + retryOn: "errors-and-responses", + times: 2, + schedule: Schedule.exponential(200).pipe(Schedule.jittered), + }), + ), + ) + + const source = Flag.KILO_MODELS_URL || "https://models.dev" + const filepath = path.join( + Global.Path.cache, + source === "https://models.dev" ? "models.json" : `models-${Hash.fast(source)}.json`, + ) + const ttl = Duration.minutes(5) + const lockKey = `models-dev:${filepath}` + + const fresh = Effect.fnUntraced(function* () { + const stat = yield* fs.stat(filepath).pipe(Effect.catch(() => Effect.succeed(undefined))) + if (!stat) return false + const mtime = Option.getOrElse(stat.mtime, () => new Date(0)).getTime() + return Date.now() - mtime < Duration.toMillis(ttl) + }) + + const fetchApi = Effect.fn("ModelsDev.fetchApi")(function* () { + return yield* HttpClientRequest.get(`${source}/api.json`).pipe( + HttpClientRequest.setHeader("User-Agent", USER_AGENT), + http.execute, + Effect.flatMap((res) => res.text), + Effect.timeout("10 seconds"), + ) + }) + + const loadFromDisk = fs.readJson(Flag.KILO_MODELS_PATH ?? filepath).pipe( + Effect.catch(() => Effect.succeed(undefined)), + Effect.map((v) => v as Record | undefined), + ) + + // Bundled at build time; absent in dev — `tryPromise` covers both. + const loadSnapshot = Effect.tryPromise({ + // @ts-ignore — generated at build time, may not exist in dev + try: () => import("./models-snapshot.js").then((m) => m.snapshot as Record | undefined), + catch: () => undefined, + }).pipe(Effect.catch(() => Effect.succeed(undefined))) + + const fetchAndWrite = Effect.fn("ModelsDev.fetchAndWrite")(function* () { + const text = yield* fetchApi() + yield* fs.writeWithDirs(filepath, text) + return text + }) + + const populate = Effect.gen(function* () { + const fromDisk = yield* loadFromDisk + if (fromDisk) return fromDisk + const snapshot = yield* loadSnapshot + if (snapshot) return snapshot + if (Flag.KILO_DISABLE_MODELS_FETCH) return {} + // Flock is cross-process: concurrent opencode CLIs can race on this cache file. + const text = yield* Effect.scoped( + Effect.gen(function* () { + yield* Flock.effect(lockKey) + return yield* fetchAndWrite() + }), + ) + return JSON.parse(text) as Record + }).pipe(Effect.withSpan("ModelsDev.populate"), Effect.orDie) + + const [cachedGet, invalidate] = yield* Effect.cachedInvalidateWithTTL(populate, Duration.infinity) + + const get = (): Effect.Effect> => cachedGet + + const refresh = Effect.fn("ModelsDev.refresh")(function* (force = false) { + if (!force && (yield* fresh())) return + yield* Effect.scoped( + Effect.gen(function* () { + yield* Flock.effect(lockKey) + // Re-check under the lock: another process may have refreshed between + // our outer check and lock acquisition. + if (!force && (yield* fresh())) return + yield* fetchAndWrite() + yield* invalidate + }), + ).pipe( + Effect.tapCause((cause) => + Effect.logError("Failed to fetch models.dev").pipe(Effect.annotateLogs("cause", cause)), + ), + Effect.ignore, + ) + }) + + if (!Flag.KILO_DISABLE_MODELS_FETCH && !process.argv.includes("--get-yargs-completions")) { + // Schedule.spaced runs the effect once, then waits between completions. + yield* Effect.forkScoped(refresh().pipe(Effect.repeat(Schedule.spaced("60 minutes")), Effect.ignore)) + } + + return Service.of({ get, refresh }) + }), +) + +export const defaultLayer: Layer.Layer = layer.pipe( + Layer.provide(FetchHttpClient.layer), + Layer.provide(AppFileSystem.defaultLayer), +) + +export * as ModelsDev from "./models" diff --git a/packages/core/src/plugin.ts b/packages/core/src/plugin.ts new file mode 100644 index 00000000000..dfcae946859 --- /dev/null +++ b/packages/core/src/plugin.ts @@ -0,0 +1,146 @@ +export * as PluginV2 from "./plugin" + +import { createDraft, finishDraft, type Draft } from "immer" +import type { LanguageModelV3 } from "@ai-sdk/provider" +import { type ProviderV2 } from "./provider" +import { Context, Effect, Layer, Schema } from "effect" +import type { ModelV2 } from "./model" + +export const ID = Schema.String.pipe(Schema.brand("Plugin.ID")) +export type ID = typeof ID.Type + +type HookSpec = { + "provider.update": { + input: {} + output: { + provider: ProviderV2.Info + cancel: boolean + } + } + "model.update": { + input: {} + output: { + model: ModelV2.Info + cancel: boolean + } + } + "aisdk.language": { + input: { + model: ModelV2.Info + sdk: any + options: Record + } + output: { + language?: LanguageModelV3 + } + } + "aisdk.sdk": { + input: { + model: ModelV2.Info + package: string + options: Record + } + output: { + sdk?: any + } + } +} + +export type Hooks = { + [Name in keyof HookSpec]: Readonly & { + -readonly [Field in keyof HookSpec[Name]["output"]]: HookSpec[Name]["output"][Field] extends object + ? Draft + : HookSpec[Name]["output"][Field] + } +} + +export type HookFunctions = { + [key in keyof Hooks]?: (input: Hooks[key]) => Effect.Effect +} + +export type HookInput = HookSpec[Name]["input"] +export type HookOutput = HookSpec[Name]["output"] + +export type Effect = Effect.Effect + +export function define(input: { id: ID; effect: Effect.Effect }) { + return input +} + +export interface Interface { + readonly add: (input: { id: ID; effect: Effect }) => Effect.Effect + readonly remove: (id: ID) => Effect.Effect + readonly trigger: ( + name: Name, + input: HookInput, + output: HookOutput, + ) => Effect.Effect & HookOutput> +} + +export class Service extends Context.Service()("@opencode/v2/Plugin") {} + +export const layer = Layer.effect( + Service, + Effect.gen(function* () { + let hooks: { + id: ID + hooks: HookFunctions + }[] = [] + + const svc = Service.of({ + add: Effect.fn("Plugin.add")(function* (input) { + const result = yield* input.effect + if (!result) return + hooks = [ + ...hooks.filter((item) => item.id !== input.id), + { + id: input.id, + hooks: result, + }, + ] + }), + trigger: Effect.fn("Plugin.trigger")(function* (name, input, output) { + const draftEntries = new Map>() + const event = { + ...input, + ...output, + } as Record + + for (const [field, value] of Object.entries(output)) { + if (value && typeof value === "object") { + draftEntries.set(field, createDraft(value)) + event[field] = draftEntries.get(field) + } + } + + for (const item of hooks) { + const match = item.hooks[name] + if (!match) continue + yield* match(event as any).pipe( + Effect.withSpan(`Plugin.hook.${name}`, { + attributes: { + plugin: item.id, + hook: name, + }, + }), + ) + } + + for (const [field, draft] of draftEntries) { + event[field] = finishDraft(draft) + } + + return event as any + }), + remove: Effect.fn("Plugin.remove")(function* (id) { + hooks = hooks.filter((item) => item.id !== id) + }), + }) + return svc + }), +) + +export const defaultLayer = layer + +// opencode +// sdcok diff --git a/packages/core/src/plugin/auth.ts b/packages/core/src/plugin/auth.ts new file mode 100644 index 00000000000..503d82852cc --- /dev/null +++ b/packages/core/src/plugin/auth.ts @@ -0,0 +1,30 @@ +import { Effect } from "effect" +import { AuthV2 } from "../auth" +import { PluginV2 } from "../plugin" + +export const AuthPlugin = PluginV2.define({ + id: PluginV2.ID.make("auth"), + effect: Effect.gen(function* () { + const auth = yield* AuthV2.Service + return { + "provider.update": Effect.fn(function* (evt) { + const account = yield* auth.active(AuthV2.ServiceID.make(evt.provider.id)).pipe(Effect.orDie) + if (!account) return + evt.provider.enabled = { + via: "auth", + service: account.serviceID, + } + if (account.credential.type === "api") { + evt.provider.options.aisdk.provider.apiKey = account.credential.key + Object.assign(evt.provider.options.aisdk.provider, account.credential.metadata ?? {}) + } + if (account.credential.type === "oauth") { + evt.provider.options.aisdk.provider.apiKey = account.credential.access + if (evt.provider.id === "kilo" && account.credential.accountId) { + evt.provider.options.aisdk.provider.kilocodeOrganizationId = account.credential.accountId // kilocode_change + } + } + }), + } + }), +}) diff --git a/packages/core/src/plugin/boot.ts b/packages/core/src/plugin/boot.ts new file mode 100644 index 00000000000..74560ac85b7 --- /dev/null +++ b/packages/core/src/plugin/boot.ts @@ -0,0 +1,71 @@ +export * as PluginBoot from "./boot" + +import { Context, Deferred, Effect, Layer } from "effect" +import { AuthV2 } from "../auth" +import { Catalog } from "../catalog" +import { Npm } from "../npm" +import { PluginV2 } from "../plugin" +import { AuthPlugin } from "./auth" +import { EnvPlugin } from "./env" +import { ModelsDevPlugin } from "./models-dev" +import { ProviderPlugins } from "./provider" + +type Plugin = { + id: PluginV2.ID + effect: Effect.Effect +} + +export interface Interface { + readonly wait: () => Effect.Effect +} + +export class Service extends Context.Service()("@opencode/v2/PluginBoot") {} + +export const layer: Layer.Layer = + Layer.effect( + Service, + Effect.gen(function* () { + const catalog = yield* Catalog.Service + const plugin = yield* PluginV2.Service + const auth = yield* AuthV2.Service + const npm = yield* Npm.Service + const done = yield* Deferred.make() + + const add = Effect.fn("PluginBoot.add")(function* (input: Plugin) { + yield* plugin.add({ + id: input.id, + effect: input.effect.pipe( + Effect.provideService(Catalog.Service, catalog), + Effect.provideService(AuthV2.Service, auth), + Effect.provideService(Npm.Service, npm), + ), + }) + }) + + const boot = Effect.gen(function* () { + yield* add(EnvPlugin) + yield* add(AuthPlugin) + for (const item of ProviderPlugins) { + yield* add(item) + } + yield* add(ModelsDevPlugin) + }).pipe(Effect.withSpan("PluginBoot.boot")) + + yield* boot.pipe( + Effect.exit, + Effect.flatMap((exit) => Deferred.done(done, exit)), + Effect.forkScoped, + ) + + return Service.of({ + wait: () => Deferred.await(done), + }) + }), + ) + +export const defaultLayer = layer.pipe( + Layer.provide(Catalog.defaultLayer), + Layer.provide(PluginV2.defaultLayer), + Layer.provide(Layer.orDie(AuthV2.defaultLayer)), + Layer.provide(Npm.defaultLayer), +) diff --git a/packages/core/src/plugin/env.ts b/packages/core/src/plugin/env.ts new file mode 100644 index 00000000000..d63936fa13d --- /dev/null +++ b/packages/core/src/plugin/env.ts @@ -0,0 +1,18 @@ +import { Effect } from "effect" +import { PluginV2 } from "../plugin" + +export const EnvPlugin = PluginV2.define({ + id: PluginV2.ID.make("env"), + effect: Effect.gen(function* () { + return { + "provider.update": Effect.fn(function* (evt) { + const key = evt.provider.env.find((item) => process.env[item]) + if (!key) return + evt.provider.enabled = { + via: "env", + name: key, + } + }), + } + }), +}) diff --git a/packages/core/src/plugin/layer-map.example.ts b/packages/core/src/plugin/layer-map.example.ts new file mode 100644 index 00000000000..63e33d3f0c4 --- /dev/null +++ b/packages/core/src/plugin/layer-map.example.ts @@ -0,0 +1,94 @@ +export * as LayerMapExample from "./layer-map.example" + +import { Context, Effect, Layer, LayerMap } from "effect" +import { Npm } from "../npm" + +/** + * Tutorial: split global services from context-specific services. + * + * Use this pattern when part of the app should be constructed once at the app edge, + * while another part should be cached per request/project/workspace key. + * + * In this example: + * - Npm.Service is the global service. It is not keyed by request context and should + * be provided once by the application runtime. + * - ConfigService is context-specific. It is built from a RequestContext key and is + * cached by LayerMap for that key. + * - ConfigServiceMap.layer owns the cache. Provide it once globally, then each + * request can provide ConfigServiceMap.get(context) to select the right instance. + * + * Lifetime model: + * - ConfigServiceMap.layer has the app/global lifetime and depends on Npm.Service. + * - ConfigServiceMap.get(context) has the request/context lifetime and provides + * ConfigService for exactly that context key. + * - The cached ConfigService entry stays alive while something is using it. Once idle, + * it remains cached for idleTimeToLive, then its scope is finalized. + * - invalidate(context) removes the cache entry for future lookups. Active users keep + * running on the old instance; the next lookup can create a fresh instance. + * + * Key model: + * - Keys can be strings, structs, classes, arrays, etc. + * - Prefer primitive or immutable keys. Effect uses Hash / Equal semantics for cache + * lookup, so mutating an object after it has been used as a key is a bug. + */ + +export type RequestContext = { + readonly directory: string + readonly workspace: string +} + +export class RequestContextRef extends Context.Service()( + "@opencode/example/RequestContextRef", +) {} + +export interface ConfigServiceShape { + readonly directory: string + readonly workspace: string + readonly nextUse: () => Effect.Effect + readonly which: Npm.Interface["which"] +} + +export class ConfigService extends Context.Service()( + "@opencode/example/ConfigService", +) {} + +const configServiceLayer = Layer.effect( + ConfigService, + Effect.gen(function* () { + const context = yield* RequestContextRef + const npm = yield* Npm.Service + + let useCount = 0 + + return ConfigService.of({ + directory: context.directory, + workspace: context.workspace, + nextUse: () => Effect.succeed(++useCount), + which: npm.which, + }) + }), +) + +export class ConfigServiceMap extends LayerMap.Service()("@opencode/example/ConfigServiceMap", { + lookup: (context: RequestContext) => + configServiceLayer.pipe(Layer.provide(Layer.succeed(RequestContextRef, RequestContextRef.of(context)))), + idleTimeToLive: "5 minutes", +}) {} + +export const appLayer = ConfigServiceMap.layer + +export const readConfig = Effect.fn("LayerMapExample.readConfig")(function* () { + const config = yield* ConfigService + + return { + directory: config.directory, + workspace: config.workspace, + useCount: yield* config.nextUse(), + } +}) + +export const handleRequest = Effect.fn("LayerMapExample.handleRequest")(function* (context: RequestContext) { + return yield* readConfig().pipe(Effect.provide(ConfigServiceMap.get(context))) +}) + +export const invalidateContext = (context: RequestContext) => ConfigServiceMap.invalidate(context) diff --git a/packages/core/src/plugin/models-dev.ts b/packages/core/src/plugin/models-dev.ts new file mode 100644 index 00000000000..e67c2e75a98 --- /dev/null +++ b/packages/core/src/plugin/models-dev.ts @@ -0,0 +1,108 @@ +import { DateTime, Effect } from "effect" +import { Catalog } from "../catalog" +import { ModelV2 } from "../model" +import { ModelsDev } from "../models" +import { PluginV2 } from "../plugin" +import { ProviderV2 } from "../provider" + +function released(date: string) { + const time = Date.parse(date) + return DateTime.makeUnsafe(Number.isFinite(time) ? time : 0) +} + +function cost(input: ModelsDev.Model["cost"]) { + const base = { + input: input?.input ?? 0, + output: input?.output ?? 0, + cache: { + read: input?.cache_read ?? 0, + write: input?.cache_write ?? 0, + }, + } + if (!input?.context_over_200k) return [base] + return [ + base, + { + tier: { + type: "context" as const, + size: 200_000, + }, + input: input.context_over_200k.input, + output: input.context_over_200k.output, + cache: { + read: input.context_over_200k.cache_read ?? 0, + write: input.context_over_200k.cache_write ?? 0, + }, + }, + ] +} + +function variants(model: ModelsDev.Model) { + return Object.entries(model.experimental?.modes ?? {}).map(([id, item]) => ({ + id: ModelV2.VariantID.make(id), + headers: { ...(item.provider?.headers ?? {}) }, + body: { ...(item.provider?.body ?? {}) }, + aisdk: { + provider: {}, + request: {}, + }, + })) +} + +export const ModelsDevPlugin = PluginV2.define({ + id: PluginV2.ID.make("models-dev"), + effect: Effect.gen(function* () { + const catalog = yield* Catalog.Service + const modelsDev = yield* ModelsDev.Service + for (const item of Object.values(yield* modelsDev.get())) { + const providerID = ProviderV2.ID.make(item.id) + yield* catalog.provider.update(providerID, (provider) => { + provider.name = item.name + provider.env = [...item.env] + provider.endpoint = item.npm + ? { + type: "aisdk", + package: item.npm, + url: item.api, + } + : { + type: "unknown", + } + }) + + for (const model of Object.values(item.models)) { + const modelID = ModelV2.ID.make(model.id) + yield* catalog.model + .update(providerID, modelID, (draft) => { + draft.name = model.name + draft.family = model.family ? ModelV2.Family.make(model.family) : undefined + draft.endpoint = model.provider?.npm + ? { + type: "aisdk", + package: model.provider?.npm, + url: model.provider.api, + } + : { + type: "unknown", + } + draft.capabilities = { + tools: model.tool_call, + input: [...(model.modalities?.input ?? [])], + output: [...(model.modalities?.output ?? [])], + } + draft.variants = variants(model) + draft.time.released = released(model.release_date) + draft.cost = cost(model.cost) + draft.status = model.status ?? "active" + draft.enabled = true + draft.limit = { + context: model.limit.context, + input: model.limit.input, + output: model.limit.output, + } + }) + .pipe(Effect.orDie) + } + } + }).pipe(Effect.provide(ModelsDev.defaultLayer)), +}) diff --git a/packages/core/src/plugin/provider.ts b/packages/core/src/plugin/provider.ts new file mode 100644 index 00000000000..1880787495f --- /dev/null +++ b/packages/core/src/plugin/provider.ts @@ -0,0 +1 @@ +export { ProviderPlugins } from "./provider/index" diff --git a/packages/core/src/plugin/provider/alibaba.ts b/packages/core/src/plugin/provider/alibaba.ts new file mode 100644 index 00000000000..fa5c0a91cfb --- /dev/null +++ b/packages/core/src/plugin/provider/alibaba.ts @@ -0,0 +1,15 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" + +export const AlibabaPlugin = PluginV2.define({ + id: PluginV2.ID.make("alibaba"), + effect: Effect.gen(function* () { + return { + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "@ai-sdk/alibaba") return + const mod = yield* Effect.promise(() => import("@ai-sdk/alibaba")) + evt.sdk = mod.createAlibaba(evt.options) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/amazon-bedrock.ts b/packages/core/src/plugin/provider/amazon-bedrock.ts new file mode 100644 index 00000000000..366548a0a32 --- /dev/null +++ b/packages/core/src/plugin/provider/amazon-bedrock.ts @@ -0,0 +1,94 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" +import { ProviderV2 } from "../../provider" + +// Bedrock cross-region inference profiles require regional prefixes only for +// specific model/region combinations. Keep the mapping narrow and avoid +// double-prefixing model IDs that models.dev already marks as global/us/eu/etc. +function resolveModelID(modelID: string, region: string | undefined) { + const crossRegionPrefixes = ["global.", "us.", "eu.", "jp.", "apac.", "au."] + if (crossRegionPrefixes.some((prefix) => modelID.startsWith(prefix))) return modelID + + const resolvedRegion = region ?? "us-east-1" + const regionPrefix = resolvedRegion.split("-")[0] + if (regionPrefix === "us") { + const requiresPrefix = ["nova-micro", "nova-lite", "nova-pro", "nova-premier", "nova-2", "claude", "deepseek"].some( + (item) => modelID.includes(item), + ) + if (requiresPrefix && !resolvedRegion.startsWith("us-gov")) return `${regionPrefix}.${modelID}` + return modelID + } + if (regionPrefix === "eu") { + const regionRequiresPrefix = [ + "eu-west-1", + "eu-west-2", + "eu-west-3", + "eu-north-1", + "eu-central-1", + "eu-south-1", + "eu-south-2", + ].some((item) => resolvedRegion.includes(item)) + const modelRequiresPrefix = ["claude", "nova-lite", "nova-micro", "llama3", "pixtral"].some((item) => + modelID.includes(item), + ) + return regionRequiresPrefix && modelRequiresPrefix ? `${regionPrefix}.${modelID}` : modelID + } + if (regionPrefix !== "ap") return modelID + + const australia = ["ap-southeast-2", "ap-southeast-4"].includes(resolvedRegion) + if (australia && ["anthropic.claude-sonnet-4-5", "anthropic.claude-haiku"].some((item) => modelID.includes(item))) { + return `au.${modelID}` + } + + const prefix = resolvedRegion === "ap-northeast-1" ? "jp" : "apac" + return ["claude", "nova-lite", "nova-micro", "nova-pro"].some((item) => modelID.includes(item)) + ? `${prefix}.${modelID}` + : modelID +} + +export const AmazonBedrockPlugin = PluginV2.define({ + id: PluginV2.ID.make("amazon-bedrock"), + effect: Effect.gen(function* () { + return { + "provider.update": Effect.fn(function* (evt) { + if (evt.provider.id !== ProviderV2.ID.amazonBedrock) return + if (evt.provider.endpoint.type !== "aisdk") return + if (typeof evt.provider.options.aisdk.provider.endpoint !== "string") return + // The AI SDK expects a base URL, but users configure Bedrock private/VPC + // endpoints as `endpoint`; move it into the catalog endpoint URL once. + evt.provider.endpoint.url = evt.provider.options.aisdk.provider.endpoint + delete evt.provider.options.aisdk.provider.endpoint + }), + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "@ai-sdk/amazon-bedrock") return + const options = { ...evt.options } + const profile = typeof options.profile === "string" ? options.profile : process.env.AWS_PROFILE + const region = typeof options.region === "string" ? options.region : (process.env.AWS_REGION ?? "us-east-1") + const bearerToken = + process.env.AWS_BEARER_TOKEN_BEDROCK ?? + (typeof options.bearerToken === "string" ? options.bearerToken : undefined) + if (bearerToken && !process.env.AWS_BEARER_TOKEN_BEDROCK) process.env.AWS_BEARER_TOKEN_BEDROCK = bearerToken + const containerCreds = Boolean( + process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI || process.env.AWS_CONTAINER_CREDENTIALS_FULL_URI, + ) + + options.region = region + if (typeof options.endpoint === "string") options.baseURL = options.endpoint + if (!bearerToken && options.credentialProvider === undefined) { + // Do not gate SDK creation on explicit AWS env vars. The default chain + // also handles ~/.aws/credentials, SSO, process creds, and instance roles. + const { fromNodeProviderChain } = yield* Effect.promise(() => import("@aws-sdk/credential-providers")) + options.credentialProvider = fromNodeProviderChain(profile ? { profile } : {}) + } + + const mod = yield* Effect.promise(() => import("@ai-sdk/amazon-bedrock")) + evt.sdk = mod.createAmazonBedrock(options) + }), + "aisdk.language": Effect.fn(function* (evt) { + if (evt.model.providerID !== ProviderV2.ID.amazonBedrock) return + const region = typeof evt.options.region === "string" ? evt.options.region : process.env.AWS_REGION + evt.language = evt.sdk.languageModel(resolveModelID(evt.model.apiID, region)) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/anthropic.ts b/packages/core/src/plugin/provider/anthropic.ts new file mode 100644 index 00000000000..14851c4a319 --- /dev/null +++ b/packages/core/src/plugin/provider/anthropic.ts @@ -0,0 +1,21 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" +import { ProviderV2 } from "../../provider" + +export const AnthropicPlugin = PluginV2.define({ + id: PluginV2.ID.make("anthropic"), + effect: Effect.gen(function* () { + return { + "provider.update": Effect.fn(function* (evt) { + if (evt.provider.id !== ProviderV2.ID.anthropic) return + evt.provider.options.headers["anthropic-beta"] = + "interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14" + }), + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "@ai-sdk/anthropic") return + const mod = yield* Effect.promise(() => import("@ai-sdk/anthropic")) + evt.sdk = mod.createAnthropic(evt.options) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/azure.ts b/packages/core/src/plugin/provider/azure.ts new file mode 100644 index 00000000000..6c29a161034 --- /dev/null +++ b/packages/core/src/plugin/provider/azure.ts @@ -0,0 +1,64 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" +import { ProviderV2 } from "../../provider" + +function selectLanguage(sdk: any, modelID: string, useChat: boolean) { + if (useChat && sdk.chat) return sdk.chat(modelID) + if (sdk.responses) return sdk.responses(modelID) + if (sdk.messages) return sdk.messages(modelID) + if (sdk.chat) return sdk.chat(modelID) + return sdk.languageModel(modelID) +} + +export const AzurePlugin = PluginV2.define({ + id: PluginV2.ID.make("azure"), + effect: Effect.gen(function* () { + return { + "provider.update": Effect.fn(function* (evt) { + if (evt.provider.id !== ProviderV2.ID.azure) return + const configured = evt.provider.options.aisdk.provider.resourceName + const resourceName = + typeof configured === "string" && configured.trim() !== "" ? configured : process.env.AZURE_RESOURCE_NAME + if (resourceName) evt.provider.options.aisdk.provider.resourceName = resourceName + }), + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "@ai-sdk/azure") return + if (evt.model.providerID === ProviderV2.ID.azure) { + if ( + !evt.options.resourceName && + !evt.options.baseURL && + (evt.model.endpoint.type !== "aisdk" || !evt.model.endpoint.url) + ) { + throw new Error( + "AZURE_RESOURCE_NAME is missing, set it using env var or reconnecting the azure provider and setting it", + ) + } + } + const mod = yield* Effect.promise(() => import("@ai-sdk/azure")) + evt.sdk = mod.createAzure(evt.options) + }), + "aisdk.language": Effect.fn(function* (evt) { + if (evt.model.providerID !== ProviderV2.ID.azure) return + evt.language = selectLanguage(evt.sdk, evt.model.apiID, Boolean(evt.options.useCompletionUrls)) + }), + } + }), +}) + +export const AzureCognitiveServicesPlugin = PluginV2.define({ + id: PluginV2.ID.make("azure-cognitive-services"), + effect: Effect.gen(function* () { + return { + "provider.update": Effect.fn(function* (evt) { + if (evt.provider.id !== ProviderV2.ID.make("azure-cognitive-services")) return + const resourceName = process.env.AZURE_COGNITIVE_SERVICES_RESOURCE_NAME + if (resourceName) + evt.provider.options.aisdk.provider.baseURL = `https://${resourceName}.cognitiveservices.azure.com/openai` + }), + "aisdk.language": Effect.fn(function* (evt) { + if (evt.model.providerID !== ProviderV2.ID.make("azure-cognitive-services")) return + evt.language = selectLanguage(evt.sdk, evt.model.apiID, Boolean(evt.options.useCompletionUrls)) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/cerebras.ts b/packages/core/src/plugin/provider/cerebras.ts new file mode 100644 index 00000000000..b2fadd8bf11 --- /dev/null +++ b/packages/core/src/plugin/provider/cerebras.ts @@ -0,0 +1,20 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" +import { ProviderV2 } from "../../provider" + +export const CerebrasPlugin = PluginV2.define({ + id: PluginV2.ID.make("cerebras"), + effect: Effect.gen(function* () { + return { + "provider.update": Effect.fn(function* (evt) { + if (evt.provider.id !== ProviderV2.ID.make("cerebras")) return + evt.provider.options.headers["X-Cerebras-3rd-Party-Integration"] = "opencode" + }), + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "@ai-sdk/cerebras") return + const mod = yield* Effect.promise(() => import("@ai-sdk/cerebras")) + evt.sdk = mod.createCerebras(evt.options) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/cloudflare-ai-gateway.ts b/packages/core/src/plugin/provider/cloudflare-ai-gateway.ts new file mode 100644 index 00000000000..ffcd4adcf46 --- /dev/null +++ b/packages/core/src/plugin/provider/cloudflare-ai-gateway.ts @@ -0,0 +1,81 @@ +import os from "os" +import { InstallationVersion } from "../../installation/version" +import { Effect, Option, Schema } from "effect" +import { PluginV2 } from "../../plugin" + +export const CloudflareAIGatewayPlugin = PluginV2.define({ + id: PluginV2.ID.make("cloudflare-ai-gateway"), + effect: Effect.gen(function* () { + return { + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "ai-gateway-provider") return + if (evt.options.baseURL) return + + const config = gatewayConfig(evt.options) + if (!config) return + const metadata = gatewayMetadata(evt.options) + const { createAiGateway } = yield* Effect.promise(() => import("ai-gateway-provider")).pipe(Effect.orDie) + const { createUnified } = yield* Effect.promise(() => import("ai-gateway-provider/providers/unified")).pipe( + Effect.orDie, + ) + const gateway = createAiGateway({ + accountId: config.accountId, + gateway: config.gatewayId, + apiKey: config.apiKey, + options: gatewayOptions(evt.options, metadata), + } as any) + const unified = createUnified() + evt.sdk = { + languageModel(modelID: string) { + return gateway(unified(modelID)) + }, + } + }), + } + }), +}) + +type GatewayConfig = { + accountId: string + gatewayId: string + apiKey: string +} + +const decodeJson = Schema.decodeUnknownOption(Schema.UnknownFromJsonString) + +function gatewayConfig(options: Record): GatewayConfig | undefined { + const accountId = process.env.CLOUDFLARE_ACCOUNT_ID ?? stringOption(options, "accountId") + // AuthPlugin copies CLI prompt metadata into options. The prompt stores the + // gateway as gatewayId, while older config examples may use gateway. + const gatewayId = + process.env.CLOUDFLARE_GATEWAY_ID ?? stringOption(options, "gatewayId") ?? stringOption(options, "gateway") + const apiKey = process.env.CLOUDFLARE_API_TOKEN ?? process.env.CF_AIG_TOKEN ?? stringOption(options, "apiKey") + if (!accountId || !gatewayId || !apiKey) return undefined + + return { accountId, gatewayId, apiKey } +} + +function gatewayMetadata(options: Record) { + // Preserve the legacy cf-aig-metadata header escape hatch for gateway logging + // metadata, but prefer the typed metadata option when present. + if (options.metadata !== undefined) return options.metadata + const raw = (options.headers as Record | undefined)?.["cf-aig-metadata"] + return raw ? Option.getOrUndefined(decodeJson(raw)) : undefined +} + +function gatewayOptions(options: Record, metadata: unknown) { + return { + metadata, + cacheTtl: options.cacheTtl, + cacheKey: options.cacheKey, + skipCache: options.skipCache, + collectLog: options.collectLog, + headers: { + "User-Agent": `opencode/${InstallationVersion} cloudflare-ai-gateway (${os.platform()} ${os.release()}; ${os.arch()})`, + }, + } +} + +function stringOption(options: Record, key: string) { + return typeof options[key] === "string" ? options[key] : undefined +} diff --git a/packages/core/src/plugin/provider/cloudflare-workers-ai.ts b/packages/core/src/plugin/provider/cloudflare-workers-ai.ts new file mode 100644 index 00000000000..f39869b57d7 --- /dev/null +++ b/packages/core/src/plugin/provider/cloudflare-workers-ai.ts @@ -0,0 +1,69 @@ +import os from "os" +import { InstallationVersion } from "../../installation/version" +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" +import { ProviderV2 } from "../../provider" + +const providerID = ProviderV2.ID.make("cloudflare-workers-ai") + +export const CloudflareWorkersAIPlugin = PluginV2.define({ + id: PluginV2.ID.make("cloudflare-workers-ai"), + effect: Effect.gen(function* () { + return { + "provider.update": Effect.fn(function* (evt) { + if (evt.provider.id !== providerID) return + if (evt.provider.endpoint.type !== "aisdk") return + if (evt.provider.endpoint.url) return + + const accountId = resolveAccountId(evt.provider.options.aisdk.provider) + if (accountId) evt.provider.endpoint.url = workersEndpoint(accountId) + }), + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.model.providerID !== providerID) return + if (evt.package !== "@ai-sdk/openai-compatible") return + + if (!hasWorkersEndpoint(evt.model.endpoint)) return + const mod = yield* Effect.promise(() => import("@ai-sdk/openai-compatible")) + evt.sdk = mod.createOpenAICompatible(sdkOptions(evt.options) as any) + }), + "aisdk.language": Effect.fn(function* (evt) { + if (evt.model.providerID !== providerID) return + evt.language = evt.sdk.languageModel(evt.model.apiID) + }), + } + }), +}) + +function resolveAccountId(options: Record) { + return process.env.CLOUDFLARE_ACCOUNT_ID ?? stringOption(options, "accountId") +} + +function workersEndpoint(accountId: string) { + return `https://api.cloudflare.com/client/v4/accounts/${accountId}/ai/v1` +} + +function hasWorkersEndpoint(endpoint: ProviderV2.Endpoint) { + return endpoint.type === "aisdk" && Boolean(endpoint.url) +} + +function sdkOptions(options: Record) { + return { + ...options, + baseURL: expandAccountId(options.baseURL), + apiKey: process.env.CLOUDFLARE_API_KEY ?? options.apiKey, + headers: { + "User-Agent": `opencode/${InstallationVersion} cloudflare-workers-ai (${os.platform()} ${os.release()}; ${os.arch()})`, + ...options.headers, + }, + name: providerID, + } +} + +function expandAccountId(baseURL: unknown) { + if (typeof baseURL !== "string") return baseURL + return baseURL.replaceAll("${CLOUDFLARE_ACCOUNT_ID}", process.env.CLOUDFLARE_ACCOUNT_ID ?? "${CLOUDFLARE_ACCOUNT_ID}") +} + +function stringOption(options: Record, key: string) { + return typeof options[key] === "string" ? options[key] : undefined +} diff --git a/packages/core/src/plugin/provider/cohere.ts b/packages/core/src/plugin/provider/cohere.ts new file mode 100644 index 00000000000..991c370d175 --- /dev/null +++ b/packages/core/src/plugin/provider/cohere.ts @@ -0,0 +1,15 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" + +export const CoherePlugin = PluginV2.define({ + id: PluginV2.ID.make("cohere"), + effect: Effect.gen(function* () { + return { + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "@ai-sdk/cohere") return + const mod = yield* Effect.promise(() => import("@ai-sdk/cohere")) + evt.sdk = mod.createCohere(evt.options) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/deepinfra.ts b/packages/core/src/plugin/provider/deepinfra.ts new file mode 100644 index 00000000000..bbd42f6e283 --- /dev/null +++ b/packages/core/src/plugin/provider/deepinfra.ts @@ -0,0 +1,15 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" + +export const DeepInfraPlugin = PluginV2.define({ + id: PluginV2.ID.make("deepinfra"), + effect: Effect.gen(function* () { + return { + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "@ai-sdk/deepinfra") return + const mod = yield* Effect.promise(() => import("@ai-sdk/deepinfra")) + evt.sdk = mod.createDeepInfra(evt.options) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/dynamic.ts b/packages/core/src/plugin/provider/dynamic.ts new file mode 100644 index 00000000000..e5abc7009e3 --- /dev/null +++ b/packages/core/src/plugin/provider/dynamic.ts @@ -0,0 +1,31 @@ +import { Npm } from "../../npm" +import { Effect, Option } from "effect" +import { pathToFileURL } from "url" +import { PluginV2 } from "../../plugin" + +export const DynamicProviderPlugin = PluginV2.define({ + id: PluginV2.ID.make("dynamic-provider"), + effect: Effect.gen(function* () { + const npm = yield* Npm.Service + return { + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.sdk) return + + const installedPath = evt.package.startsWith("file://") + ? evt.package + : Option.getOrUndefined((yield* npm.add(evt.package).pipe(Effect.orDie)).entrypoint) + if (!installedPath) throw new Error(`Package ${evt.package} has no import entrypoint`) + + const mod = yield* Effect.promise(async () => { + return (await import( + installedPath.startsWith("file://") ? installedPath : pathToFileURL(installedPath).href + )) as Record any> + }).pipe(Effect.orDie) + const match = Object.keys(mod).find((name) => name.startsWith("create")) + if (!match) throw new Error(`Package ${evt.package} has no provider factory export`) + + evt.sdk = mod[match](evt.options) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/gateway.ts b/packages/core/src/plugin/provider/gateway.ts new file mode 100644 index 00000000000..5b08ad9ef5e --- /dev/null +++ b/packages/core/src/plugin/provider/gateway.ts @@ -0,0 +1,15 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" + +export const GatewayPlugin = PluginV2.define({ + id: PluginV2.ID.make("gateway"), + effect: Effect.gen(function* () { + return { + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "@ai-sdk/gateway") return + const mod = yield* Effect.promise(() => import("@ai-sdk/gateway")) + evt.sdk = mod.createGateway(evt.options) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/github-copilot.ts b/packages/core/src/plugin/provider/github-copilot.ts new file mode 100644 index 00000000000..31e57ba12ab --- /dev/null +++ b/packages/core/src/plugin/provider/github-copilot.ts @@ -0,0 +1,44 @@ +import { Effect } from "effect" +import { ModelV2 } from "../../model" +import { PluginV2 } from "../../plugin" +import { ProviderV2 } from "../../provider" + +function shouldUseResponses(modelID: string) { + // Copilot supports Responses for GPT-5 class models, except mini variants + // which still need the chat-completions endpoint. + const match = /^gpt-(\d+)/.exec(modelID) + if (!match) return false + return Number(match[1]) >= 5 && !modelID.startsWith("gpt-5-mini") +} + +export const GithubCopilotPlugin = PluginV2.define({ + id: PluginV2.ID.make("github-copilot"), + effect: Effect.gen(function* () { + return { + "provider.update": Effect.fn(function* (evt) { + if (evt.provider.id !== ProviderV2.ID.githubCopilot) return + }), + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "@ai-sdk/github-copilot") return + const mod = yield* Effect.promise(() => import("../../github-copilot/copilot-provider")) + evt.sdk = mod.createOpenaiCompatible(evt.options) + }), + "aisdk.language": Effect.fn(function* (evt) { + if (evt.model.providerID !== ProviderV2.ID.githubCopilot) return + if (evt.sdk.responses === undefined && evt.sdk.chat === undefined) { + evt.language = evt.sdk.languageModel(evt.model.apiID) + return + } + evt.language = shouldUseResponses(evt.model.apiID) + ? evt.sdk.responses(evt.model.apiID) + : evt.sdk.chat(evt.model.apiID) + }), + "model.update": Effect.fn(function* (evt) { + if (evt.model.providerID !== ProviderV2.ID.githubCopilot) return + // This chat-only alias conflicts with the Copilot GPT-5 Responses route, + // so hide it only for Copilot rather than for every provider catalog. + if (evt.model.id === ModelV2.ID.make("gpt-5-chat-latest")) evt.cancel = true + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/gitlab.ts b/packages/core/src/plugin/provider/gitlab.ts new file mode 100644 index 00000000000..226f5a45eb4 --- /dev/null +++ b/packages/core/src/plugin/provider/gitlab.ts @@ -0,0 +1,65 @@ +import os from "os" +import { InstallationVersion } from "../../installation/version" +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" +import { ProviderV2 } from "../../provider" + +export const GitLabPlugin = PluginV2.define({ + id: PluginV2.ID.make("gitlab"), + effect: Effect.gen(function* () { + return { + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "gitlab-ai-provider") return + const mod = yield* Effect.promise(() => import("gitlab-ai-provider")) + evt.sdk = mod.createGitLab({ + ...evt.options, + instanceUrl: + typeof evt.options.instanceUrl === "string" + ? evt.options.instanceUrl + : (process.env.GITLAB_INSTANCE_URL ?? "https://gitlab.com"), + apiKey: typeof evt.options.apiKey === "string" ? evt.options.apiKey : process.env.GITLAB_TOKEN, + aiGatewayHeaders: { + "User-Agent": `opencode/${InstallationVersion} gitlab-ai-provider/${mod.VERSION} (${os.platform()} ${os.release()}; ${os.arch()})`, + "anthropic-beta": "context-1m-2025-08-07", + ...evt.options.aiGatewayHeaders, + }, + featureFlags: { + duo_agent_platform_agentic_chat: true, + duo_agent_platform: true, + ...evt.options.featureFlags, + }, + }) + }), + "aisdk.language": Effect.fn(function* (evt) { + if (evt.model.providerID !== ProviderV2.ID.gitlab) return + const featureFlags = + typeof evt.options.featureFlags === "object" && evt.options.featureFlags ? evt.options.featureFlags : {} + if (evt.model.apiID.startsWith("duo-workflow-")) { + const gitlab = yield* Effect.promise(() => import("gitlab-ai-provider")).pipe(Effect.orDie) + const workflowRef = + typeof evt.model.options.aisdk.request.workflowRef === "string" + ? evt.model.options.aisdk.request.workflowRef + : undefined + const workflowDefinition = + typeof evt.model.options.aisdk.request.workflowDefinition === "string" + ? evt.model.options.aisdk.request.workflowDefinition + : undefined + const language = evt.sdk.workflowChat( + gitlab.isWorkflowModel(evt.model.apiID) ? evt.model.apiID : "duo-workflow", + { + featureFlags, + workflowDefinition, + }, + ) + if (workflowRef) language.selectedModelRef = workflowRef + evt.language = language + return + } + evt.language = evt.sdk.agenticChat(evt.model.apiID, { + aiGatewayHeaders: evt.options.aiGatewayHeaders, + featureFlags, + }) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/google-vertex.ts b/packages/core/src/plugin/provider/google-vertex.ts new file mode 100644 index 00000000000..0c335df9312 --- /dev/null +++ b/packages/core/src/plugin/provider/google-vertex.ts @@ -0,0 +1,141 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" +import { ProviderV2 } from "../../provider" + +function resolveProject(options: Record) { + // models.dev advertises GOOGLE_VERTEX_PROJECT for Vertex, while Google SDKs + // and ADC examples commonly use the broader Google Cloud project aliases. + return ( + options.project ?? + process.env.GOOGLE_VERTEX_PROJECT ?? + process.env.GOOGLE_CLOUD_PROJECT ?? + process.env.GCP_PROJECT ?? + process.env.GCLOUD_PROJECT + ) +} + +function resolveLocation(options: Record) { + return ( + options.location ?? + process.env.GOOGLE_VERTEX_LOCATION ?? + process.env.GOOGLE_CLOUD_LOCATION ?? + process.env.VERTEX_LOCATION ?? + "us-central1" + ) +} + +function vertexEndpoint(location: string) { + return location === "global" ? "aiplatform.googleapis.com" : `${location}-aiplatform.googleapis.com` +} + +function replaceVertexVars(value: string, project: string | undefined, location: string) { + // Vertex OpenAI-compatible endpoints are stored as templates in the catalog; + // expand them after provider config/env project and location have been resolved. + return value + .replaceAll("${GOOGLE_VERTEX_PROJECT}", project ?? "${GOOGLE_VERTEX_PROJECT}") + .replaceAll("${GOOGLE_VERTEX_LOCATION}", location) + .replaceAll("${GOOGLE_VERTEX_ENDPOINT}", vertexEndpoint(location)) +} + +function authFetch(fetchWithRuntimeOptions?: unknown) { + // Native Vertex SDKs handle ADC internally. OpenAI-compatible Vertex endpoints + // do not, so inject a Google access token into their fetch path. + return async (input: Parameters[0], init?: RequestInit) => { + const { GoogleAuth } = await import("google-auth-library") + const auth = new GoogleAuth() + const client = await auth.getApplicationDefault() + const token = await client.credential.getAccessToken() + const headers = new Headers(init?.headers) + headers.set("Authorization", `Bearer ${token.token}`) + return typeof fetchWithRuntimeOptions === "function" + ? fetchWithRuntimeOptions(input, { ...init, headers }) + : fetch(input, { ...init, headers }) + } +} + +export const GoogleVertexPlugin = PluginV2.define({ + id: PluginV2.ID.make("google-vertex"), + effect: Effect.gen(function* () { + return { + "provider.update": Effect.fn(function* (evt) { + if (evt.provider.id !== ProviderV2.ID.googleVertex) return + const project = resolveProject(evt.provider.options.aisdk.provider) + const location = String(resolveLocation(evt.provider.options.aisdk.provider)) + if (project) evt.provider.options.aisdk.provider.project = project + evt.provider.options.aisdk.provider.location = location + if (evt.provider.endpoint.type === "aisdk" && evt.provider.endpoint.url) { + evt.provider.endpoint.url = replaceVertexVars(evt.provider.endpoint.url, project, location) + } + if ( + evt.provider.endpoint.type === "aisdk" && + evt.provider.endpoint.package.includes("@ai-sdk/openai-compatible") + ) { + evt.provider.options.aisdk.provider.fetch = authFetch(evt.provider.options.aisdk.provider.fetch) + } + }), + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.model.providerID === ProviderV2.ID.googleVertex && evt.package.includes("@ai-sdk/openai-compatible")) { + evt.options.fetch = authFetch(evt.options.fetch) + return + } + if (evt.package !== "@ai-sdk/google-vertex") return + const mod = yield* Effect.promise(() => import("@ai-sdk/google-vertex")) + const project = resolveProject(evt.options) + const location = resolveLocation(evt.options) + const options = { ...evt.options } + delete options.fetch + evt.sdk = mod.createVertex({ + ...options, + project, + location, + }) + }), + "aisdk.language": Effect.fn(function* (evt) { + if (evt.model.providerID !== ProviderV2.ID.googleVertex) return + evt.language = evt.sdk.languageModel(String(evt.model.apiID).trim()) + }), + } + }), +}) + +export const GoogleVertexAnthropicPlugin = PluginV2.define({ + id: PluginV2.ID.make("google-vertex-anthropic"), + effect: Effect.gen(function* () { + return { + "provider.update": Effect.fn(function* (evt) { + if (evt.provider.id !== ProviderV2.ID.make("google-vertex-anthropic")) return + const project = + evt.provider.options.aisdk.provider.project ?? + process.env.GOOGLE_CLOUD_PROJECT ?? + process.env.GCP_PROJECT ?? + process.env.GCLOUD_PROJECT + const location = + evt.provider.options.aisdk.provider.location ?? + process.env.GOOGLE_CLOUD_LOCATION ?? + process.env.VERTEX_LOCATION ?? + "global" + if (project) evt.provider.options.aisdk.provider.project = project + evt.provider.options.aisdk.provider.location = location + }), + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "@ai-sdk/google-vertex/anthropic") return + const mod = yield* Effect.promise(() => import("@ai-sdk/google-vertex/anthropic")) + evt.sdk = mod.createVertexAnthropic({ + ...evt.options, + project: + typeof evt.options.project === "string" + ? evt.options.project + : (process.env.GOOGLE_CLOUD_PROJECT ?? process.env.GCP_PROJECT ?? process.env.GCLOUD_PROJECT), + location: + typeof evt.options.location === "string" + ? evt.options.location + : (process.env.GOOGLE_CLOUD_LOCATION ?? process.env.VERTEX_LOCATION ?? "global"), + }) + }), + "aisdk.language": Effect.fn(function* (evt) { + if (evt.model.providerID !== ProviderV2.ID.make("google-vertex-anthropic")) return + evt.language = evt.sdk.languageModel(String(evt.model.apiID).trim()) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/google.ts b/packages/core/src/plugin/provider/google.ts new file mode 100644 index 00000000000..47e29c6b5d5 --- /dev/null +++ b/packages/core/src/plugin/provider/google.ts @@ -0,0 +1,15 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" + +export const GooglePlugin = PluginV2.define({ + id: PluginV2.ID.make("google"), + effect: Effect.gen(function* () { + return { + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "@ai-sdk/google") return + const mod = yield* Effect.promise(() => import("@ai-sdk/google")) + evt.sdk = mod.createGoogleGenerativeAI(evt.options) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/groq.ts b/packages/core/src/plugin/provider/groq.ts new file mode 100644 index 00000000000..f2052afd1a8 --- /dev/null +++ b/packages/core/src/plugin/provider/groq.ts @@ -0,0 +1,15 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" + +export const GroqPlugin = PluginV2.define({ + id: PluginV2.ID.make("groq"), + effect: Effect.gen(function* () { + return { + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "@ai-sdk/groq") return + const mod = yield* Effect.promise(() => import("@ai-sdk/groq")) + evt.sdk = mod.createGroq(evt.options) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/index.ts b/packages/core/src/plugin/provider/index.ts new file mode 100644 index 00000000000..fd02d322a1f --- /dev/null +++ b/packages/core/src/plugin/provider/index.ts @@ -0,0 +1,67 @@ +import { AlibabaPlugin } from "./alibaba" +import { AmazonBedrockPlugin } from "./amazon-bedrock" +import { AnthropicPlugin } from "./anthropic" +import { AzureCognitiveServicesPlugin, AzurePlugin } from "./azure" +import { CerebrasPlugin } from "./cerebras" +import { CloudflareAIGatewayPlugin } from "./cloudflare-ai-gateway" +import { CloudflareWorkersAIPlugin } from "./cloudflare-workers-ai" +import { CoherePlugin } from "./cohere" +import { DeepInfraPlugin } from "./deepinfra" +import { DynamicProviderPlugin } from "./dynamic" +import { GatewayPlugin } from "./gateway" +import { GithubCopilotPlugin } from "./github-copilot" +import { GitLabPlugin } from "./gitlab" +import { GooglePlugin } from "./google" +import { GoogleVertexAnthropicPlugin, GoogleVertexPlugin } from "./google-vertex" +import { GroqPlugin } from "./groq" +import { KiloPlugin } from "./kilo" +import { LLMGatewayPlugin } from "./llmgateway" +import { MistralPlugin } from "./mistral" +import { NvidiaPlugin } from "./nvidia" +import { OpenAIPlugin } from "./openai" +import { OpenAICompatiblePlugin } from "./openai-compatible" +import { OpencodePlugin } from "./opencode" +import { OpenRouterPlugin } from "./openrouter" +import { PerplexityPlugin } from "./perplexity" +import { SapAICorePlugin } from "./sap-ai-core" +import { TogetherAIPlugin } from "./togetherai" +import { VercelPlugin } from "./vercel" +import { VenicePlugin } from "./venice" +import { XAIPlugin } from "./xai" +import { ZenmuxPlugin } from "./zenmux" + +export const ProviderPlugins = [ + AlibabaPlugin, + AmazonBedrockPlugin, + AnthropicPlugin, + AzureCognitiveServicesPlugin, + AzurePlugin, + CerebrasPlugin, + CloudflareAIGatewayPlugin, + CloudflareWorkersAIPlugin, + CoherePlugin, + DeepInfraPlugin, + GatewayPlugin, + GithubCopilotPlugin, + GitLabPlugin, + GooglePlugin, + GoogleVertexAnthropicPlugin, + GoogleVertexPlugin, + GroqPlugin, + KiloPlugin, + LLMGatewayPlugin, + MistralPlugin, + NvidiaPlugin, + OpencodePlugin, + OpenAICompatiblePlugin, + OpenAIPlugin, + OpenRouterPlugin, + PerplexityPlugin, + SapAICorePlugin, + TogetherAIPlugin, + VercelPlugin, + VenicePlugin, + XAIPlugin, + ZenmuxPlugin, + DynamicProviderPlugin, +] diff --git a/packages/core/src/plugin/provider/kilo.ts b/packages/core/src/plugin/provider/kilo.ts new file mode 100644 index 00000000000..82eca003d10 --- /dev/null +++ b/packages/core/src/plugin/provider/kilo.ts @@ -0,0 +1,37 @@ +// kilocode_change - new file +import { createKilo, KILO_OPENROUTER_BASE } from "@kilocode/kilo-gateway" +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" +import { ProviderV2 } from "../../provider" + +const id = ProviderV2.ID.make("kilo") + +export const KiloPlugin = PluginV2.define({ + id: PluginV2.ID.make("kilo"), + effect: Effect.gen(function* () { + return { + "provider.update": Effect.fn(function* (evt) { + if (evt.provider.id !== id) return + + const options = evt.provider.options.aisdk.provider + const token = options.kilocodeToken ?? options.apiKey ?? process.env.KILO_API_KEY + const org = process.env.KILO_ORG_ID ?? options.kilocodeOrganizationId + + evt.provider.endpoint = { + type: "aisdk", + package: "@kilocode/kilo-gateway", + url: KILO_OPENROUTER_BASE, + } + evt.provider.options.headers["HTTP-Referer"] = "https://kilo.ai/" + evt.provider.options.headers["X-Title"] = "Kilo Code" + options.kilocodeToken = token ?? "anonymous" + if (org) options.kilocodeOrganizationId = org + if (!evt.provider.enabled) evt.provider.enabled = { via: "custom", data: { anonymous: true } } + }), + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.model.providerID !== id) return + evt.sdk = createKilo(evt.options) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/llmgateway.ts b/packages/core/src/plugin/provider/llmgateway.ts new file mode 100644 index 00000000000..dd656dfc59d --- /dev/null +++ b/packages/core/src/plugin/provider/llmgateway.ts @@ -0,0 +1,18 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" +import { ProviderV2 } from "../../provider" + +export const LLMGatewayPlugin = PluginV2.define({ + id: PluginV2.ID.make("llmgateway"), + effect: Effect.gen(function* () { + return { + "provider.update": Effect.fn(function* (evt) { + if (evt.provider.id !== ProviderV2.ID.make("llmgateway")) return + if (evt.provider.enabled === false) return + evt.provider.options.headers["HTTP-Referer"] = "https://kilo.ai/" // kilocode_change + evt.provider.options.headers["X-Title"] = "Kilo Code" // kilocode_change + evt.provider.options.headers["X-Source"] = "kilo" // kilocode_change + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/mistral.ts b/packages/core/src/plugin/provider/mistral.ts new file mode 100644 index 00000000000..e7f0decb79e --- /dev/null +++ b/packages/core/src/plugin/provider/mistral.ts @@ -0,0 +1,15 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" + +export const MistralPlugin = PluginV2.define({ + id: PluginV2.ID.make("mistral"), + effect: Effect.gen(function* () { + return { + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "@ai-sdk/mistral") return + const mod = yield* Effect.promise(() => import("@ai-sdk/mistral")) + evt.sdk = mod.createMistral(evt.options) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/nvidia.ts b/packages/core/src/plugin/provider/nvidia.ts new file mode 100644 index 00000000000..ebbca16d2af --- /dev/null +++ b/packages/core/src/plugin/provider/nvidia.ts @@ -0,0 +1,17 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" +import { ProviderV2 } from "../../provider" + +export const NvidiaPlugin = PluginV2.define({ + id: PluginV2.ID.make("nvidia"), + effect: Effect.gen(function* () { + return { + "provider.update": Effect.fn(function* (evt) { + if (evt.provider.id !== ProviderV2.ID.make("nvidia")) return + evt.provider.options.headers["HTTP-Referer"] = "https://kilo.ai/" // kilocode_change + evt.provider.options.headers["X-Title"] = "Kilo Code" // kilocode_change + evt.provider.options.headers["X-BILLING-INVOKE-ORIGIN"] ??= "KiloCode" // kilocode_change + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/openai-compatible.ts b/packages/core/src/plugin/provider/openai-compatible.ts new file mode 100644 index 00000000000..76c33737066 --- /dev/null +++ b/packages/core/src/plugin/provider/openai-compatible.ts @@ -0,0 +1,17 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" + +export const OpenAICompatiblePlugin = PluginV2.define({ + id: PluginV2.ID.make("openai-compatible"), + effect: Effect.gen(function* () { + return { + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.sdk) return + if (!evt.package.includes("@ai-sdk/openai-compatible")) return + if (evt.options.includeUsage !== false) evt.options.includeUsage = true + const mod = yield* Effect.promise(() => import("@ai-sdk/openai-compatible")) + evt.sdk = mod.createOpenAICompatible(evt.options as any) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/openai.ts b/packages/core/src/plugin/provider/openai.ts new file mode 100644 index 00000000000..a81455f1985 --- /dev/null +++ b/packages/core/src/plugin/provider/openai.ts @@ -0,0 +1,27 @@ +import { Effect } from "effect" +import { ModelV2 } from "../../model" +import { PluginV2 } from "../../plugin" +import { ProviderV2 } from "../../provider" + +export const OpenAIPlugin = PluginV2.define({ + id: PluginV2.ID.make("openai"), + effect: Effect.gen(function* () { + return { + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "@ai-sdk/openai") return + const mod = yield* Effect.promise(() => import("@ai-sdk/openai")) + evt.sdk = mod.createOpenAI(evt.options) + }), + "aisdk.language": Effect.fn(function* (evt) { + if (evt.model.providerID !== ProviderV2.ID.openai) return + evt.language = evt.sdk.responses(evt.model.apiID) + }), + "model.update": Effect.fn(function* (evt) { + if (evt.model.providerID !== ProviderV2.ID.openai) return + // OpenAIPlugin sends OpenAI models through Responses; this alias is a + // chat-completions-only model, so remove it only from OpenAI's catalog. + if (evt.model.id === ModelV2.ID.make("gpt-5-chat-latest")) evt.cancel = true + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/opencode.ts b/packages/core/src/plugin/provider/opencode.ts new file mode 100644 index 00000000000..10bbb62dadb --- /dev/null +++ b/packages/core/src/plugin/provider/opencode.ts @@ -0,0 +1,27 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" +import { ProviderV2 } from "../../provider" + +export const OpencodePlugin = PluginV2.define({ + id: PluginV2.ID.make("opencode"), + effect: Effect.gen(function* () { + let hasKey = false + return { + "provider.update": Effect.fn(function* (evt) { + if (evt.provider.id !== ProviderV2.ID.opencode) return + hasKey = Boolean( + process.env.OPENCODE_API_KEY || + evt.provider.env.some((item) => process.env[item]) || + evt.provider.options.aisdk.provider.apiKey || + (evt.provider.enabled && evt.provider.enabled.via === "auth"), + ) + if (!hasKey) evt.provider.options.aisdk.provider.apiKey = "public" + }), + "model.update": Effect.fn(function* (evt) { + if (evt.model.providerID !== ProviderV2.ID.opencode) return + if (hasKey) return + if (evt.model.cost.some((item) => item.input > 0)) evt.cancel = true + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/openrouter.ts b/packages/core/src/plugin/provider/openrouter.ts new file mode 100644 index 00000000000..b3e926137f7 --- /dev/null +++ b/packages/core/src/plugin/provider/openrouter.ts @@ -0,0 +1,29 @@ +import { Effect } from "effect" +import { ModelV2 } from "../../model" +import { PluginV2 } from "../../plugin" +import { ProviderV2 } from "../../provider" + +export const OpenRouterPlugin = PluginV2.define({ + id: PluginV2.ID.make("openrouter"), + effect: Effect.gen(function* () { + return { + "provider.update": Effect.fn(function* (evt) { + if (evt.provider.id !== ProviderV2.ID.openrouter) return + evt.provider.options.headers["HTTP-Referer"] = "https://kilo.ai/" // kilocode_change + evt.provider.options.headers["X-Title"] = "Kilo Code" // kilocode_change + }), + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "@openrouter/ai-sdk-provider") return + const mod = yield* Effect.promise(() => import("@openrouter/ai-sdk-provider")) + evt.sdk = mod.createOpenRouter(evt.options) + }), + "model.update": Effect.fn(function* (evt) { + if (evt.model.providerID !== ProviderV2.ID.openrouter) return + // These are OpenRouter-specific OpenAI chat aliases that do not work on + // the generic path. Keep custom providers with matching IDs untouched. + if (evt.model.id === ModelV2.ID.make("gpt-5-chat-latest")) evt.cancel = true + if (evt.model.id === ModelV2.ID.make("openai/gpt-5-chat")) evt.cancel = true + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/perplexity.ts b/packages/core/src/plugin/provider/perplexity.ts new file mode 100644 index 00000000000..2415ab7c1a2 --- /dev/null +++ b/packages/core/src/plugin/provider/perplexity.ts @@ -0,0 +1,15 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" + +export const PerplexityPlugin = PluginV2.define({ + id: PluginV2.ID.make("perplexity"), + effect: Effect.gen(function* () { + return { + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "@ai-sdk/perplexity") return + const mod = yield* Effect.promise(() => import("@ai-sdk/perplexity")) + evt.sdk = mod.createPerplexity(evt.options) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/sap-ai-core.ts b/packages/core/src/plugin/provider/sap-ai-core.ts new file mode 100644 index 00000000000..7c57b785bff --- /dev/null +++ b/packages/core/src/plugin/provider/sap-ai-core.ts @@ -0,0 +1,44 @@ +import { Npm } from "../../npm" +import { Effect, Option } from "effect" +import { pathToFileURL } from "url" +import { PluginV2 } from "../../plugin" +import { ProviderV2 } from "../../provider" + +export const SapAICorePlugin = PluginV2.define({ + id: PluginV2.ID.make("sap-ai-core"), + effect: Effect.gen(function* () { + const npm = yield* Npm.Service + return { + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.model.providerID !== ProviderV2.ID.make("sap-ai-core")) return + const serviceKey = + process.env.AICORE_SERVICE_KEY ?? + (typeof evt.options.serviceKey === "string" ? evt.options.serviceKey : undefined) + if (serviceKey && !process.env.AICORE_SERVICE_KEY) process.env.AICORE_SERVICE_KEY = serviceKey + + const installedPath = evt.package.startsWith("file://") + ? evt.package + : Option.getOrUndefined((yield* npm.add(evt.package).pipe(Effect.orDie)).entrypoint) + if (!installedPath) throw new Error(`Package ${evt.package} has no import entrypoint`) + + const mod = yield* Effect.promise(async () => { + return (await import( + installedPath.startsWith("file://") ? installedPath : pathToFileURL(installedPath).href + )) as Record any> + }).pipe(Effect.orDie) + const match = Object.keys(mod).find((name) => name.startsWith("create")) + if (!match) throw new Error(`Package ${evt.package} has no provider factory export`) + + evt.sdk = mod[match]( + serviceKey + ? { deploymentId: process.env.AICORE_DEPLOYMENT_ID, resourceGroup: process.env.AICORE_RESOURCE_GROUP } + : {}, + ) + }), + "aisdk.language": Effect.fn(function* (evt) { + if (evt.model.providerID !== ProviderV2.ID.make("sap-ai-core")) return + evt.language = evt.sdk(evt.model.apiID) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/togetherai.ts b/packages/core/src/plugin/provider/togetherai.ts new file mode 100644 index 00000000000..b1870f26625 --- /dev/null +++ b/packages/core/src/plugin/provider/togetherai.ts @@ -0,0 +1,15 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" + +export const TogetherAIPlugin = PluginV2.define({ + id: PluginV2.ID.make("togetherai"), + effect: Effect.gen(function* () { + return { + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "@ai-sdk/togetherai") return + const mod = yield* Effect.promise(() => import("@ai-sdk/togetherai")) + evt.sdk = mod.createTogetherAI(evt.options) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/venice.ts b/packages/core/src/plugin/provider/venice.ts new file mode 100644 index 00000000000..8a3b950245c --- /dev/null +++ b/packages/core/src/plugin/provider/venice.ts @@ -0,0 +1,15 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" + +export const VenicePlugin = PluginV2.define({ + id: PluginV2.ID.make("venice"), + effect: Effect.gen(function* () { + return { + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "venice-ai-sdk-provider") return + const mod = yield* Effect.promise(() => import("venice-ai-sdk-provider")) + evt.sdk = mod.createVenice(evt.options) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/vercel.ts b/packages/core/src/plugin/provider/vercel.ts new file mode 100644 index 00000000000..2d50e3b0930 --- /dev/null +++ b/packages/core/src/plugin/provider/vercel.ts @@ -0,0 +1,21 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" +import { ProviderV2 } from "../../provider" + +export const VercelPlugin = PluginV2.define({ + id: PluginV2.ID.make("vercel"), + effect: Effect.gen(function* () { + return { + "provider.update": Effect.fn(function* (evt) { + if (evt.provider.id !== ProviderV2.ID.make("vercel")) return + evt.provider.options.headers["http-referer"] = "https://kilo.ai/" // kilocode_change + evt.provider.options.headers["x-title"] = "Kilo Code" // kilocode_change + }), + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "@ai-sdk/vercel") return + const mod = yield* Effect.promise(() => import("@ai-sdk/vercel")) + evt.sdk = mod.createVercel(evt.options) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/xai.ts b/packages/core/src/plugin/provider/xai.ts new file mode 100644 index 00000000000..b54aa7374c6 --- /dev/null +++ b/packages/core/src/plugin/provider/xai.ts @@ -0,0 +1,20 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" +import { ProviderV2 } from "../../provider" + +export const XAIPlugin = PluginV2.define({ + id: PluginV2.ID.make("xai"), + effect: Effect.gen(function* () { + return { + "aisdk.sdk": Effect.fn(function* (evt) { + if (evt.package !== "@ai-sdk/xai") return + const mod = yield* Effect.promise(() => import("@ai-sdk/xai")) + evt.sdk = mod.createXai(evt.options) + }), + "aisdk.language": Effect.fn(function* (evt) { + if (evt.model.providerID !== ProviderV2.ID.make("xai")) return + evt.language = evt.sdk.responses(evt.model.apiID) + }), + } + }), +}) diff --git a/packages/core/src/plugin/provider/zenmux.ts b/packages/core/src/plugin/provider/zenmux.ts new file mode 100644 index 00000000000..be8086f4826 --- /dev/null +++ b/packages/core/src/plugin/provider/zenmux.ts @@ -0,0 +1,16 @@ +import { Effect } from "effect" +import { PluginV2 } from "../../plugin" +import { ProviderV2 } from "../../provider" + +export const ZenmuxPlugin = PluginV2.define({ + id: PluginV2.ID.make("zenmux"), + effect: Effect.gen(function* () { + return { + "provider.update": Effect.fn(function* (evt) { + if (evt.provider.id !== ProviderV2.ID.make("zenmux")) return + evt.provider.options.headers["HTTP-Referer"] ??= "https://kilo.ai/" // kilocode_change + evt.provider.options.headers["X-Title"] ??= "Kilo Code" // kilocode_change + }), + } + }), +}) diff --git a/packages/core/src/process.ts b/packages/core/src/process.ts new file mode 100644 index 00000000000..f076ea4e42c --- /dev/null +++ b/packages/core/src/process.ts @@ -0,0 +1,234 @@ +import { Context, Duration, Effect, Fiber, Layer, Schema, Stream } from "effect" +import type { PlatformError } from "effect/PlatformError" +import { ChildProcess } from "effect/unstable/process" +import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner" +import { CrossSpawnSpawner } from "./cross-spawn-spawner" + +export class AppProcessError extends Schema.TaggedErrorClass()("AppProcessError", { + command: Schema.String, + exitCode: Schema.optional(Schema.Number), + stderr: Schema.optional(Schema.String), + cause: Schema.optional(Schema.Defect), +}) {} + +export interface RunOptions { + readonly maxOutputBytes?: number + readonly maxErrorBytes?: number + readonly signal?: AbortSignal + readonly timeout?: Duration.Input + readonly stdin?: string | Uint8Array | Stream.Stream +} + +export interface RunStreamOptions { + readonly signal?: AbortSignal + readonly includeStderr?: boolean + readonly okExitCodes?: ReadonlyArray + readonly maxErrorBytes?: number +} + +export interface RunResult { + readonly command: string + readonly exitCode: number + readonly stdout: Buffer + readonly stderr: Buffer + readonly stdoutTruncated: boolean + readonly stderrTruncated: boolean +} + +export type Interface = ChildProcessSpawner["Service"] & { + readonly run: (command: ChildProcess.Command, options?: RunOptions) => Effect.Effect + readonly runStream: ( + command: ChildProcess.Command, + options?: RunStreamOptions, + ) => Stream.Stream +} + +export class Service extends Context.Service()("@opencode/AppProcess") {} + +export const requireSuccess = (result: RunResult): Effect.Effect => + result.exitCode === 0 + ? Effect.succeed(result) + : Effect.fail( + new AppProcessError({ + command: result.command, + exitCode: result.exitCode, + stderr: result.stderr.toString("utf8"), + }), + ) + +export const requireExitIn = + (codes: ReadonlyArray) => + (result: RunResult): Effect.Effect => + codes.includes(result.exitCode) + ? Effect.succeed(result) + : Effect.fail( + new AppProcessError({ + command: result.command, + exitCode: result.exitCode, + stderr: result.stderr.toString("utf8"), + }), + ) + +const describeCommand = (command: ChildProcess.Command): string => { + if (command._tag === "StandardCommand") { + return command.args.length ? `${command.command} ${command.args.join(" ")}` : command.command + } + return `${describeCommand(command.left)} | ${describeCommand(command.right)}` +} + +const wrapError = (description: string, cause: unknown): AppProcessError => + cause instanceof AppProcessError ? cause : new AppProcessError({ command: description, cause }) + +const abortError = (signal: AbortSignal): Error => { + const reason = signal.reason + if (reason instanceof Error) return reason + const err = new Error("Aborted") + err.name = "AbortError" + return err +} + +const waitForAbort = (signal: AbortSignal) => + Effect.callback((resume) => { + if (signal.aborted) { + resume(Effect.fail(abortError(signal))) + return + } + const onabort = () => resume(Effect.fail(abortError(signal))) + signal.addEventListener("abort", onabort, { once: true }) + return Effect.sync(() => signal.removeEventListener("abort", onabort)) + }) + +const normalizeStdin = ( + input: string | Uint8Array | Stream.Stream, +): Stream.Stream => + typeof input === "string" + ? Stream.make(new TextEncoder().encode(input)) + : input instanceof Uint8Array + ? Stream.make(input) + : input + +const collectStream = (stream: Stream.Stream, maxOutputBytes: number | undefined) => + Stream.runFold( + stream, + () => ({ chunks: [] as Uint8Array[], bytes: 0, truncated: false }), + (acc, chunk) => { + if (maxOutputBytes === undefined) { + acc.chunks.push(chunk) + acc.bytes += chunk.length + return acc + } + const remaining = maxOutputBytes - acc.bytes + if (remaining > 0) acc.chunks.push(remaining >= chunk.length ? chunk : chunk.slice(0, remaining)) + acc.bytes += chunk.length + acc.truncated = acc.truncated || acc.bytes > maxOutputBytes + return acc + }, + ).pipe(Effect.map((x) => ({ buffer: Buffer.concat(x.chunks), truncated: x.truncated }))) + +export const layer = Layer.effect( + Service, + Effect.gen(function* () { + const spawner = yield* ChildProcessSpawner + + const runCommand = (command: ChildProcess.Command, options?: RunOptions) => { + const description = describeCommand(command) + const collect = Effect.scoped( + Effect.gen(function* () { + const handle = yield* spawner.spawn(command) + const [stdout, stderr, exitCode] = yield* Effect.all( + [ + collectStream(handle.stdout, options?.maxOutputBytes), + collectStream(handle.stderr, options?.maxErrorBytes), + handle.exitCode, + ], + { concurrency: "unbounded" }, + ) + return { + command: description, + exitCode, + stdout: stdout.buffer, + stderr: stderr.buffer, + stdoutTruncated: stdout.truncated, + stderrTruncated: stderr.truncated, + } satisfies RunResult + }), + ) + const timed = options?.timeout + ? Effect.timeoutOrElse(collect, { + duration: options.timeout, + orElse: () => Effect.fail(new AppProcessError({ command: description, cause: new Error("Timed out") })), + }) + : collect + const aborted = options?.signal + ? timed.pipe( + Effect.raceFirst( + waitForAbort(options.signal).pipe(Effect.mapError((cause) => wrapError(description, cause))), + ), + ) + : timed + return aborted.pipe(Effect.catch((cause) => Effect.fail(wrapError(description, cause)))) + } + + const run = Effect.fn("AppProcess.run")(function* (command: ChildProcess.Command, options?: RunOptions) { + if (options?.stdin === undefined) return yield* runCommand(command, options) + if (command._tag !== "StandardCommand") { + return yield* new AppProcessError({ + command: describeCommand(command), + cause: new Error("stdin option only supports StandardCommand; received PipedCommand"), + }) + } + const next = ChildProcess.make(command.command, command.args, { + ...command.options, + stdin: normalizeStdin(options.stdin), + }) + return yield* runCommand(next, options) + }) + + const runStream = ( + command: ChildProcess.Command, + options?: RunStreamOptions, + ): Stream.Stream => { + const description = describeCommand(command) + const okExitCodes = options?.okExitCodes + const built: Stream.Stream = Stream.unwrap( + Effect.gen(function* () { + const handle = yield* spawner.spawn(command) + const stderrFiber = yield* Effect.forkScoped( + collectStream(handle.stderr, options?.maxErrorBytes).pipe(Effect.map((x) => x.buffer.toString("utf8"))), + ) + const source = options?.includeStderr === true ? handle.all : handle.stdout + const lines = source.pipe( + Stream.decodeText, + Stream.splitLines, + Stream.filter((line) => line.length > 0), + ) + const tail = Stream.unwrap( + Effect.gen(function* () { + const code = yield* handle.exitCode + if (okExitCodes && okExitCodes.length > 0 && !okExitCodes.includes(code)) { + const stderr = yield* Fiber.join(stderrFiber) + return Stream.fail(new AppProcessError({ command: description, exitCode: code, stderr })) + } + return Stream.empty + }), + ) + return Stream.concat(lines, tail) as Stream.Stream + }), + ) + const mapped = built.pipe( + Stream.catch((cause): Stream.Stream => Stream.fail(wrapError(description, cause))), + ) + if (!options?.signal) return mapped + const signal = options.signal + return mapped.pipe( + Stream.interruptWhen(waitForAbort(signal).pipe(Effect.mapError((cause) => wrapError(description, cause)))), + ) + } + + return Service.of({ ...spawner, run, runStream }) + }), +) + +export const defaultLayer = layer.pipe(Layer.provide(CrossSpawnSpawner.defaultLayer)) + +export * as AppProcess from "./process" diff --git a/packages/core/src/provider.ts b/packages/core/src/provider.ts new file mode 100644 index 00000000000..7c1c9666542 --- /dev/null +++ b/packages/core/src/provider.ts @@ -0,0 +1,120 @@ +export * as ProviderV2 from "./provider" + +import { withStatics } from "./schema" +import { Schema } from "effect" + +export const ID = Schema.String.pipe( + Schema.brand("ProviderV2.ID"), + withStatics((schema) => ({ + // Well-known providers + opencode: schema.make("opencode"), + anthropic: schema.make("anthropic"), + openai: schema.make("openai"), + google: schema.make("google"), + googleVertex: schema.make("google-vertex"), + githubCopilot: schema.make("github-copilot"), + amazonBedrock: schema.make("amazon-bedrock"), + azure: schema.make("azure"), + openrouter: schema.make("openrouter"), + mistral: schema.make("mistral"), + gitlab: schema.make("gitlab"), + })), +) +export type ID = typeof ID.Type + +const OpenAIResponses = Schema.Struct({ + type: Schema.Literal("openai/responses"), + url: Schema.String, + websocket: Schema.optional(Schema.Boolean), +}) + +const OpenAICompletions = Schema.Struct({ + type: Schema.Literal("openai/completions"), + url: Schema.String, + reasoning: Schema.Union([ + Schema.Struct({ + type: Schema.Literal("reasoning_content"), + }), + Schema.Struct({ + type: Schema.Literal("reasoning_details"), + }), + ]).pipe(Schema.optional), +}) +export type OpenAICompletions = typeof OpenAICompletions.Type + +const AISDK = Schema.Struct({ + type: Schema.Literal("aisdk"), + package: Schema.String, + url: Schema.String.pipe(Schema.optional), +}) + +const AnthropicMessages = Schema.Struct({ + type: Schema.Literal("anthropic/messages"), + url: Schema.String, +}) + +const UnknownEndpoint = Schema.Struct({ + type: Schema.Literal("unknown"), +}) + +export const Endpoint = Schema.Union([ + UnknownEndpoint, + OpenAIResponses, + OpenAICompletions, + AnthropicMessages, + AISDK, +]).pipe(Schema.toTaggedUnion("type")) +export type Endpoint = typeof Endpoint.Type + +export const Options = Schema.Struct({ + headers: Schema.Record(Schema.String, Schema.String), + body: Schema.Record(Schema.String, Schema.Any), + aisdk: Schema.Struct({ + provider: Schema.Record(Schema.String, Schema.Any), + request: Schema.Record(Schema.String, Schema.Any), + }), +}) +export type Options = typeof Options.Type + +export class Info extends Schema.Class("ProviderV2.Info")({ + id: ID, + name: Schema.String, + enabled: Schema.Union([ + Schema.Literal(false), + Schema.Struct({ + via: Schema.Literal("env"), + name: Schema.String, + }), + Schema.Struct({ + via: Schema.Literal("auth"), + service: Schema.String, + }), + Schema.Struct({ + via: Schema.Literal("custom"), + data: Schema.Record(Schema.String, Schema.Any), + }), + ]), + env: Schema.String.pipe(Schema.Array), + endpoint: Endpoint, + options: Options, +}) { + static empty(providerID: ID) { + return new Info({ + id: providerID, + name: providerID, + enabled: false, + env: [], + endpoint: { + type: "unknown", + }, + options: { + headers: {}, + body: {}, + aisdk: { + provider: {}, + request: {}, + }, + }, + }) + } +} diff --git a/packages/core/src/schema.ts b/packages/core/src/schema.ts index 2a6c02349fb..5b4042c7369 100644 --- a/packages/core/src/schema.ts +++ b/packages/core/src/schema.ts @@ -1,5 +1,4 @@ import { Option, Schema, SchemaGetter } from "effect" -import { zod, ZodOverride } from "./effect-zod" /** * Integer greater than zero. @@ -21,7 +20,6 @@ export const optionalOmitUndefined = (schema: S) => decode: SchemaGetter.passthrough({ strict: false }), encode: SchemaGetter.transformOptional(Option.filter((value) => value !== undefined)), }), - Schema.annotate({ [ZodOverride]: zod(schema).optional() }), ) /** diff --git a/packages/opencode/src/v2/session-prompt.ts b/packages/core/src/session-prompt.ts similarity index 64% rename from packages/opencode/src/v2/session-prompt.ts rename to packages/core/src/session-prompt.ts index 86d8e52eb78..14167fc2889 100644 --- a/packages/opencode/src/v2/session-prompt.ts +++ b/packages/core/src/session-prompt.ts @@ -29,8 +29,21 @@ export class AgentAttachment extends Schema.Class("Prompt.Agent source: Source.pipe(Schema.optional), }) {} +export class ReferenceAttachment extends Schema.Class("Prompt.ReferenceAttachment")({ + name: Schema.String, + kind: Schema.Literals(["local", "git", "invalid"]), + uri: Schema.String.pipe(Schema.optional), + repository: Schema.String.pipe(Schema.optional), + branch: Schema.String.pipe(Schema.optional), + target: Schema.String.pipe(Schema.optional), + targetUri: Schema.String.pipe(Schema.optional), + problem: Schema.String.pipe(Schema.optional), + source: Source.pipe(Schema.optional), +}) {} + export class Prompt extends Schema.Class("Prompt")({ text: Schema.String, files: Schema.Array(FileAttachment).pipe(Schema.optional), agents: Schema.Array(AgentAttachment).pipe(Schema.optional), + references: Schema.Array(ReferenceAttachment).pipe(Schema.optional), }) {} diff --git a/packages/opencode/src/v2/tool-output.ts b/packages/core/src/tool-output.ts similarity index 100% rename from packages/opencode/src/v2/tool-output.ts rename to packages/core/src/tool-output.ts diff --git a/packages/core/src/util/error.ts b/packages/core/src/util/error.ts index 9d3b7c661a3..7338571f298 100644 --- a/packages/core/src/util/error.ts +++ b/packages/core/src/util/error.ts @@ -1,8 +1,8 @@ -import z from "zod" +import { Schema } from "effect" export abstract class NamedError extends Error { - abstract schema(): z.core.$ZodType - abstract toObject(): { name: string; data: any } + abstract schema(): Schema.Top + abstract toObject(): { name: string; data: unknown } static hasName(error: unknown, name: string): boolean { return ( @@ -10,30 +10,42 @@ export abstract class NamedError extends Error { ) } - static create(name: Name, data: Data) { - const schema = z - .object({ - name: z.literal(name), - data, - }) - .meta({ - ref: name, - }) + static create( + name: Name, + fields: Fields, + ): ReturnType>> + static create( + name: Name, + data: DataSchema, + ): ReturnType> + static create(name: Name, data: Schema.Top | Schema.Struct.Fields) { + return NamedError.createSchemaClass(name, Schema.isSchema(data) ? data : Schema.Struct(data)) + } + + private static createSchemaClass(name: Name, data: DataSchema) { + const schema = Schema.Struct({ + name: Schema.Literal(name), + data, + }).annotate({ identifier: name }) + type Data = Schema.Schema.Type + const result = class extends NamedError { public static readonly Schema = schema + public static readonly EffectSchema = schema + public static readonly tag = name - public override readonly name = name as Name + public override readonly name = name constructor( - public readonly data: z.input, + public readonly data: Data, options?: ErrorOptions, ) { super(name, options) this.name = name } - static isInstance(input: any): input is InstanceType { - return typeof input === "object" && "name" in input && input.name === name + static isInstance(input: unknown): input is InstanceType { + return NamedError.hasName(input, name) } schema() { @@ -51,10 +63,7 @@ export abstract class NamedError extends Error { return result } - public static readonly Unknown = NamedError.create( - "UnknownError", - z.object({ - message: z.string(), - }), - ) + public static readonly Unknown = NamedError.create("UnknownError", { + message: Schema.String, + }) } diff --git a/packages/core/src/util/log.ts b/packages/core/src/util/log.ts index 85ffbd915dc..fc341f09c02 100644 --- a/packages/core/src/util/log.ts +++ b/packages/core/src/util/log.ts @@ -4,12 +4,16 @@ import path from "path" import { existsSync, writeFileSync } from "fs" // kilocode_change import fs from "fs/promises" import * as Global from "../global" -import z from "zod" +import { Schema } from "effect" import { Glob } from "./glob" import { createStream } from "rotating-file-stream" // kilocode_change +import { KILO_RUN_ID } from "./opencode-process" // kilocode_change -export const Level = z.enum(["DEBUG", "INFO", "WARN", "ERROR"]).meta({ ref: "LogLevel", description: "Log level" }) -export type Level = z.infer +export const Level = Schema.Literals(["DEBUG", "INFO", "WARN", "ERROR"]).annotate({ + identifier: "LogLevel", + description: "Log level", +}) +export type Level = Schema.Schema.Type const levelPriority: Record = { DEBUG: 0, @@ -18,6 +22,7 @@ const levelPriority: Record = { ERROR: 3, } const keep = 10 +const initializedRunID = "KILO_LOG_INITIALIZED_RUN_ID" let level: Level = "INFO" @@ -55,29 +60,44 @@ let logpath = "" export function file() { return logpath } -let write = (msg: any) => { +const stderr = (msg: any) => { process.stderr.write(msg) return msg.length } +let write = stderr +let stream: ReturnType | undefined // kilocode_change export async function init(options: Options) { if (options.level) level = options.level void cleanup(Global.Path.log) - if (options.print) return + // kilocode_change start - initialize one rotating stream and truncate dev.log once per Kilo run + if (stream) { + const active = stream + stream = undefined + await new Promise((resolve) => active.end(resolve)) + } + if (options.print) { + write = stderr + return + } logpath = path.join( Global.Path.log, options.dev ? "dev.log" : new Date().toISOString().split(".")[0].replace(/:/g, "") + ".log", ) - await fs.truncate(logpath).catch(() => {}) - // kilocode_change start - use rotating-file-stream to cap log files at 50 MB + const run = process.env[KILO_RUN_ID] + if (!options.dev || !run || process.env[initializedRunID] !== run) { + await fs.truncate(logpath).catch(() => {}) + if (options.dev && run) process.env[initializedRunID] = run + } const dir = path.dirname(logpath) - const stream = createStream(path.basename(logpath), { + const active = createStream(path.basename(logpath), { size: "50M", maxFiles: 10, history: ".log-history", path: dir, }) - stream.on("rotation", () => { + stream = active + active.on("rotation", () => { if (!existsSync(dir)) return try { @@ -91,14 +111,14 @@ export async function init(options: Options) { process.stderr.write("log stream warning: " + msg + "\n") } }) - stream.on("error", (err: Error) => { + active.on("error", (err: Error) => { process.stderr.write("log stream error: " + err.message + "\n") }) - stream.on("warning", (err: Error) => { + active.on("warning", (err: Error) => { process.stderr.write("log stream warning: " + err.message + "\n") }) write = (msg: any) => { - stream.write(msg) + active.write(msg) return msg.length } // kilocode_change end diff --git a/packages/opencode/src/v2/schema.ts b/packages/core/src/v2-schema.ts similarity index 88% rename from packages/opencode/src/v2/schema.ts rename to packages/core/src/v2-schema.ts index 44587b838a4..a34b0b15169 100644 --- a/packages/opencode/src/v2/schema.ts +++ b/packages/core/src/v2-schema.ts @@ -7,4 +7,4 @@ export const DateTimeUtcFromMillis = Schema.Finite.pipe( }), ) -export * as V2Schema from "./schema" +export * as V2Schema from "./v2-schema" diff --git a/packages/core/test/catalog.test.ts b/packages/core/test/catalog.test.ts new file mode 100644 index 00000000000..c5ac2c06cc4 --- /dev/null +++ b/packages/core/test/catalog.test.ts @@ -0,0 +1,201 @@ +import { describe, expect } from "bun:test" +import { DateTime, Effect, Layer, Option } from "effect" +import { Catalog } from "@opencode-ai/core/catalog" +import { Instance } from "@opencode-ai/core/instance" +import { ModelV2 } from "@opencode-ai/core/model" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { ProviderV2 } from "@opencode-ai/core/provider" +import { testEffect } from "./lib/effect" + +const instanceLayer = Layer.succeed(Instance.Service, Instance.Service.of({ directory: "test" })) +const it = testEffect(Catalog.layer.pipe(Layer.provideMerge(PluginV2.defaultLayer), Layer.provide(instanceLayer))) + +describe("CatalogV2", () => { + it.effect("normalizes provider baseURL into endpoint url", () => + Effect.gen(function* () { + const catalog = yield* Catalog.Service + const providerID = ProviderV2.ID.make("test") + + yield* catalog.provider.update(providerID, (provider) => { + provider.endpoint = { + type: "aisdk", + package: "@ai-sdk/openai-compatible", + url: "https://default.example.com", + } + provider.options.aisdk.provider.baseURL = "https://override.example.com" + }) + + const provider = yield* catalog.provider.get(providerID) + + expect(provider.endpoint).toEqual({ + type: "aisdk", + package: "@ai-sdk/openai-compatible", + url: "https://override.example.com", + }) + expect(provider.options.aisdk.provider.baseURL).toBeUndefined() + }), + ) + + it.effect("normalizes model baseURL into endpoint url", () => + Effect.gen(function* () { + const catalog = yield* Catalog.Service + const providerID = ProviderV2.ID.make("test") + const modelID = ModelV2.ID.make("model") + + yield* catalog.provider.update(providerID, (provider) => { + provider.endpoint = { + type: "aisdk", + package: "@ai-sdk/openai-compatible", + url: "https://provider.example.com", + } + }) + yield* catalog.model.update(providerID, modelID, (model) => { + model.endpoint = { + type: "aisdk", + package: "@ai-sdk/openai-compatible", + url: "https://model.example.com", + } + model.options.aisdk.provider.baseURL = "https://override.example.com" + }) + + const model = yield* catalog.model.get(providerID, modelID) + + expect(model.endpoint).toEqual({ + type: "aisdk", + package: "@ai-sdk/openai-compatible", + url: "https://override.example.com", + }) + expect(model.options.aisdk.provider.baseURL).toBeUndefined() + }), + ) + + it.effect("resolves unknown model endpoint from provider endpoint", () => + Effect.gen(function* () { + const catalog = yield* Catalog.Service + const providerID = ProviderV2.ID.make("test") + const modelID = ModelV2.ID.make("model") + + yield* catalog.provider.update(providerID, (provider) => { + provider.endpoint = { + type: "aisdk", + package: "@ai-sdk/openai-compatible", + url: "https://provider.example.com", + } + }) + yield* catalog.model.update(providerID, modelID, () => {}) + + const model = yield* catalog.model.get(providerID, modelID) + + expect(model.endpoint).toEqual({ + type: "aisdk", + package: "@ai-sdk/openai-compatible", + url: "https://provider.example.com", + }) + }), + ) + + it.effect("runs provider hooks after baseURL is normalized", () => + Effect.gen(function* () { + const catalog = yield* Catalog.Service + const plugin = yield* PluginV2.Service + const providerID = ProviderV2.ID.make("test") + const seen: unknown[] = [] + + yield* plugin.add({ + id: PluginV2.ID.make("test"), + effect: Effect.succeed({ + "provider.update": (evt) => + Effect.sync(() => { + seen.push(evt.provider.endpoint.type) + if (evt.provider.endpoint.type === "aisdk") seen.push(evt.provider.endpoint.url) + seen.push(evt.provider.options.aisdk.provider.baseURL) + }), + }), + }) + yield* catalog.provider.update(providerID, (provider) => { + provider.endpoint = { + type: "aisdk", + package: "@ai-sdk/openai-compatible", + } + provider.options.aisdk.provider.baseURL = "https://provider.example.com" + }) + + expect(seen).toEqual(["aisdk", "https://provider.example.com", undefined]) + }), + ) + + it.effect("resolves provider and model option merges", () => + Effect.gen(function* () { + const catalog = yield* Catalog.Service + const providerID = ProviderV2.ID.make("test") + const modelID = ModelV2.ID.make("model") + + yield* catalog.provider.update(providerID, (provider) => { + provider.options.headers.provider = "provider" + provider.options.headers.shared = "provider" + provider.options.body.provider = true + provider.options.aisdk.provider.provider = true + }) + yield* catalog.model.update(providerID, modelID, (model) => { + model.options.headers.model = "model" + model.options.headers.shared = "model" + model.options.body.model = true + model.options.aisdk.provider.model = true + model.options.aisdk.request.request = true + }) + + const model = yield* catalog.model.get(providerID, modelID) + + expect(model.options.headers).toEqual({ provider: "provider", shared: "model", model: "model" }) + expect(model.options.body).toEqual({ provider: true, model: true }) + expect(model.options.aisdk.provider).toEqual({ provider: true, model: true }) + expect(model.options.aisdk.request).toEqual({ request: true }) + }), + ) + + it.effect("falls back to newest available model when no default is configured", () => + Effect.gen(function* () { + const catalog = yield* Catalog.Service + const providerID = ProviderV2.ID.make("test") + + yield* catalog.provider.update(providerID, (provider) => { + provider.enabled = { via: "custom", data: {} } + }) + yield* catalog.model.update(providerID, ModelV2.ID.make("old"), (model) => { + model.time.released = DateTime.makeUnsafe(1000) + }) + yield* catalog.model.update(providerID, ModelV2.ID.make("new"), (model) => { + model.time.released = DateTime.makeUnsafe(2000) + }) + + const model = yield* catalog.model.default() + + expect(Option.getOrUndefined(model)?.id).toMatch("new") + }), + ) + + it.effect("small model prefers small keyword candidates before cost scoring", () => + Effect.gen(function* () { + const catalog = yield* Catalog.Service + const providerID = ProviderV2.ID.make("test") + + yield* catalog.provider.update(providerID, () => {}) + yield* catalog.model.update(providerID, ModelV2.ID.make("cheap-large"), (model) => { + model.capabilities.input = ["text"] + model.capabilities.output = ["text"] + model.cost = [{ input: 1, output: 1, cache: { read: 0, write: 0 } }] + model.time.released = DateTime.makeUnsafe(Date.now()) + }) + yield* catalog.model.update(providerID, ModelV2.ID.make("expensive-mini"), (model) => { + model.capabilities.input = ["text"] + model.capabilities.output = ["text"] + model.cost = [{ input: 10, output: 10, cache: { read: 0, write: 0 } }] + model.time.released = DateTime.makeUnsafe(Date.now()) + }) + + const model = yield* catalog.model.small(providerID) + + expect(Option.getOrUndefined(model)?.id).toMatch("expensive-mini") + }), + ) +}) diff --git a/packages/opencode/test/provider/copilot/convert-to-copilot-messages.test.ts b/packages/core/test/github-copilot/convert-to-copilot-messages.test.ts similarity index 99% rename from packages/opencode/test/provider/copilot/convert-to-copilot-messages.test.ts rename to packages/core/test/github-copilot/convert-to-copilot-messages.test.ts index 6f874db6d2e..65f4b6a5369 100644 --- a/packages/opencode/test/provider/copilot/convert-to-copilot-messages.test.ts +++ b/packages/core/test/github-copilot/convert-to-copilot-messages.test.ts @@ -1,4 +1,4 @@ -import { convertToOpenAICompatibleChatMessages as convertToCopilotMessages } from "@/provider/sdk/copilot/chat/convert-to-openai-compatible-chat-messages" +import { convertToOpenAICompatibleChatMessages as convertToCopilotMessages } from "@opencode-ai/core/github-copilot/chat/convert-to-openai-compatible-chat-messages" import { describe, test, expect } from "bun:test" describe("system messages", () => { diff --git a/packages/opencode/test/provider/copilot/copilot-chat-model.test.ts b/packages/core/test/github-copilot/copilot-chat-model.test.ts similarity index 99% rename from packages/opencode/test/provider/copilot/copilot-chat-model.test.ts rename to packages/core/test/github-copilot/copilot-chat-model.test.ts index 389a72bb377..bc1e2ecd956 100644 --- a/packages/opencode/test/provider/copilot/copilot-chat-model.test.ts +++ b/packages/core/test/github-copilot/copilot-chat-model.test.ts @@ -1,4 +1,4 @@ -import { OpenAICompatibleChatLanguageModel } from "@/provider/sdk/copilot/chat/openai-compatible-chat-language-model" +import { OpenAICompatibleChatLanguageModel } from "@opencode-ai/core/github-copilot/chat/openai-compatible-chat-language-model" import { describe, test, expect, mock } from "bun:test" import type { LanguageModelV3Prompt } from "@ai-sdk/provider" diff --git a/packages/opencode/test/util/effect-zod.test.ts b/packages/core/test/kilocode/effect-zod.test.ts similarity index 99% rename from packages/opencode/test/util/effect-zod.test.ts rename to packages/core/test/kilocode/effect-zod.test.ts index ab3923d8e0a..5874aa55a76 100644 --- a/packages/opencode/test/util/effect-zod.test.ts +++ b/packages/core/test/kilocode/effect-zod.test.ts @@ -9,7 +9,7 @@ function json(schema: z.ZodTypeAny) { return rest } -describe("util.effect-zod", () => { +describe("effect-zod", () => { test("converts class schemas for route dto shapes", () => { class Method extends Schema.Class("ProviderAuthMethod")({ type: Schema.Union([Schema.Literal("oauth"), Schema.Literal("api")]), diff --git a/packages/opencode/test/provider/models.test.ts b/packages/core/test/models.test.ts similarity index 88% rename from packages/opencode/test/provider/models.test.ts rename to packages/core/test/models.test.ts index 4336a3786d0..26e254f53e8 100644 --- a/packages/opencode/test/provider/models.test.ts +++ b/packages/core/test/models.test.ts @@ -4,11 +4,8 @@ import { HttpClient, HttpClientResponse } from "effect/unstable/http" import { AppFileSystem } from "@opencode-ai/core/filesystem" import { Flag } from "@opencode-ai/core/flag/flag" import { Global } from "@opencode-ai/core/global" -import { ModelsDev } from "../../src/provider/models" -import { ModelCache } from "../../src/provider/model-cache" // kilocode_change -import { Config } from "../../src/config/config" // kilocode_change -import { Auth } from "../../src/auth" // kilocode_change -import { it } from "../lib/effect" +import { ModelsDev } from "@opencode-ai/core/models" +import { it } from "./lib/effect" import { rm, writeFile, utimes, mkdir } from "fs/promises" import path from "path" @@ -73,13 +70,16 @@ const fixture2: Record = { interface MockState { body: string status: number - calls: Array<{ url: string }> + calls: Array<{ url: string; userAgent: string | null }> } const makeMockClient = (state: Ref.Ref) => HttpClient.make((request) => Effect.gen(function* () { - yield* Ref.update(state, (s) => ({ ...s, calls: [...s.calls, { url: request.url }] })) + yield* Ref.update(state, (s) => ({ + ...s, + calls: [...s.calls, { url: request.url, userAgent: request.headers["user-agent"] ?? null }], + })) const s = yield* Ref.get(state) return HttpClientResponse.fromWeb(request, new Response(s.body, { status: s.status })) }), @@ -92,9 +92,6 @@ const buildLayer = (state: Ref.Ref) => Layer.fresh(ModelsDev.layer).pipe( Layer.provide(Layer.succeed(HttpClient.HttpClient, makeMockClient(state))), Layer.provide(AppFileSystem.defaultLayer), - Layer.provide(Config.defaultLayer), // kilocode_change - Layer.provide(Auth.defaultLayer), // kilocode_change - Layer.provide(ModelCache.defaultLayer), // kilocode_change ) const writeCache = (data: object, mtimeMs?: number) => @@ -124,11 +121,7 @@ const initialState: MockState = { calls: [], } -// kilocode_change start - skip: upstream tests assert raw-fixture passthrough but Kilo's -// ModelsDev.get() filters/injects providers based on effect-native config access (kilo-allowed -// gating, apertis options, and Kilo provider injection). -// kilocode_change end -describe.skip("ModelsDev Service", () => { +describe("ModelsDev Service", () => { it.live("get() returns providers from disk when cache file exists", () => Effect.gen(function* () { yield* writeCache(fixture) @@ -143,14 +136,14 @@ describe.skip("ModelsDev Service", () => { }), ) - it.live("get() returns {} when disk empty and fetch disabled", () => + it.live("get() returns bundled snapshot when disk empty and fetch disabled", () => Effect.gen(function* () { const state = yield* Ref.make(initialState) const result = yield* provided( state, ModelsDev.Service.use((s) => s.get()), ) - expect(result).toEqual({}) + expect(Object.keys(result).length).toBeGreaterThan(0) const final = yield* Ref.get(state) expect(final.calls).toEqual([]) }), @@ -212,6 +205,8 @@ describe.skip("ModelsDev Service", () => { const final = yield* Ref.get(state) expect(final.calls.length).toBe(1) expect(final.calls[0].url).toContain("/api.json") + expect(final.calls[0].userAgent).toStartWith("opencode/") + expect(final.calls[0].userAgent).toEndWith(`/${Flag.KILO_CLIENT}`) }), ) @@ -261,7 +256,7 @@ describe.skip("ModelsDev Service", () => { }), ) expect(result).toEqual(fixture) - // withTransientReadRetry retries 5xx, so calls may be > 1. + // retryTransient retries 5xx, so calls may be > 1. const final = yield* Ref.get(state) expect(final.calls.length).toBeGreaterThanOrEqual(1) }), diff --git a/packages/core/test/plugin/fixtures/provider-factory.ts b/packages/core/test/plugin/fixtures/provider-factory.ts new file mode 100644 index 00000000000..7278c231dd5 --- /dev/null +++ b/packages/core/test/plugin/fixtures/provider-factory.ts @@ -0,0 +1,9 @@ +export function createFixtureProvider(options: Record) { + const captured = Object.fromEntries(Object.entries(options)) + return Object.assign((modelID: string) => ({ modelID, options: captured }), { + options: captured, + languageModel(modelID: string) { + return { modelID, options: captured } + }, + }) +} diff --git a/packages/core/test/plugin/provider-alibaba.test.ts b/packages/core/test/plugin/provider-alibaba.test.ts new file mode 100644 index 00000000000..06e6f969fdc --- /dev/null +++ b/packages/core/test/plugin/provider-alibaba.test.ts @@ -0,0 +1,67 @@ +import { describe, expect } from "bun:test" +import { createAlibaba } from "@ai-sdk/alibaba" +import { Effect } from "effect" +import { ModelV2 } from "@opencode-ai/core/model" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { AlibabaPlugin } from "@opencode-ai/core/plugin/provider/alibaba" +import { it, model } from "./provider-helper" + +describe("AlibabaPlugin", () => { + it.effect("creates an Alibaba SDK for @ai-sdk/alibaba", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AlibabaPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { model: model("alibaba", "qwen"), package: "@ai-sdk/alibaba", options: { name: "alibaba" } }, + {}, + ) + expect(result.sdk).toBeDefined() + }), + ) + + it.effect("ignores non-Alibaba SDK packages", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AlibabaPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { model: model("alibaba", "qwen"), package: "@ai-sdk/openai-compatible", options: { name: "alibaba" } }, + {}, + ) + expect(result.sdk).toBeUndefined() + }), + ) + + it.effect("matches the old bundled Alibaba SDK provider naming", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AlibabaPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("custom-alibaba", "qwen"), + package: "@ai-sdk/alibaba", + options: { name: "custom-alibaba", apiKey: "test" }, + }, + {}, + ) + const expected = createAlibaba({ apiKey: "test", ...{ name: "custom-alibaba" } }).languageModel("qwen") + const actual = result.sdk?.languageModel("qwen") + expect(actual?.provider).toBe(expected.provider) + expect(actual?.modelId).toBe(expected.modelId) + }), + ) + + it.effect("uses the old default languageModel(apiID) behavior", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AlibabaPlugin) + const item = model("alibaba", "alias", { apiID: ModelV2.ID.make("qwen-plus") }) + const result = yield* plugin.trigger("aisdk.sdk", { model: item, package: "@ai-sdk/alibaba", options: {} }, {}) + const language = result.sdk?.languageModel(item.apiID) + expect(language?.modelId).toBe("qwen-plus") + expect(language?.provider).toBe("alibaba.chat") + }), + ) +}) diff --git a/packages/core/test/plugin/provider-amazon-bedrock.test.ts b/packages/core/test/plugin/provider-amazon-bedrock.test.ts new file mode 100644 index 00000000000..c70ada08d94 --- /dev/null +++ b/packages/core/test/plugin/provider-amazon-bedrock.test.ts @@ -0,0 +1,465 @@ +import { describe, expect } from "bun:test" +import { Effect } from "effect" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { AmazonBedrockPlugin } from "@opencode-ai/core/plugin/provider/amazon-bedrock" +import { fakeSelectorSdk, it, model, provider, withEnv } from "./provider-helper" + +function bedrockBaseURL(sdk: unknown, modelID = "anthropic.claude-sonnet-4-5") { + const language = (sdk as { languageModel: (id: string) => unknown }).languageModel(modelID) + return (language as { config: { baseUrl: () => string } }).config.baseUrl() +} + +function bedrockFetch(sdk: unknown, modelID = "anthropic.claude-sonnet-4-5") { + const language = (sdk as { languageModel: (id: string) => unknown }).languageModel(modelID) + return ( + language as { config: { fetch: (input: Parameters[0], init?: RequestInit) => Promise } } + ).config.fetch +} + +describe("AmazonBedrockPlugin", () => { + it.effect("moves endpoint option to endpoint URL", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AmazonBedrockPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("amazon-bedrock", { + options: { + headers: {}, + body: {}, + aisdk: { provider: { endpoint: "https://bedrock.example" }, request: {} }, + }, + }), + cancel: false, + }, + ) + expect(result.provider.endpoint).toEqual({ + type: "aisdk", + package: "test-provider", + url: "https://bedrock.example", + }) + expect(result.provider.options.aisdk.provider.endpoint).toBeUndefined() + }), + ) + + it.effect("prefers endpoint over baseURL for SDK base URL", () => + withEnv({ AWS_BEARER_TOKEN_BEDROCK: undefined, AWS_PROFILE: undefined, AWS_ACCESS_KEY_ID: undefined }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AmazonBedrockPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("amazon-bedrock", "anthropic.claude-sonnet-4-5"), + package: "@ai-sdk/amazon-bedrock", + options: { + name: "amazon-bedrock", + bearerToken: "token", + baseURL: "https://base.example", + endpoint: "https://endpoint.example", + region: "us-east-1", + }, + }, + {}, + ) + expect(bedrockBaseURL(result.sdk)).toBe("https://endpoint.example") + }), + ), + ) + + it.effect("uses baseURL as SDK base URL", () => + withEnv({ AWS_BEARER_TOKEN_BEDROCK: undefined, AWS_PROFILE: undefined, AWS_ACCESS_KEY_ID: undefined }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AmazonBedrockPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("amazon-bedrock", "anthropic.claude-sonnet-4-5"), + package: "@ai-sdk/amazon-bedrock", + options: { + name: "amazon-bedrock", + bearerToken: "token", + baseURL: "https://base.example", + region: "us-east-1", + }, + }, + {}, + ) + expect(bedrockBaseURL(result.sdk)).toBe("https://base.example") + }), + ), + ) + + it.effect("creates SDK without explicit credential env so the default AWS chain can resolve credentials", () => + withEnv( + { + AWS_ACCESS_KEY_ID: undefined, + AWS_BEARER_TOKEN_BEDROCK: undefined, + AWS_CONTAINER_CREDENTIALS_FULL_URI: undefined, + AWS_CONTAINER_CREDENTIALS_RELATIVE_URI: undefined, + AWS_PROFILE: undefined, + AWS_REGION: undefined, + AWS_WEB_IDENTITY_TOKEN_FILE: undefined, + }, + () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AmazonBedrockPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("amazon-bedrock", "anthropic.claude-sonnet-4-5"), + package: "@ai-sdk/amazon-bedrock", + options: { name: "amazon-bedrock" }, + }, + {}, + ) + expect(result.sdk).toBeDefined() + expect(bedrockBaseURL(result.sdk)).toBe("https://bedrock-runtime.us-east-1.amazonaws.com") + }), + ), + ) + + it.effect("uses config region over AWS_REGION for SDK base URL", () => + withEnv({ AWS_BEARER_TOKEN_BEDROCK: "token", AWS_REGION: "us-east-1" }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AmazonBedrockPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("amazon-bedrock", "anthropic.claude-sonnet-4-5"), + package: "@ai-sdk/amazon-bedrock", + options: { name: "amazon-bedrock", region: "eu-west-1" }, + }, + {}, + ) + expect(bedrockBaseURL(result.sdk)).toBe("https://bedrock-runtime.eu-west-1.amazonaws.com") + }), + ), + ) + + it.effect("uses AWS_REGION for SDK base URL when config region is absent", () => + withEnv({ AWS_BEARER_TOKEN_BEDROCK: "token", AWS_REGION: "eu-west-1" }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AmazonBedrockPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("amazon-bedrock", "anthropic.claude-sonnet-4-5"), + package: "@ai-sdk/amazon-bedrock", + options: { name: "amazon-bedrock" }, + }, + {}, + ) + expect(bedrockBaseURL(result.sdk)).toBe("https://bedrock-runtime.eu-west-1.amazonaws.com") + }), + ), + ) + + it.effect("defaults SDK region to us-east-1", () => + withEnv({ AWS_BEARER_TOKEN_BEDROCK: "token", AWS_REGION: undefined }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AmazonBedrockPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("amazon-bedrock", "anthropic.claude-sonnet-4-5"), + package: "@ai-sdk/amazon-bedrock", + options: { name: "amazon-bedrock" }, + }, + {}, + ) + expect(bedrockBaseURL(result.sdk)).toBe("https://bedrock-runtime.us-east-1.amazonaws.com") + }), + ), + ) + + it.effect("loads bearer token option into env and uses bearer auth", () => + withEnv({ AWS_ACCESS_KEY_ID: undefined, AWS_BEARER_TOKEN_BEDROCK: undefined, AWS_PROFILE: undefined }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const headers: Array = [] + yield* plugin.add(AmazonBedrockPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("amazon-bedrock", "anthropic.claude-sonnet-4-5"), + package: "@ai-sdk/amazon-bedrock", + options: { + name: "amazon-bedrock", + bearerToken: "option-token", + fetch: async (_input: Parameters[0], init?: RequestInit) => { + headers.push(new Headers(init?.headers).get("Authorization")) + return new Response("{}") + }, + }, + }, + {}, + ) + yield* Effect.promise(() => bedrockFetch(result.sdk)("https://bedrock.example", { method: "POST" })) + expect(process.env.AWS_BEARER_TOKEN_BEDROCK).toBe("option-token") + expect(headers).toEqual(["Bearer option-token"]) + }), + ), + ) + + it.effect("prefers bearer token env over bearer token option", () => + withEnv({ AWS_BEARER_TOKEN_BEDROCK: "env-token" }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const headers: Array = [] + yield* plugin.add(AmazonBedrockPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("amazon-bedrock", "anthropic.claude-sonnet-4-5"), + package: "@ai-sdk/amazon-bedrock", + options: { + name: "amazon-bedrock", + bearerToken: "option-token", + fetch: async (_input: Parameters[0], init?: RequestInit) => { + headers.push(new Headers(init?.headers).get("Authorization")) + return new Response("{}") + }, + }, + }, + {}, + ) + yield* Effect.promise(() => bedrockFetch(result.sdk)("https://bedrock.example", { method: "POST" })) + expect(process.env.AWS_BEARER_TOKEN_BEDROCK).toBe("env-token") + expect(headers).toEqual(["Bearer env-token"]) + }), + ), + ) + + it.effect("uses SigV4 credential env when bearer token is absent", () => + withEnv( + { + AWS_ACCESS_KEY_ID: "test-access-key", + AWS_BEARER_TOKEN_BEDROCK: undefined, + AWS_REGION: "us-east-1", + AWS_SECRET_ACCESS_KEY: "test-secret-key", + AWS_SESSION_TOKEN: "test-session-token", + }, + () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const headers: Array = [] + yield* plugin.add(AmazonBedrockPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("amazon-bedrock", "anthropic.claude-sonnet-4-5"), + package: "@ai-sdk/amazon-bedrock", + options: { + name: "amazon-bedrock", + fetch: async (_input: Parameters[0], init?: RequestInit) => { + headers.push(new Headers(init?.headers).get("Authorization")) + return new Response("{}") + }, + }, + }, + {}, + ) + yield* Effect.promise(() => + bedrockFetch(result.sdk)("https://bedrock-runtime.us-east-1.amazonaws.com/model/test/invoke", { + body: "{}", + method: "POST", + }), + ) + expect(headers[0]?.startsWith("AWS4-HMAC-SHA256 ")).toBe(true) + }), + ), + ) + + it.effect("applies legacy cross-region inference prefixes", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(AmazonBedrockPlugin) + yield* plugin.trigger( + "aisdk.language", + { + model: model("amazon-bedrock", "anthropic.claude-sonnet-4-5"), + sdk: { languageModel: fakeSelectorSdk(calls).languageModel }, + options: {}, + }, + {}, + ) + yield* plugin.trigger( + "aisdk.language", + { + model: model("amazon-bedrock", "anthropic.claude-sonnet-4-5"), + sdk: { languageModel: fakeSelectorSdk(calls).languageModel }, + options: { region: "eu-west-1" }, + }, + {}, + ) + yield* plugin.trigger( + "aisdk.language", + { + model: model("amazon-bedrock", "global.anthropic.claude-sonnet-4-5"), + sdk: { languageModel: fakeSelectorSdk(calls).languageModel }, + options: { region: "eu-west-1" }, + }, + {}, + ) + yield* plugin.trigger( + "aisdk.language", + { + model: model("amazon-bedrock", "anthropic.claude-sonnet-4-5"), + sdk: { languageModel: fakeSelectorSdk(calls).languageModel }, + options: { region: "ap-northeast-1" }, + }, + {}, + ) + yield* plugin.trigger( + "aisdk.language", + { + model: model("amazon-bedrock", "anthropic.claude-sonnet-4-5"), + sdk: { languageModel: fakeSelectorSdk(calls).languageModel }, + options: { region: "ap-southeast-2" }, + }, + {}, + ) + expect(calls).toEqual([ + "languageModel:us.anthropic.claude-sonnet-4-5", + "languageModel:eu.anthropic.claude-sonnet-4-5", + "languageModel:global.anthropic.claude-sonnet-4-5", + "languageModel:jp.anthropic.claude-sonnet-4-5", + "languageModel:au.anthropic.claude-sonnet-4-5", + ]) + }), + ) + + it.effect("uses AWS_REGION for language prefixes when region option is absent", () => + withEnv({ AWS_REGION: "eu-west-1" }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(AmazonBedrockPlugin) + yield* plugin.trigger( + "aisdk.language", + { + model: model("amazon-bedrock", "anthropic.claude-sonnet-4-5"), + sdk: { languageModel: fakeSelectorSdk(calls).languageModel }, + options: {}, + }, + {}, + ) + expect(calls).toEqual(["languageModel:eu.anthropic.claude-sonnet-4-5"]) + }), + ), + ) + + it.effect("applies the full legacy cross-region prefix matrix", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + const cases = [ + { region: "us-east-1", modelID: "amazon.nova-micro-v1:0", expected: "us.amazon.nova-micro-v1:0" }, + { region: "us-east-1", modelID: "amazon.nova-lite-v1:0", expected: "us.amazon.nova-lite-v1:0" }, + { region: "us-east-1", modelID: "amazon.nova-pro-v1:0", expected: "us.amazon.nova-pro-v1:0" }, + { region: "us-east-1", modelID: "amazon.nova-premier-v1:0", expected: "us.amazon.nova-premier-v1:0" }, + { region: "us-east-1", modelID: "amazon.nova-2-lite-v1:0", expected: "us.amazon.nova-2-lite-v1:0" }, + { region: "us-east-1", modelID: "anthropic.claude-sonnet-4-5", expected: "us.anthropic.claude-sonnet-4-5" }, + { region: "us-east-1", modelID: "deepseek.r1-v1:0", expected: "us.deepseek.r1-v1:0" }, + { region: "us-gov-west-1", modelID: "anthropic.claude-sonnet-4-5", expected: "anthropic.claude-sonnet-4-5" }, + { region: "us-east-1", modelID: "cohere.command-r-plus-v1:0", expected: "cohere.command-r-plus-v1:0" }, + { region: "eu-west-1", modelID: "anthropic.claude-sonnet-4-5", expected: "eu.anthropic.claude-sonnet-4-5" }, + { region: "eu-west-2", modelID: "amazon.nova-lite-v1:0", expected: "eu.amazon.nova-lite-v1:0" }, + { region: "eu-west-3", modelID: "amazon.nova-micro-v1:0", expected: "eu.amazon.nova-micro-v1:0" }, + { + region: "eu-north-1", + modelID: "meta.llama3-70b-instruct-v1:0", + expected: "eu.meta.llama3-70b-instruct-v1:0", + }, + { region: "eu-central-1", modelID: "mistral.pixtral-large-v1:0", expected: "eu.mistral.pixtral-large-v1:0" }, + { region: "eu-south-1", modelID: "anthropic.claude-sonnet-4-5", expected: "eu.anthropic.claude-sonnet-4-5" }, + { region: "eu-south-2", modelID: "anthropic.claude-sonnet-4-5", expected: "eu.anthropic.claude-sonnet-4-5" }, + { region: "eu-central-2", modelID: "anthropic.claude-sonnet-4-5", expected: "anthropic.claude-sonnet-4-5" }, + { region: "eu-west-1", modelID: "cohere.command-r-plus-v1:0", expected: "cohere.command-r-plus-v1:0" }, + { + region: "ap-southeast-2", + modelID: "anthropic.claude-sonnet-4-5", + expected: "au.anthropic.claude-sonnet-4-5", + }, + { + region: "ap-southeast-4", + modelID: "anthropic.claude-haiku-v1:0", + expected: "au.anthropic.claude-haiku-v1:0", + }, + { region: "ap-southeast-2", modelID: "anthropic.claude-opus-4", expected: "apac.anthropic.claude-opus-4" }, + { + region: "ap-northeast-1", + modelID: "anthropic.claude-sonnet-4-5", + expected: "jp.anthropic.claude-sonnet-4-5", + }, + { region: "ap-northeast-1", modelID: "amazon.nova-pro-v1:0", expected: "jp.amazon.nova-pro-v1:0" }, + { region: "ap-south-1", modelID: "anthropic.claude-sonnet-4-5", expected: "apac.anthropic.claude-sonnet-4-5" }, + { region: "ap-south-1", modelID: "amazon.nova-lite-v1:0", expected: "apac.amazon.nova-lite-v1:0" }, + { region: "ca-central-1", modelID: "anthropic.claude-sonnet-4-5", expected: "anthropic.claude-sonnet-4-5" }, + { + region: "us-east-1", + modelID: "global.anthropic.claude-sonnet-4-5", + expected: "global.anthropic.claude-sonnet-4-5", + }, + { region: "us-east-1", modelID: "us.anthropic.claude-sonnet-4-5", expected: "us.anthropic.claude-sonnet-4-5" }, + { region: "eu-west-1", modelID: "eu.anthropic.claude-sonnet-4-5", expected: "eu.anthropic.claude-sonnet-4-5" }, + { + region: "ap-northeast-1", + modelID: "jp.anthropic.claude-sonnet-4-5", + expected: "jp.anthropic.claude-sonnet-4-5", + }, + { + region: "ap-south-1", + modelID: "apac.anthropic.claude-sonnet-4-5", + expected: "apac.anthropic.claude-sonnet-4-5", + }, + { + region: "ap-southeast-2", + modelID: "au.anthropic.claude-sonnet-4-5", + expected: "au.anthropic.claude-sonnet-4-5", + }, + ] + yield* plugin.add(AmazonBedrockPlugin) + for (const item of cases) { + yield* plugin.trigger( + "aisdk.language", + { + model: model("amazon-bedrock", item.modelID), + sdk: { languageModel: fakeSelectorSdk(calls).languageModel }, + options: { region: item.region }, + }, + {}, + ) + } + expect(calls).toEqual(cases.map((item) => `languageModel:${item.expected}`)) + }), + ) + + it.effect("ignores non-Bedrock providers for language selection", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(AmazonBedrockPlugin) + const result = yield* plugin.trigger( + "aisdk.language", + { + model: model("openai", "anthropic.claude-sonnet-4-5"), + sdk: { languageModel: fakeSelectorSdk(calls).languageModel }, + options: { region: "eu-west-1" }, + }, + {}, + ) + expect(calls).toEqual([]) + expect(result.language).toBeUndefined() + }), + ) +}) diff --git a/packages/core/test/plugin/provider-anthropic.test.ts b/packages/core/test/plugin/provider-anthropic.test.ts new file mode 100644 index 00000000000..bbea4a37215 --- /dev/null +++ b/packages/core/test/plugin/provider-anthropic.test.ts @@ -0,0 +1,91 @@ +import { describe, expect } from "bun:test" +import { Effect } from "effect" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { AnthropicPlugin } from "@opencode-ai/core/plugin/provider/anthropic" +import { it, model, provider } from "./provider-helper" + +describe("AnthropicPlugin", () => { + it.effect("applies legacy beta headers", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AnthropicPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("anthropic", { + options: { headers: { Existing: "1" }, body: {}, aisdk: { provider: {}, request: {} } }, + }), + cancel: false, + }, + ) + expect(result.provider.options.headers["anthropic-beta"]).toBe( + "interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14", + ) + expect(result.provider.options.headers.Existing).toBe("1") + }), + ) + + it.effect("ignores non-Anthropic providers", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AnthropicPlugin) + const result = yield* plugin.trigger("provider.update", {}, { provider: provider("openai"), cancel: false }) + expect(result.provider.options.headers["anthropic-beta"]).toBeUndefined() + }), + ) + + it.effect("creates Anthropic SDKs with the model provider ID as the SDK name", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const providers: string[] = [] + yield* plugin.add(AnthropicPlugin) + yield* plugin.add({ + id: PluginV2.ID.make("anthropic-sdk-inspector"), + effect: Effect.succeed({ + "aisdk.sdk": (evt) => + Effect.sync(() => { + providers.push(evt.sdk.languageModel("claude-sonnet-4-5").provider) + }), + }), + }) + yield* plugin.trigger( + "aisdk.sdk", + { + model: model("custom-anthropic", "claude-sonnet-4-5"), + package: "@ai-sdk/anthropic", + options: { name: "custom-anthropic", apiKey: "test" }, + }, + {}, + ) + expect(providers).toEqual(["custom-anthropic"]) + }), + ) + + it.effect("uses the Anthropic provider ID as the SDK name for the bundled Anthropic provider", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const providers: string[] = [] + yield* plugin.add(AnthropicPlugin) + yield* plugin.add({ + id: PluginV2.ID.make("anthropic-sdk-inspector"), + effect: Effect.succeed({ + "aisdk.sdk": (evt) => + Effect.sync(() => { + providers.push(evt.sdk.languageModel("claude-sonnet-4-5").provider) + }), + }), + }) + yield* plugin.trigger( + "aisdk.sdk", + { + model: model("anthropic", "claude-sonnet-4-5"), + package: "@ai-sdk/anthropic", + options: { name: "anthropic", apiKey: "test" }, + }, + {}, + ) + expect(providers).toEqual(["anthropic"]) + }), + ) +}) diff --git a/packages/core/test/plugin/provider-azure-cognitive-services.test.ts b/packages/core/test/plugin/provider-azure-cognitive-services.test.ts new file mode 100644 index 00000000000..b835cbeeff0 --- /dev/null +++ b/packages/core/test/plugin/provider-azure-cognitive-services.test.ts @@ -0,0 +1,127 @@ +import { describe, expect } from "bun:test" +import { Effect } from "effect" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { AzureCognitiveServicesPlugin } from "@opencode-ai/core/plugin/provider/azure" +import { fakeSelectorSdk, it, model, provider, withEnv } from "./provider-helper" + +describe("AzureCognitiveServicesPlugin", () => { + it.effect("maps the resource env var to the Azure SDK baseURL", () => + withEnv({ AZURE_COGNITIVE_SERVICES_RESOURCE_NAME: "cognitive" }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AzureCognitiveServicesPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { provider: provider("azure-cognitive-services"), cancel: false }, + ) + expect(result.provider.endpoint).toEqual({ + type: "aisdk", + package: "test-provider", + }) + expect(result.provider.options.aisdk.provider.baseURL).toBe( + "https://cognitive.cognitiveservices.azure.com/openai", + ) + expect(result.provider.options.aisdk.provider.resourceName).toBeUndefined() + }), + ), + ) + + it.effect("leaves baseURL unset without resource env and ignores other providers", () => + withEnv({ AZURE_COGNITIVE_SERVICES_RESOURCE_NAME: undefined }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AzureCognitiveServicesPlugin) + const azure = yield* plugin.trigger( + "provider.update", + {}, + { provider: provider("azure-cognitive-services"), cancel: false }, + ) + const other = yield* plugin.trigger("provider.update", {}, { provider: provider("openai"), cancel: false }) + expect(azure.provider.options.aisdk.provider.baseURL).toBeUndefined() + expect(azure.provider.endpoint).toEqual({ type: "aisdk", package: "test-provider" }) + expect(other.provider.options.aisdk.provider.baseURL).toBeUndefined() + expect(other.provider.endpoint).toEqual({ type: "aisdk", package: "test-provider" }) + }), + ), + ) + + it.effect("selects chat only for completion URLs", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(AzureCognitiveServicesPlugin) + yield* plugin.trigger( + "aisdk.language", + { + model: model("azure-cognitive-services", "deployment"), + sdk: fakeSelectorSdk(calls), + options: { useCompletionUrls: true }, + }, + {}, + ) + expect(calls).toEqual(["chat:deployment"]) + }), + ) + + it.effect("uses the legacy Azure selector order and provider guard", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(AzureCognitiveServicesPlugin) + yield* plugin.trigger( + "aisdk.language", + { model: model("azure-cognitive-services", "deployment"), sdk: fakeSelectorSdk(calls), options: {} }, + {}, + ) + const ignored = yield* plugin.trigger( + "aisdk.language", + { model: model("openai", "deployment"), sdk: fakeSelectorSdk(calls), options: {} }, + {}, + ) + expect(calls).toEqual(["responses:deployment"]) + expect(ignored.language).toBeUndefined() + }), + ) + + it.effect("falls back from responses to messages, chat, then languageModel", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + const sdk = fakeSelectorSdk(calls) + yield* plugin.add(AzureCognitiveServicesPlugin) + yield* plugin.trigger( + "aisdk.language", + { + model: model("azure-cognitive-services", "messages-deployment"), + sdk: { messages: sdk.messages, chat: sdk.chat, languageModel: sdk.languageModel }, + options: {}, + }, + {}, + ) + yield* plugin.trigger( + "aisdk.language", + { + model: model("azure-cognitive-services", "chat-deployment"), + sdk: { chat: sdk.chat, languageModel: sdk.languageModel }, + options: {}, + }, + {}, + ) + yield* plugin.trigger( + "aisdk.language", + { + model: model("azure-cognitive-services", "language-deployment"), + sdk: { languageModel: sdk.languageModel }, + options: {}, + }, + {}, + ) + expect(calls).toEqual([ + "messages:messages-deployment", + "chat:chat-deployment", + "languageModel:language-deployment", + ]) + }), + ) +}) diff --git a/packages/core/test/plugin/provider-azure.test.ts b/packages/core/test/plugin/provider-azure.test.ts new file mode 100644 index 00000000000..5121b1eec02 --- /dev/null +++ b/packages/core/test/plugin/provider-azure.test.ts @@ -0,0 +1,245 @@ +import { describe, expect } from "bun:test" +import { Effect, Layer } from "effect" +import { AuthV2 } from "@opencode-ai/core/auth" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { AuthPlugin } from "@opencode-ai/core/plugin/auth" +import { AzurePlugin } from "@opencode-ai/core/plugin/provider/azure" +import { testEffect } from "../lib/effect" +import { fakeSelectorSdk, it, model, npmLayer, provider, withEnv } from "./provider-helper" + +const itWithAuth = testEffect(Layer.mergeAll(PluginV2.defaultLayer, AuthV2.defaultLayer, npmLayer)) + +describe("AzurePlugin", () => { + it.effect("resolves resourceName from env", () => + withEnv({ AZURE_RESOURCE_NAME: "from-env" }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AzurePlugin) + const result = yield* plugin.trigger("provider.update", {}, { provider: provider("azure"), cancel: false }) + expect(result.provider.options.aisdk.provider.resourceName).toBe("from-env") + }), + ), + ) + + it.effect("keeps explicit resourceName over env and ignores other providers", () => + withEnv({ AZURE_RESOURCE_NAME: "from-env" }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AzurePlugin) + const azure = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("azure", { + options: { headers: {}, body: {}, aisdk: { provider: { resourceName: "from-config" }, request: {} } }, + }), + cancel: false, + }, + ) + const other = yield* plugin.trigger("provider.update", {}, { provider: provider("openai"), cancel: false }) + expect(azure.provider.options.aisdk.provider.resourceName).toBe("from-config") + expect(other.provider.options.aisdk.provider.resourceName).toBeUndefined() + }), + ), + ) + + itWithAuth.effect("prefers auth resourceName over env", () => + withEnv( + { + AZURE_RESOURCE_NAME: "from-env", + }, + () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const auth = yield* AuthV2.Service + yield* auth.create({ + serviceID: AuthV2.ServiceID.make("azure"), + credential: new AuthV2.ApiKeyCredential({ + type: "api", + key: "key", + metadata: { resourceName: "from-auth" }, + }), + active: true, + }) + yield* plugin.add({ + ...AuthPlugin, + effect: AuthPlugin.effect.pipe(Effect.provideService(AuthV2.Service, auth)), + }) + yield* plugin.add(AzurePlugin) + const result = yield* plugin.trigger("provider.update", {}, { provider: provider("azure"), cancel: false }) + expect(result.provider.options.aisdk.provider.resourceName).toBe("from-auth") + }), + ), + ) + + it.effect("falls back to env when configured resourceName is blank", () => + withEnv({ AZURE_RESOURCE_NAME: "from-env" }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AzurePlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("azure", { + options: { headers: {}, body: {}, aisdk: { provider: { resourceName: "" }, request: {} } }, + }), + cancel: false, + }, + ) + expect(result.provider.options.aisdk.provider.resourceName).toBe("from-env") + }), + ), + ) + + it.effect("falls back to env when configured resourceName is whitespace", () => + withEnv({ AZURE_RESOURCE_NAME: "from-env" }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AzurePlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("azure", { + options: { headers: {}, body: {}, aisdk: { provider: { resourceName: " " }, request: {} } }, + }), + cancel: false, + }, + ) + expect(result.provider.options.aisdk.provider.resourceName).toBe("from-env") + }), + ), + ) + + it.effect("allows configured baseURL without resourceName", () => + withEnv({ AZURE_RESOURCE_NAME: undefined }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AzurePlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("azure", "deployment"), + package: "@ai-sdk/azure", + options: { name: "azure", baseURL: "https://proxy.example.com/openai" }, + }, + {}, + ) + expect(result.sdk).toBeDefined() + }), + ), + ) + + it.effect("rejects missing resourceName when baseURL is not configured", () => + withEnv({ AZURE_RESOURCE_NAME: undefined }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(AzurePlugin) + const exit = yield* plugin + .trigger( + "aisdk.sdk", + { model: model("azure", "deployment"), package: "@ai-sdk/azure", options: { name: "azure" } }, + {}, + ) + .pipe(Effect.exit) + expect(exit._tag).toBe("Failure") + }), + ), + ) + + it.effect("selects chat only for completion URLs", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(AzurePlugin) + yield* plugin.trigger( + "aisdk.language", + { model: model("azure", "deployment"), sdk: fakeSelectorSdk(calls), options: { useCompletionUrls: true } }, + {}, + ) + expect(calls).toEqual(["chat:deployment"]) + }), + ) + + it.effect("selects chat from per-call useCompletionUrls", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(AzurePlugin) + yield* plugin.trigger( + "aisdk.language", + { model: model("azure", "deployment"), sdk: fakeSelectorSdk(calls), options: { useCompletionUrls: true } }, + {}, + ) + expect(calls).toEqual(["chat:deployment"]) + }), + ) + + it.effect("ignores model useCompletionUrls when per-call option is unset", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(AzurePlugin) + yield* plugin.trigger( + "aisdk.language", + { + model: model("azure", "deployment", { + options: { headers: {}, body: {}, aisdk: { provider: {}, request: { useCompletionUrls: true } } }, + }), + sdk: fakeSelectorSdk(calls), + options: {}, + }, + {}, + ) + expect(calls).toEqual(["responses:deployment"]) + }), + ) + + it.effect("uses the legacy Azure selector order and provider guard", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(AzurePlugin) + yield* plugin.trigger( + "aisdk.language", + { model: model("azure", "deployment"), sdk: fakeSelectorSdk(calls), options: {} }, + {}, + ) + const ignored = yield* plugin.trigger( + "aisdk.language", + { model: model("openai", "deployment"), sdk: fakeSelectorSdk(calls), options: {} }, + {}, + ) + expect(calls).toEqual(["responses:deployment"]) + expect(ignored.language).toBeUndefined() + }), + ) + + it.effect("falls back through the legacy Azure selector order", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + const make = (method: string) => (id: string) => { + calls.push(`${method}:${id}`) + return { modelId: id, provider: method, specificationVersion: "v3" } + } + yield* plugin.add(AzurePlugin) + yield* plugin.trigger( + "aisdk.language", + { + model: model("azure", "messages-deployment"), + sdk: { messages: make("messages"), chat: make("chat"), languageModel: make("languageModel") }, + options: {}, + }, + {}, + ) + yield* plugin.trigger( + "aisdk.language", + { model: model("azure", "language-deployment"), sdk: { languageModel: make("languageModel") }, options: {} }, + {}, + ) + expect(calls).toEqual(["messages:messages-deployment", "languageModel:language-deployment"]) + }), + ) +}) diff --git a/packages/core/test/plugin/provider-cerebras.test.ts b/packages/core/test/plugin/provider-cerebras.test.ts new file mode 100644 index 00000000000..7270d5367ae --- /dev/null +++ b/packages/core/test/plugin/provider-cerebras.test.ts @@ -0,0 +1,102 @@ +import { describe, expect, mock } from "bun:test" +import { Effect } from "effect" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { CerebrasPlugin } from "@opencode-ai/core/plugin/provider/cerebras" +import { it, model, provider } from "./provider-helper" + +const cerebrasOptions: Record[] = [] + +void mock.module("@ai-sdk/cerebras", () => ({ + createCerebras: (options: Record) => { + const snapshot = { ...options } + cerebrasOptions.push(snapshot) + return { + languageModel: (modelID: string) => ({ modelID, provider: snapshot.name, specificationVersion: "v3" }), + } + }, +})) + +describe("CerebrasPlugin", () => { + it.effect("applies the legacy integration header", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(CerebrasPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("cerebras", { + options: { headers: { Existing: "1" }, body: {}, aisdk: { provider: {}, request: {} } }, + }), + cancel: false, + }, + ) + expect(result.provider.options.headers).toEqual({ Existing: "1", "X-Cerebras-3rd-Party-Integration": "opencode" }) + }), + ) + + it.effect("ignores non-Cerebras providers", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(CerebrasPlugin) + const result = yield* plugin.trigger("provider.update", {}, { provider: provider("groq"), cancel: false }) + expect(result.provider.options.headers).toEqual({}) + }), + ) + + it.effect("creates a bundled Cerebras SDK with the model provider ID as the SDK name", () => + Effect.gen(function* () { + cerebrasOptions.length = 0 + const plugin = yield* PluginV2.Service + yield* plugin.add(CerebrasPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("custom-cerebras", "llama-4-scout-17b-16e-instruct"), + package: "@ai-sdk/cerebras", + options: { name: "custom-cerebras", apiKey: "test" }, + }, + {}, + ) + expect(cerebrasOptions).toEqual([{ name: "custom-cerebras", apiKey: "test" }]) + expect(result.sdk.languageModel("llama-4-scout-17b-16e-instruct").provider).toBe("custom-cerebras") + }), + ) + + it.effect("preserves an explicit bundled Cerebras SDK name option", () => + Effect.gen(function* () { + cerebrasOptions.length = 0 + const plugin = yield* PluginV2.Service + yield* plugin.add(CerebrasPlugin) + yield* plugin.trigger( + "aisdk.sdk", + { + model: model("custom-cerebras", "llama-4-scout-17b-16e-instruct"), + package: "@ai-sdk/cerebras", + options: { name: "configured-cerebras", apiKey: "test" }, + }, + {}, + ) + expect(cerebrasOptions).toEqual([{ name: "configured-cerebras", apiKey: "test" }]) + }), + ) + + it.effect("ignores non-Cerebras SDK packages", () => + Effect.gen(function* () { + cerebrasOptions.length = 0 + const plugin = yield* PluginV2.Service + yield* plugin.add(CerebrasPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("custom-cerebras", "llama-4-scout-17b-16e-instruct"), + package: "@ai-sdk/groq", + options: { name: "custom-cerebras", apiKey: "test" }, + }, + {}, + ) + expect(cerebrasOptions).toEqual([]) + expect(result.sdk).toBeUndefined() + }), + ) +}) diff --git a/packages/core/test/plugin/provider-cloudflare-ai-gateway.test.ts b/packages/core/test/plugin/provider-cloudflare-ai-gateway.test.ts new file mode 100644 index 00000000000..72ad5da33f1 --- /dev/null +++ b/packages/core/test/plugin/provider-cloudflare-ai-gateway.test.ts @@ -0,0 +1,384 @@ +import { describe, expect, mock } from "bun:test" +import { Effect } from "effect" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { CloudflareAIGatewayPlugin } from "@opencode-ai/core/plugin/provider/cloudflare-ai-gateway" +import { it, model, withEnv } from "./provider-helper" + +const aiGatewayCalls: Record[] = [] +const unifiedCalls: string[] = [] +const gatewayModelCalls: unknown[] = [] + +function captureAiGatewayOptions(options: Record) { + const nested = + options.options && typeof options.options === "object" ? (options.options as Record) : undefined + return { + ...options, + ...(nested + ? { + options: { + ...nested, + headers: + nested.headers && typeof nested.headers === "object" + ? { ...(nested.headers as Record) } + : nested.headers, + }, + } + : {}), + } +} + +function resetCalls() { + aiGatewayCalls.length = 0 + unifiedCalls.length = 0 + gatewayModelCalls.length = 0 +} + +function cloudflareEnv(overrides: Record = {}) { + return { + CLOUDFLARE_ACCOUNT_ID: "env-account", + CLOUDFLARE_GATEWAY_ID: "env-gateway", + CLOUDFLARE_API_TOKEN: "env-token", + CF_AIG_TOKEN: undefined, + ...overrides, + } +} + +mock.module("ai-gateway-provider", () => ({ + createAiGateway(options: Record) { + aiGatewayCalls.push(captureAiGatewayOptions(options)) + return (input: unknown) => { + gatewayModelCalls.push(input) + return { + modelId: input, + provider: "cloudflare-ai-gateway", + specificationVersion: "v3", + } + } + }, +})) + +mock.module("ai-gateway-provider/providers/unified", () => ({ + createUnified() { + return (modelID: string) => { + unifiedCalls.push(modelID) + return { unifiedModelID: modelID } + } + }, +})) + +describe("CloudflareAIGatewayPlugin", () => { + it.effect("requires account, gateway, and token before creating the unified SDK", () => + withEnv( + { + CLOUDFLARE_ACCOUNT_ID: "acct", + CLOUDFLARE_GATEWAY_ID: "gateway", + CLOUDFLARE_API_TOKEN: "token", + CF_AIG_TOKEN: undefined, + }, + () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(CloudflareAIGatewayPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("cloudflare-ai-gateway", "openai/gpt-5"), + package: "ai-gateway-provider", + options: { name: "cloudflare-ai-gateway" }, + }, + {}, + ) + expect(result.sdk.languageModel("openai/gpt-5")).toBeDefined() + }), + ), + ) + + it.effect("passes legacy metadata, cache, log, and User-Agent values under the AI Gateway options key", () => + withEnv(cloudflareEnv(), () => + Effect.gen(function* () { + resetCalls() + const plugin = yield* PluginV2.Service + yield* plugin.add(CloudflareAIGatewayPlugin) + + yield* plugin.trigger( + "aisdk.sdk", + { + model: model("cloudflare-ai-gateway", "openai/gpt-5"), + package: "ai-gateway-provider", + options: { + name: "cloudflare-ai-gateway", + metadata: { invoked_by: "test", project: "opencode" }, + cacheTtl: 300, + cacheKey: "cache-key", + skipCache: true, + collectLog: false, + }, + }, + {}, + ) + + expect(aiGatewayCalls).toHaveLength(1) + expect(aiGatewayCalls[0]).toEqual({ + accountId: "env-account", + gateway: "env-gateway", + apiKey: "env-token", + options: { + metadata: { invoked_by: "test", project: "opencode" }, + cacheTtl: 300, + cacheKey: "cache-key", + skipCache: true, + collectLog: false, + headers: { + "User-Agent": expect.stringContaining("opencode/"), + }, + }, + }) + }), + ), + ) + + it.effect("parses legacy cf-aig-metadata header when metadata option is absent", () => + withEnv(cloudflareEnv(), () => + Effect.gen(function* () { + resetCalls() + const plugin = yield* PluginV2.Service + yield* plugin.add(CloudflareAIGatewayPlugin) + + yield* plugin.trigger( + "aisdk.sdk", + { + model: model("cloudflare-ai-gateway", "openai/gpt-5"), + package: "ai-gateway-provider", + options: { + name: "cloudflare-ai-gateway", + headers: { + "cf-aig-metadata": JSON.stringify({ invoked_by: "header", project: "opencode" }), + }, + }, + }, + {}, + ) + + expect(aiGatewayCalls[0]?.options).toMatchObject({ + metadata: { invoked_by: "header", project: "opencode" }, + }) + }), + ), + ) + + it.effect("prefers Cloudflare env values over auth/config-derived options", () => + withEnv(cloudflareEnv(), () => + Effect.gen(function* () { + resetCalls() + const plugin = yield* PluginV2.Service + yield* plugin.add(CloudflareAIGatewayPlugin) + + yield* plugin.trigger( + "aisdk.sdk", + { + model: model("cloudflare-ai-gateway", "openai/gpt-5"), + package: "ai-gateway-provider", + options: { + name: "cloudflare-ai-gateway", + accountId: "auth-account", + gateway: "auth-gateway", + apiKey: "auth-token", + }, + }, + {}, + ) + + expect(aiGatewayCalls[0]).toMatchObject({ + accountId: "env-account", + gateway: "env-gateway", + apiKey: "env-token", + }) + }), + ), + ) + + it.effect("accepts gatewayId metadata copied from auth into provider options", () => + withEnv( + cloudflareEnv({ + CLOUDFLARE_ACCOUNT_ID: undefined, + CLOUDFLARE_GATEWAY_ID: undefined, + CLOUDFLARE_API_TOKEN: undefined, + }), + () => + Effect.gen(function* () { + resetCalls() + const plugin = yield* PluginV2.Service + yield* plugin.add(CloudflareAIGatewayPlugin) + + yield* plugin.trigger( + "aisdk.sdk", + { + model: model("cloudflare-ai-gateway", "openai/gpt-5"), + package: "ai-gateway-provider", + options: { + name: "cloudflare-ai-gateway", + accountId: "auth-account", + gatewayId: "auth-gateway", + apiKey: "auth-token", + }, + }, + {}, + ) + + expect(aiGatewayCalls[0]).toMatchObject({ + accountId: "auth-account", + gateway: "auth-gateway", + apiKey: "auth-token", + }) + }), + ), + ) + + it.effect("falls back to CF_AIG_TOKEN when CLOUDFLARE_API_TOKEN is unset", () => + withEnv(cloudflareEnv({ CLOUDFLARE_API_TOKEN: undefined, CF_AIG_TOKEN: "cf-aig-token" }), () => + Effect.gen(function* () { + resetCalls() + const plugin = yield* PluginV2.Service + yield* plugin.add(CloudflareAIGatewayPlugin) + + yield* plugin.trigger( + "aisdk.sdk", + { + model: model("cloudflare-ai-gateway", "openai/gpt-5"), + package: "ai-gateway-provider", + options: { name: "cloudflare-ai-gateway" }, + }, + {}, + ) + + expect(aiGatewayCalls[0]).toMatchObject({ apiKey: "cf-aig-token" }) + }), + ), + ) + + it.effect("does not create an SDK when account and gateway IDs are missing", () => + withEnv(cloudflareEnv({ CLOUDFLARE_ACCOUNT_ID: undefined, CLOUDFLARE_GATEWAY_ID: undefined }), () => + Effect.gen(function* () { + resetCalls() + const plugin = yield* PluginV2.Service + yield* plugin.add(CloudflareAIGatewayPlugin) + + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("cloudflare-ai-gateway", "openai/gpt-5"), + package: "ai-gateway-provider", + options: { name: "cloudflare-ai-gateway" }, + }, + {}, + ) + + expect(result.sdk).toBeUndefined() + expect(aiGatewayCalls).toHaveLength(0) + }), + ), + ) + + it.effect("does not create an SDK when the token is missing", () => + withEnv(cloudflareEnv({ CLOUDFLARE_API_TOKEN: undefined, CF_AIG_TOKEN: undefined }), () => + Effect.gen(function* () { + resetCalls() + const plugin = yield* PluginV2.Service + yield* plugin.add(CloudflareAIGatewayPlugin) + + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("cloudflare-ai-gateway", "openai/gpt-5"), + package: "ai-gateway-provider", + options: { name: "cloudflare-ai-gateway" }, + }, + {}, + ) + + expect(result.sdk).toBeUndefined() + expect(aiGatewayCalls).toHaveLength(0) + }), + ), + ) + + it.effect("does not replace a configured baseURL with the Cloudflare AI Gateway SDK", () => + withEnv( + cloudflareEnv({ + CLOUDFLARE_ACCOUNT_ID: undefined, + CLOUDFLARE_GATEWAY_ID: undefined, + CLOUDFLARE_API_TOKEN: undefined, + }), + () => + Effect.gen(function* () { + resetCalls() + const plugin = yield* PluginV2.Service + yield* plugin.add(CloudflareAIGatewayPlugin) + + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("cloudflare-ai-gateway", "openai/gpt-5"), + package: "ai-gateway-provider", + options: { name: "cloudflare-ai-gateway", baseURL: "https://proxy.example/v1" }, + }, + {}, + ) + + expect(result.sdk).toBeUndefined() + expect(aiGatewayCalls).toHaveLength(0) + }), + ), + ) + + it.effect("maps provider/model IDs through the unified Cloudflare provider unchanged", () => + withEnv(cloudflareEnv(), () => + Effect.gen(function* () { + resetCalls() + const plugin = yield* PluginV2.Service + yield* plugin.add(CloudflareAIGatewayPlugin) + + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("cloudflare-ai-gateway", "anthropic/claude-sonnet-4-5"), + package: "ai-gateway-provider", + options: { name: "cloudflare-ai-gateway" }, + }, + {}, + ) + + expect(result.sdk.languageModel("anthropic/claude-sonnet-4-5")).toEqual({ + modelId: { unifiedModelID: "anthropic/claude-sonnet-4-5" }, + provider: "cloudflare-ai-gateway", + specificationVersion: "v3", + }) + expect(unifiedCalls).toEqual(["anthropic/claude-sonnet-4-5"]) + expect(gatewayModelCalls).toEqual([{ unifiedModelID: "anthropic/claude-sonnet-4-5" }]) + }), + ), + ) + + it.effect("ignores non Cloudflare AI Gateway packages", () => + withEnv(cloudflareEnv(), () => + Effect.gen(function* () { + resetCalls() + const plugin = yield* PluginV2.Service + yield* plugin.add(CloudflareAIGatewayPlugin) + + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("cloudflare-ai-gateway", "openai/gpt-5"), + package: "@ai-sdk/openai-compatible", + options: { name: "cloudflare-ai-gateway" }, + }, + {}, + ) + + expect(result.sdk).toBeUndefined() + expect(aiGatewayCalls).toHaveLength(0) + }), + ), + ) +}) diff --git a/packages/core/test/plugin/provider-cloudflare-workers-ai.test.ts b/packages/core/test/plugin/provider-cloudflare-workers-ai.test.ts new file mode 100644 index 00000000000..10aba171c33 --- /dev/null +++ b/packages/core/test/plugin/provider-cloudflare-workers-ai.test.ts @@ -0,0 +1,267 @@ +import { describe, expect } from "bun:test" +import { Effect, Layer } from "effect" +import { AuthV2 } from "@opencode-ai/core/auth" +import { ModelV2 } from "@opencode-ai/core/model" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { AuthPlugin } from "@opencode-ai/core/plugin/auth" +import { CloudflareWorkersAIPlugin } from "@opencode-ai/core/plugin/provider/cloudflare-workers-ai" +import { testEffect } from "../lib/effect" +import { fakeSelectorSdk, it, model, npmLayer, provider, withEnv } from "./provider-helper" + +const itWithAuth = testEffect(Layer.mergeAll(PluginV2.defaultLayer, AuthV2.defaultLayer, npmLayer)) + +function cloudflareLanguage(sdk: unknown, modelID = "@cf/model") { + return (sdk as { languageModel: (id: string) => { config: CloudflareConfig; provider: string } }).languageModel( + modelID, + ) +} + +type CloudflareConfig = { + url: (input: { path: string; modelId: string }) => string + headers: () => Record | Promise> +} + +function cloudflareURL(sdk: unknown, modelID = "@cf/model") { + return cloudflareLanguage(sdk, modelID).config.url({ path: "/chat/completions", modelId: modelID }) +} + +function cloudflareHeaders(sdk: unknown, modelID = "@cf/model") { + return cloudflareLanguage(sdk, modelID).config.headers() +} + +describe("CloudflareWorkersAIPlugin", () => { + it.effect("maps account ID to endpoint URL and creates an OpenAI-compatible SDK", () => + withEnv({ CLOUDFLARE_ACCOUNT_ID: "acct", CLOUDFLARE_API_KEY: "key" }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(CloudflareWorkersAIPlugin) + const updated = yield* plugin.trigger( + "provider.update", + {}, + { provider: provider("cloudflare-workers-ai"), cancel: false }, + ) + const sdk = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("cloudflare-workers-ai", "@cf/model", { endpoint: updated.provider.endpoint }), + package: "@ai-sdk/openai-compatible", + options: { name: "cloudflare-workers-ai", headers: { custom: "header" } }, + }, + {}, + ) + expect(updated.provider.endpoint).toEqual({ + type: "aisdk", + package: "test-provider", + url: "https://api.cloudflare.com/client/v4/accounts/acct/ai/v1", + }) + expect(sdk.sdk).toBeDefined() + }), + ), + ) + + it.effect("preserves a configured endpoint URL instead of deriving one from account ID", () => + withEnv({ CLOUDFLARE_ACCOUNT_ID: "acct" }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(CloudflareWorkersAIPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("cloudflare-workers-ai", { + endpoint: { type: "aisdk", package: "test-provider", url: "https://proxy.example/v1" }, + }), + cancel: false, + }, + ) + expect(result.provider.endpoint).toEqual({ + type: "aisdk", + package: "test-provider", + url: "https://proxy.example/v1", + }) + }), + ), + ) + + it.effect("allows a configured baseURL without account ID", () => + withEnv({ CLOUDFLARE_ACCOUNT_ID: undefined, CLOUDFLARE_API_KEY: "key" }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(CloudflareWorkersAIPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("cloudflare-workers-ai", "@cf/model", { + endpoint: { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://proxy.example/v1" }, + }), + package: "@ai-sdk/openai-compatible", + options: { name: "cloudflare-workers-ai", baseURL: "https://proxy.example/v1" }, + }, + {}, + ) + expect(cloudflareURL(result.sdk)).toBe("https://proxy.example/v1/chat/completions") + }), + ), + ) + + itWithAuth.effect("falls back to auth account metadata when account env is absent", () => + withEnv( + { + CLOUDFLARE_ACCOUNT_ID: undefined, + CLOUDFLARE_API_KEY: undefined, + }, + () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const auth = yield* AuthV2.Service + yield* auth.create({ + serviceID: AuthV2.ServiceID.make("cloudflare-workers-ai"), + credential: new AuthV2.ApiKeyCredential({ + type: "api", + key: "auth-key", + metadata: { accountId: "auth-acct" }, + }), + active: true, + }) + yield* plugin.add({ + ...AuthPlugin, + effect: AuthPlugin.effect.pipe(Effect.provideService(AuthV2.Service, auth)), + }) + yield* plugin.add(CloudflareWorkersAIPlugin) + const updated = yield* plugin.trigger( + "provider.update", + {}, + { provider: provider("cloudflare-workers-ai"), cancel: false }, + ) + expect(updated.provider.endpoint).toEqual({ + type: "aisdk", + package: "test-provider", + url: "https://api.cloudflare.com/client/v4/accounts/auth-acct/ai/v1", + }) + }), + ), + ) + + it.effect("uses env account ID over configured account ID", () => + withEnv({ CLOUDFLARE_ACCOUNT_ID: "env-acct" }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(CloudflareWorkersAIPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("cloudflare-workers-ai", { + options: { headers: {}, body: {}, aisdk: { provider: { accountId: "configured-acct" }, request: {} } }, + }), + cancel: false, + }, + ) + expect(result.provider.endpoint).toEqual({ + type: "aisdk", + package: "test-provider", + url: "https://api.cloudflare.com/client/v4/accounts/env-acct/ai/v1", + }) + }), + ), + ) + + it.effect("uses env API key over auth or configured API key and keeps the Cloudflare User-Agent", () => + withEnv({ CLOUDFLARE_ACCOUNT_ID: "acct", CLOUDFLARE_API_KEY: "env-key" }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(CloudflareWorkersAIPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("cloudflare-workers-ai", "@cf/model", { + endpoint: { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://proxy.example/v1" }, + }), + package: "@ai-sdk/openai-compatible", + options: { + name: "cloudflare-workers-ai", + apiKey: "auth-key", + baseURL: "https://proxy.example/v1", + headers: { custom: "header" }, + }, + }, + {}, + ) + const headers = yield* Effect.promise(() => Promise.resolve(cloudflareHeaders(result.sdk))) + expect(headers.authorization).toBe("Bearer env-key") + expect(headers.custom).toBe("header") + expect(headers["user-agent"]).toMatch(/^opencode\/.* cloudflare-workers-ai \(.+\) ai-sdk\/openai-compatible\//) + }), + ), + ) + + it.effect("expands account ID vars in endpoint URLs", () => + withEnv({ CLOUDFLARE_ACCOUNT_ID: "acct", CLOUDFLARE_API_KEY: "key" }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(CloudflareWorkersAIPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("cloudflare-workers-ai", "@cf/model", { + endpoint: { + type: "aisdk", + package: "@ai-sdk/openai-compatible", + url: "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/ai/v1", + }, + }), + package: "@ai-sdk/openai-compatible", + options: { + name: "cloudflare-workers-ai", + baseURL: "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/ai/v1", + }, + }, + {}, + ) + expect(cloudflareURL(result.sdk)).toBe( + "https://api.cloudflare.com/client/v4/accounts/acct/ai/v1/chat/completions", + ) + }), + ), + ) + + it.effect("selects languageModel with the API model ID", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(CloudflareWorkersAIPlugin) + const result = yield* plugin.trigger( + "aisdk.language", + { + model: model("cloudflare-workers-ai", "alias", { apiID: ModelV2.ID.make("@cf/api-model") }), + sdk: fakeSelectorSdk(calls), + options: {}, + }, + {}, + ) + expect(result.language).toBeDefined() + expect(calls).toEqual(["languageModel:@cf/api-model"]) + }), + ) + + it.effect("does not create an SDK for non OpenAI-compatible packages", () => + withEnv({ CLOUDFLARE_ACCOUNT_ID: "acct", CLOUDFLARE_API_KEY: "key" }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(CloudflareWorkersAIPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("cloudflare-workers-ai", "@cf/model", { + endpoint: { type: "aisdk", package: "@ai-sdk/anthropic", url: "https://proxy.example/v1" }, + }), + package: "@ai-sdk/anthropic", + options: { name: "cloudflare-workers-ai" }, + }, + {}, + ) + expect(result.sdk).toBeUndefined() + }), + ), + ) +}) diff --git a/packages/core/test/plugin/provider-cohere.test.ts b/packages/core/test/plugin/provider-cohere.test.ts new file mode 100644 index 00000000000..54bec2cec45 --- /dev/null +++ b/packages/core/test/plugin/provider-cohere.test.ts @@ -0,0 +1,86 @@ +import { describe, expect, mock } from "bun:test" +import { Effect } from "effect" +import { ModelV2 } from "@opencode-ai/core/model" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { CoherePlugin } from "@opencode-ai/core/plugin/provider/cohere" +import { fakeSelectorSdk, it, model } from "./provider-helper" + +const cohereOptions: Record[] = [] + +void mock.module("@ai-sdk/cohere", () => ({ + createCohere: (options: Record) => { + cohereOptions.push({ ...options }) + return { + languageModel: (modelID: string) => ({ + modelID, + provider: `${options.name ?? "cohere"}.chat`, + specificationVersion: "v3", + }), + } + }, +})) + +describe("CoherePlugin", () => { + it.effect("creates a Cohere SDK only for @ai-sdk/cohere", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(CoherePlugin) + + const ignored = yield* plugin.trigger( + "aisdk.sdk", + { model: model("cohere", "command"), package: "@ai-sdk/openai-compatible", options: { name: "cohere" } }, + {}, + ) + expect(ignored.sdk).toBeUndefined() + + const result = yield* plugin.trigger( + "aisdk.sdk", + { model: model("cohere", "command"), package: "@ai-sdk/cohere", options: { name: "cohere" } }, + {}, + ) + expect(result.sdk).toBeDefined() + }), + ) + + it.effect("uses the model provider ID as the bundled SDK name", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(CoherePlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("custom-cohere", "command-r-plus"), + package: "@ai-sdk/cohere", + options: { name: "custom-cohere", apiKey: "test", baseURL: "https://cohere.example" }, + }, + {}, + ) + + expect(cohereOptions.at(-1)).toEqual({ + name: "custom-cohere", + apiKey: "test", + baseURL: "https://cohere.example", + }) + expect(result.sdk?.languageModel("command-r-plus").provider).toBe("custom-cohere.chat") + }), + ) + + it.effect("leaves language selection to the default languageModel fallback", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + const sdk = fakeSelectorSdk(calls) + yield* plugin.add(CoherePlugin) + const result = yield* plugin.trigger( + "aisdk.language", + { model: model("cohere", "alias", { apiID: ModelV2.ID.make("command-r-plus") }), sdk, options: {} }, + {}, + ) + + expect(result.language).toBeUndefined() + expect(calls).toEqual([]) + expect(result.language ?? sdk.languageModel("command-r-plus")).toBeDefined() + expect(calls).toEqual(["languageModel:command-r-plus"]) + }), + ) +}) diff --git a/packages/core/test/plugin/provider-deepinfra.test.ts b/packages/core/test/plugin/provider-deepinfra.test.ts new file mode 100644 index 00000000000..9a9cb861eaf --- /dev/null +++ b/packages/core/test/plugin/provider-deepinfra.test.ts @@ -0,0 +1,129 @@ +import { describe, expect, mock } from "bun:test" +import { Effect, Layer } from "effect" +import { AISDK } from "@opencode-ai/core/aisdk" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { DeepInfraPlugin } from "@opencode-ai/core/plugin/provider/deepinfra" +import { testEffect } from "../lib/effect" +import { it, model } from "./provider-helper" + +const itAISDK = testEffect(Layer.provideMerge(AISDK.layer, PluginV2.defaultLayer)) +const deepinfraOptions: Record[] = [] +const deepinfraLanguageModels: string[] = [] + +void mock.module("@ai-sdk/deepinfra", () => ({ + createDeepInfra: (options: Record) => { + const captured = { ...options } + deepinfraOptions.push(captured) + return { + languageModel: (modelID: string) => { + deepinfraLanguageModels.push(modelID) + return { modelID, provider: `${captured.name ?? "deepinfra"}.chat`, specificationVersion: "v3" } + }, + } + }, +})) + +function resetDeepInfraMock() { + deepinfraOptions.length = 0 + deepinfraLanguageModels.length = 0 +} + +describe("DeepInfraPlugin", () => { + it.effect("creates a DeepInfra SDK for @ai-sdk/deepinfra", () => + Effect.gen(function* () { + resetDeepInfraMock() + const plugin = yield* PluginV2.Service + yield* plugin.add(DeepInfraPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { model: model("deepinfra", "model"), package: "@ai-sdk/deepinfra", options: { name: "deepinfra" } }, + {}, + ) + expect(result.sdk).toBeDefined() + }), + ) + + it.effect("passes the model provider ID as the bundled DeepInfra SDK name", () => + Effect.gen(function* () { + resetDeepInfraMock() + const plugin = yield* PluginV2.Service + yield* plugin.add(DeepInfraPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("custom-deepinfra", "model"), + package: "@ai-sdk/deepinfra", + options: { name: "custom-deepinfra", apiKey: "test" }, + }, + {}, + ) + expect(result.sdk.languageModel("model").provider).toBe("custom-deepinfra.chat") + expect(deepinfraOptions).toEqual([{ name: "custom-deepinfra", apiKey: "test" }]) + }), + ) + + it.effect("uses the canonical provider ID as the bundled DeepInfra SDK name", () => + Effect.gen(function* () { + resetDeepInfraMock() + const plugin = yield* PluginV2.Service + yield* plugin.add(DeepInfraPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("deepinfra", "model"), + package: "@ai-sdk/deepinfra", + options: { name: "deepinfra", apiKey: "test" }, + }, + {}, + ) + expect(result.sdk.languageModel("model").provider).toBe("deepinfra.chat") + expect(deepinfraOptions).toEqual([{ name: "deepinfra", apiKey: "test" }]) + }), + ) + + it.effect("matches only the exact bundled DeepInfra package", () => + Effect.gen(function* () { + resetDeepInfraMock() + const plugin = yield* PluginV2.Service + yield* plugin.add(DeepInfraPlugin) + const packages = [ + "unmatched-package", + "@ai-sdk/deepinfra-compatible", + "file:///tmp/@ai-sdk/deepinfra-provider.js", + ] + yield* Effect.forEach(packages, (item) => + Effect.gen(function* () { + const ignored = yield* plugin.trigger( + "aisdk.sdk", + { model: model("deepinfra", "model"), package: item, options: { name: "deepinfra" } }, + {}, + ) + expect(ignored.sdk).toBeUndefined() + }), + ) + const result = yield* plugin.trigger( + "aisdk.sdk", + { model: model("deepinfra", "model"), package: "@ai-sdk/deepinfra", options: { name: "deepinfra" } }, + {}, + ) + expect(result.sdk).toBeDefined() + expect(deepinfraOptions).toEqual([{ name: "deepinfra" }]) + }), + ) + + itAISDK.effect("uses the default languageModel selection for DeepInfra models", () => + Effect.gen(function* () { + resetDeepInfraMock() + const plugin = yield* PluginV2.Service + const aisdk = yield* AISDK.Service + yield* plugin.add(DeepInfraPlugin) + const language = yield* aisdk.language( + model("deepinfra", "meta-llama/Llama-3.3-70B-Instruct", { + endpoint: { type: "aisdk", package: "@ai-sdk/deepinfra" }, + }), + ) + expect(language.provider).toBe("deepinfra.chat") + expect(deepinfraLanguageModels).toEqual(["meta-llama/Llama-3.3-70B-Instruct"]) + }), + ) +}) diff --git a/packages/core/test/plugin/provider-dynamic.test.ts b/packages/core/test/plugin/provider-dynamic.test.ts new file mode 100644 index 00000000000..c15568eebd1 --- /dev/null +++ b/packages/core/test/plugin/provider-dynamic.test.ts @@ -0,0 +1,172 @@ +import { Npm } from "@opencode-ai/core/npm" +import { describe, expect } from "bun:test" +import { Cause, Effect, Layer, Option } from "effect" +import fs from "fs/promises" +import os from "os" +import path from "path" +import { fileURLToPath } from "url" +import { AISDK } from "@opencode-ai/core/aisdk" +import { ModelV2 } from "@opencode-ai/core/model" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { DynamicProviderPlugin } from "@opencode-ai/core/plugin/provider/dynamic" +import { testEffect } from "../lib/effect" +import { fixtureProvider, it, model, npmLayer } from "./provider-helper" + +const fixtureProviderPath = fileURLToPath(fixtureProvider) +const itWithAISDK = testEffect(AISDK.layer.pipe(Layer.provideMerge(PluginV2.defaultLayer))) + +function npmEntrypointLayer(entrypoint: Option.Option) { + return Layer.succeed( + Npm.Service, + Npm.Service.of({ + add: () => Effect.succeed({ directory: "", entrypoint }), + install: () => Effect.void, + which: () => Effect.succeed(Option.none()), + }), + ) +} + +function dynamicPlugin(layer = npmLayer) { + return { id: DynamicProviderPlugin.id, effect: DynamicProviderPlugin.effect.pipe(Effect.provide(layer)) } +} + +function tempEntrypoint(source: string) { + return Effect.acquireRelease( + Effect.promise(async () => { + const directory = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-provider-dynamic-")) + const entrypoint = path.join(directory, "provider.mjs") + await Bun.write(entrypoint, source) + return { directory, entrypoint } + }), + (tmp) => Effect.promise(() => fs.rm(tmp.directory, { recursive: true, force: true })), + ) +} + +describe("DynamicProviderPlugin", () => { + it.effect("creates an SDK from a provider factory export", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(dynamicPlugin()) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("custom", "test-model"), + package: fixtureProvider, + options: { name: "custom", marker: "dynamic" }, + }, + {}, + ) + expect(result.sdk.options).toEqual({ marker: "dynamic", name: "custom" }) + expect(result.sdk.languageModel("x")).toEqual({ modelID: "x", options: { marker: "dynamic", name: "custom" } }) + }), + ) + + it.effect("does not override an SDK already supplied by an earlier plugin", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const sdk = { marker: "existing" } + yield* plugin.add(dynamicPlugin()) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("custom", "test-model"), + package: fixtureProvider, + options: { name: "custom", marker: "dynamic" }, + }, + { sdk }, + ) + expect(result.sdk).toBe(sdk) + }), + ) + + it.effect("injects the provider ID as the SDK factory name", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(dynamicPlugin()) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("custom-provider", "test-model"), + package: fixtureProvider, + options: { name: "custom-provider", marker: "dynamic" }, + }, + {}, + ) + expect(result.sdk.options).toEqual({ marker: "dynamic", name: "custom-provider" }) + }), + ) + + it.effect("loads npm packages through their resolved import entrypoint", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(dynamicPlugin(npmEntrypointLayer(Option.some(fixtureProviderPath)))) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("npm-provider", "test-model"), + package: "fixture-provider", + options: { name: "npm-provider", marker: "npm" }, + }, + {}, + ) + expect(result.sdk.languageModel("x")).toEqual({ modelID: "x", options: { marker: "npm", name: "npm-provider" } }) + }), + ) + + itWithAISDK.effect("wraps missing npm entrypoint failures as AISDK init errors", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const aisdk = yield* AISDK.Service + yield* plugin.add(dynamicPlugin(npmEntrypointLayer(Option.none()))) + const exit = yield* aisdk + .language(model("missing-entrypoint", "alias", { endpoint: { type: "aisdk", package: "fixture-provider" } })) + .pipe(Effect.exit) + expect(exit._tag).toBe("Failure") + if (exit._tag === "Failure") expect(Cause.prettyErrors(exit.cause).join("\n")).toContain("AISDK.InitError") + }), + ) + + itWithAISDK.effect("wraps dynamic import failures as AISDK init errors", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const aisdk = yield* AISDK.Service + yield* plugin.add(dynamicPlugin()) + const exit = yield* aisdk + .language( + model("bad-import", "alias", { endpoint: { type: "aisdk", package: "file:///missing/provider-factory.js" } }), + ) + .pipe(Effect.exit) + expect(exit._tag).toBe("Failure") + if (exit._tag === "Failure") expect(Cause.prettyErrors(exit.cause).join("\n")).toContain("AISDK.InitError") + }), + ) + + itWithAISDK.live("wraps missing provider factory exports as AISDK init errors", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const aisdk = yield* AISDK.Service + const tmp = yield* tempEntrypoint("export const notAProviderFactory = true\n") + yield* plugin.add(dynamicPlugin(npmEntrypointLayer(Option.some(tmp.entrypoint)))) + const exit = yield* aisdk + .language(model("missing-factory", "alias", { endpoint: { type: "aisdk", package: "fixture-provider" } })) + .pipe(Effect.exit) + expect(exit._tag).toBe("Failure") + if (exit._tag === "Failure") expect(Cause.prettyErrors(exit.cause).join("\n")).toContain("AISDK.InitError") + }), + ) + + itWithAISDK.effect("uses the model apiID for the default language model", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const aisdk = yield* AISDK.Service + yield* plugin.add(dynamicPlugin()) + const language = yield* aisdk.language( + model("custom", "alias", { + apiID: ModelV2.ID.make("test-model-api"), + endpoint: { type: "aisdk", package: fixtureProvider }, + }), + ) + expect(language).toMatchObject({ modelID: "test-model-api", options: { name: "custom" } }) + }), + ) +}) diff --git a/packages/core/test/plugin/provider-gateway.test.ts b/packages/core/test/plugin/provider-gateway.test.ts new file mode 100644 index 00000000000..8ee69b7dd49 --- /dev/null +++ b/packages/core/test/plugin/provider-gateway.test.ts @@ -0,0 +1,87 @@ +import { describe, expect, mock } from "bun:test" +import { Effect } from "effect" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { GatewayPlugin } from "@opencode-ai/core/plugin/provider/gateway" +import { it, model } from "./provider-helper" + +const gatewayCalls: Record[] = [] +const vercelGatewayModels = ["anthropic/claude-sonnet-4", "openai/gpt-5", "google/gemini-2.5-pro"] + +mock.module("@ai-sdk/gateway", () => ({ + createGateway(options: Record) { + gatewayCalls.push({ ...options }) + return { + languageModel(modelID: string) { + return { + modelId: modelID, + provider: options.name, + specificationVersion: "v3", + } + }, + } + }, +})) + +describe("GatewayPlugin", () => { + it.effect("creates a Gateway SDK for @ai-sdk/gateway", () => + Effect.gen(function* () { + gatewayCalls.length = 0 + const plugin = yield* PluginV2.Service + yield* plugin.add(GatewayPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { model: model("gateway", "model"), package: "@ai-sdk/gateway", options: { name: "gateway" } }, + {}, + ) + expect(result.sdk).toBeDefined() + expect(gatewayCalls).toHaveLength(1) + }), + ) + + it.effect("passes the model providerID as the Gateway SDK name", () => + Effect.gen(function* () { + gatewayCalls.length = 0 + const plugin = yield* PluginV2.Service + yield* plugin.add(GatewayPlugin) + + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("vercel", "anthropic/claude-sonnet-4"), + package: "@ai-sdk/gateway", + options: { name: "vercel", apiKey: "test-key" }, + }, + {}, + ) + + expect(gatewayCalls).toEqual([{ name: "vercel", apiKey: "test-key" }]) + expect(result.sdk.languageModel("anthropic/claude-sonnet-4").provider).toBe("vercel") + }), + ) + + it.effect("matches Vercel AI Gateway models by their @ai-sdk/gateway package", () => + Effect.gen(function* () { + gatewayCalls.length = 0 + const plugin = yield* PluginV2.Service + yield* plugin.add(GatewayPlugin) + + for (const modelID of vercelGatewayModels) { + const ignored = yield* plugin.trigger( + "aisdk.sdk", + { model: model("vercel", modelID), package: "@ai-sdk/vercel", options: { name: "vercel" } }, + {}, + ) + expect(ignored.sdk).toBeUndefined() + + const result = yield* plugin.trigger( + "aisdk.sdk", + { model: model("vercel", modelID), package: "@ai-sdk/gateway", options: { name: "vercel" } }, + {}, + ) + expect(result.sdk).toBeDefined() + } + + expect(gatewayCalls).toHaveLength(3) + }), + ) +}) diff --git a/packages/core/test/plugin/provider-github-copilot.test.ts b/packages/core/test/plugin/provider-github-copilot.test.ts new file mode 100644 index 00000000000..e2bd899ff3f --- /dev/null +++ b/packages/core/test/plugin/provider-github-copilot.test.ts @@ -0,0 +1,188 @@ +import { describe, expect } from "bun:test" +import { Effect } from "effect" +import { ModelV2 } from "@opencode-ai/core/model" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { GithubCopilotPlugin } from "@opencode-ai/core/plugin/provider/github-copilot" +import { fakeSelectorSdk, it, model } from "./provider-helper" + +describe("GithubCopilotPlugin", () => { + it.effect("creates the bundled Copilot SDK for the GitHub Copilot package", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(GithubCopilotPlugin) + const ignored = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("github-copilot", "gpt-5"), + package: "@ai-sdk/openai-compatible", + options: { name: "github-copilot" }, + }, + {}, + ) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("github-copilot", "gpt-5"), + package: "@ai-sdk/github-copilot", + options: { name: "github-copilot" }, + }, + {}, + ) + expect(ignored.sdk).toBeUndefined() + expect(result.sdk).toBeDefined() + }), + ) + + it.effect("selects languageModel when responses and chat are absent", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(GithubCopilotPlugin) + yield* plugin.trigger( + "aisdk.language", + { + model: model("github-copilot", "claude-sonnet-4"), + sdk: { languageModel: fakeSelectorSdk(calls).languageModel }, + options: {}, + }, + {}, + ) + expect(calls).toEqual(["languageModel:claude-sonnet-4"]) + }), + ) + + it.effect("selects languageModel with the API model ID when responses and chat are absent", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(GithubCopilotPlugin) + yield* plugin.trigger( + "aisdk.language", + { + model: model("github-copilot", "alias", { apiID: ModelV2.ID.make("claude-sonnet-4") }), + sdk: { languageModel: fakeSelectorSdk(calls).languageModel }, + options: {}, + }, + {}, + ) + expect(calls).toEqual(["languageModel:claude-sonnet-4"]) + }), + ) + + it.effect("uses responses for gpt-5 models except gpt-5-mini", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(GithubCopilotPlugin) + yield* plugin.trigger( + "aisdk.language", + { model: model("github-copilot", "gpt-5"), sdk: fakeSelectorSdk(calls), options: {} }, + {}, + ) + yield* plugin.trigger( + "aisdk.language", + { model: model("github-copilot", "gpt-5.1-codex"), sdk: fakeSelectorSdk(calls), options: {} }, + {}, + ) + yield* plugin.trigger( + "aisdk.language", + { model: model("github-copilot", "gpt-4o"), sdk: fakeSelectorSdk(calls), options: {} }, + {}, + ) + yield* plugin.trigger( + "aisdk.language", + { model: model("github-copilot", "gpt-5-mini"), sdk: fakeSelectorSdk(calls), options: {} }, + {}, + ) + yield* plugin.trigger( + "aisdk.language", + { model: model("github-copilot", "gpt-5-mini-2025-08-07"), sdk: fakeSelectorSdk(calls), options: {} }, + {}, + ) + expect(calls).toEqual([ + "responses:gpt-5", + "responses:gpt-5.1-codex", + "chat:gpt-4o", + "chat:gpt-5-mini", + "chat:gpt-5-mini-2025-08-07", + ]) + }), + ) + + it.effect("uses the API model ID when selecting responses or chat", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(GithubCopilotPlugin) + yield* plugin.trigger( + "aisdk.language", + { + model: model("github-copilot", "default", { apiID: ModelV2.ID.make("gpt-5") }), + sdk: fakeSelectorSdk(calls), + options: {}, + }, + {}, + ) + yield* plugin.trigger( + "aisdk.language", + { + model: model("github-copilot", "small", { apiID: ModelV2.ID.make("gpt-5-mini") }), + sdk: fakeSelectorSdk(calls), + options: {}, + }, + {}, + ) + yield* plugin.trigger( + "aisdk.language", + { + model: model("github-copilot", "sonnet", { apiID: ModelV2.ID.make("claude-sonnet-4") }), + sdk: fakeSelectorSdk(calls), + options: {}, + }, + {}, + ) + expect(calls).toEqual(["responses:gpt-5", "chat:gpt-5-mini", "chat:claude-sonnet-4"]) + }), + ) + + it.effect("filters gpt-5-chat-latest before Copilot language selection", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(GithubCopilotPlugin) + const result = yield* plugin.trigger( + "model.update", + {}, + { model: model("github-copilot", "gpt-5-chat-latest"), cancel: false }, + ) + expect(result.cancel).toBe(true) + }), + ) + + it.effect("does not filter gpt-5-chat-latest for non-Copilot providers", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(GithubCopilotPlugin) + const result = yield* plugin.trigger( + "model.update", + {}, + { model: model("custom-copilot", "gpt-5-chat-latest"), cancel: false }, + ) + expect(result.cancel).toBe(false) + }), + ) + + it.effect("ignores non-Copilot providers", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(GithubCopilotPlugin) + const result = yield* plugin.trigger( + "aisdk.language", + { model: model("openai", "gpt-5"), sdk: fakeSelectorSdk(calls), options: {} }, + {}, + ) + expect(calls).toEqual([]) + expect(result.language).toBeUndefined() + }), + ) +}) diff --git a/packages/core/test/plugin/provider-gitlab.test.ts b/packages/core/test/plugin/provider-gitlab.test.ts new file mode 100644 index 00000000000..56a22649a99 --- /dev/null +++ b/packages/core/test/plugin/provider-gitlab.test.ts @@ -0,0 +1,346 @@ +import { describe, expect, mock } from "bun:test" +import { Effect, Layer } from "effect" +import { AuthV2 } from "@opencode-ai/core/auth" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { AuthPlugin } from "@opencode-ai/core/plugin/auth" +import { GitLabPlugin } from "@opencode-ai/core/plugin/provider/gitlab" +import { testEffect } from "../lib/effect" +import { it, model, npmLayer, provider, withEnv } from "./provider-helper" + +const gitlabSDKOptions: Record[] = [] + +void mock.module("gitlab-ai-provider", () => ({ + VERSION: "test-version", + createGitLab: (options: Record) => { + gitlabSDKOptions.push(options) + return { + agenticChat: (id: string, options: unknown) => ({ id, options, type: "agentic" }), + workflowChat: (id: string, options: unknown) => ({ id, options, type: "workflow" }), + } + }, + discoverWorkflowModels: async () => ({ models: [], project: undefined }), + isWorkflowModel: (id: string) => id === "duo-workflow" || id === "duo-workflow-exact", +})) + +const itWithAuth = testEffect(Layer.mergeAll(PluginV2.defaultLayer, AuthV2.defaultLayer, npmLayer)) + +describe("GitLabPlugin", () => { + it.effect("creates SDKs with legacy default instance URL, token env, headers, and feature flags", () => + withEnv( + { + GITLAB_INSTANCE_URL: undefined, + GITLAB_TOKEN: "env-token", + }, + () => + Effect.gen(function* () { + gitlabSDKOptions.length = 0 + const plugin = yield* PluginV2.Service + yield* plugin.add(GitLabPlugin) + yield* plugin.trigger( + "aisdk.sdk", + { model: model("gitlab", "claude"), package: "gitlab-ai-provider", options: { name: "gitlab" } }, + {}, + ) + expect(gitlabSDKOptions).toHaveLength(1) + expect(gitlabSDKOptions[0].instanceUrl).toBe("https://gitlab.com") + expect(gitlabSDKOptions[0].apiKey).toBe("env-token") + expect(gitlabSDKOptions[0].aiGatewayHeaders).toMatchObject({ + "anthropic-beta": "context-1m-2025-08-07", + }) + expect(String((gitlabSDKOptions[0].aiGatewayHeaders as Record)["User-Agent"])).toContain( + "gitlab-ai-provider/test-version", + ) + expect(gitlabSDKOptions[0].featureFlags).toEqual({ + duo_agent_platform_agentic_chat: true, + duo_agent_platform: true, + }) + }), + ), + ) + + it.effect("uses GITLAB_INSTANCE_URL when instanceUrl is not configured", () => + withEnv( + { + GITLAB_INSTANCE_URL: "https://env.gitlab.example", + GITLAB_TOKEN: undefined, + }, + () => + Effect.gen(function* () { + gitlabSDKOptions.length = 0 + const plugin = yield* PluginV2.Service + yield* plugin.add(GitLabPlugin) + yield* plugin.trigger( + "aisdk.sdk", + { model: model("gitlab", "claude"), package: "gitlab-ai-provider", options: { name: "gitlab" } }, + {}, + ) + expect(gitlabSDKOptions[0].instanceUrl).toBe("https://env.gitlab.example") + }), + ), + ) + + it.effect("keeps configured instance URL, apiKey, aiGatewayHeaders, and featureFlags over env/defaults", () => + withEnv( + { + GITLAB_INSTANCE_URL: "https://env.gitlab.example", + GITLAB_TOKEN: "env-token", + }, + () => + Effect.gen(function* () { + gitlabSDKOptions.length = 0 + const plugin = yield* PluginV2.Service + yield* plugin.add(GitLabPlugin) + yield* plugin.trigger( + "aisdk.sdk", + { + model: model("gitlab", "claude"), + package: "gitlab-ai-provider", + options: { + name: "gitlab", + instanceUrl: "https://configured.gitlab.example", + apiKey: "configured-token", + aiGatewayHeaders: { + "anthropic-beta": "configured-beta", + "x-gitlab-test": "1", + }, + featureFlags: { + duo_agent_platform: false, + custom_flag: true, + }, + }, + }, + {}, + ) + expect(gitlabSDKOptions[0].instanceUrl).toBe("https://configured.gitlab.example") + expect(gitlabSDKOptions[0].apiKey).toBe("configured-token") + expect(gitlabSDKOptions[0].aiGatewayHeaders).toMatchObject({ + "anthropic-beta": "configured-beta", + "x-gitlab-test": "1", + }) + expect(gitlabSDKOptions[0].featureFlags).toEqual({ + duo_agent_platform_agentic_chat: true, + duo_agent_platform: false, + custom_flag: true, + }) + }), + ), + ) + + it.effect("ignores non-GitLab SDK packages", () => + Effect.gen(function* () { + gitlabSDKOptions.length = 0 + const plugin = yield* PluginV2.Service + yield* plugin.add(GitLabPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { model: model("gitlab", "claude"), package: "@ai-sdk/openai", options: { name: "gitlab" } }, + {}, + ) + expect(result.sdk).toBeUndefined() + expect(gitlabSDKOptions).toHaveLength(0) + }), + ) + + itWithAuth.effect("uses active API auth token over GITLAB_TOKEN", () => + withEnv( + { + GITLAB_TOKEN: "env-token", + }, + () => + Effect.gen(function* () { + gitlabSDKOptions.length = 0 + const plugin = yield* PluginV2.Service + const auth = yield* AuthV2.Service + yield* auth.create({ + serviceID: AuthV2.ServiceID.make("gitlab"), + credential: new AuthV2.ApiKeyCredential({ type: "api", key: "auth-token" }), + active: true, + }) + yield* plugin.add({ + ...AuthPlugin, + effect: AuthPlugin.effect.pipe(Effect.provideService(AuthV2.Service, auth)), + }) + yield* plugin.add(GitLabPlugin) + const updated = yield* plugin.trigger("provider.update", {}, { provider: provider("gitlab"), cancel: false }) + yield* plugin.trigger( + "aisdk.sdk", + { + model: model("gitlab", "claude"), + package: "gitlab-ai-provider", + options: updated.provider.options.aisdk.provider, + }, + {}, + ) + expect(gitlabSDKOptions[0].apiKey).toBe("auth-token") + }), + ), + ) + + itWithAuth.effect("uses active OAuth access token when no API auth exists", () => + withEnv( + { + GITLAB_TOKEN: undefined, + }, + () => + Effect.gen(function* () { + gitlabSDKOptions.length = 0 + const plugin = yield* PluginV2.Service + const auth = yield* AuthV2.Service + yield* auth.create({ + serviceID: AuthV2.ServiceID.make("gitlab"), + credential: new AuthV2.OAuthCredential({ + type: "oauth", + refresh: "refresh-token", + access: "oauth-token", + expires: 9999999999999, + }), + active: true, + }) + yield* plugin.add({ + ...AuthPlugin, + effect: AuthPlugin.effect.pipe(Effect.provideService(AuthV2.Service, auth)), + }) + yield* plugin.add(GitLabPlugin) + const updated = yield* plugin.trigger("provider.update", {}, { provider: provider("gitlab"), cancel: false }) + yield* plugin.trigger( + "aisdk.sdk", + { + model: model("gitlab", "claude"), + package: "gitlab-ai-provider", + options: updated.provider.options.aisdk.provider, + }, + {}, + ) + expect(gitlabSDKOptions[0].apiKey).toBe("oauth-token") + }), + ), + ) + + it.effect("uses workflowChat for duo workflow models and preserves selectedModelRef", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: [string, unknown][] = [] + yield* plugin.add(GitLabPlugin) + const result = yield* plugin.trigger( + "aisdk.language", + { + model: model("gitlab", "duo-workflow-custom", { + options: { + headers: {}, + body: {}, + aisdk: { provider: {}, request: { workflowRef: "ref", workflowDefinition: "definition" } }, + }, + }), + sdk: { + workflowChat: (id: string, options: unknown) => { + calls.push([id, options]) + return { id, options } + }, + agenticChat: () => undefined, + }, + options: { featureFlags: { configured: true } }, + }, + {}, + ) + expect(calls).toEqual([ + ["duo-workflow", { featureFlags: { configured: true }, workflowDefinition: "definition" }], + ]) + expect(result.language as unknown).toEqual({ + id: "duo-workflow", + options: calls[0]?.[1], + selectedModelRef: "ref", + }) + }), + ) + + it.effect("uses exact static workflow model ids when the provider recognizes them", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: [string, unknown][] = [] + yield* plugin.add(GitLabPlugin) + const result = yield* plugin.trigger( + "aisdk.language", + { + model: model("gitlab", "duo-workflow-exact"), + sdk: { + workflowChat: (id: string, options: unknown) => { + calls.push([id, options]) + return { id, options } + }, + agenticChat: () => undefined, + }, + options: { featureFlags: { configured: true } }, + }, + {}, + ) + expect(calls).toEqual([ + ["duo-workflow-exact", { featureFlags: { configured: true }, workflowDefinition: undefined }], + ]) + expect(result.language as unknown).toEqual({ id: "duo-workflow-exact", options: calls[0]?.[1] }) + }), + ) + + it.effect("uses provider feature flags instead of request feature flags", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: [string, unknown][] = [] + yield* plugin.add(GitLabPlugin) + yield* plugin.trigger( + "aisdk.language", + { + model: model("gitlab", "duo-workflow-custom", { + options: { + headers: {}, + body: {}, + aisdk: { provider: {}, request: { featureFlags: { request_flag: true } } }, + }, + }), + sdk: { + workflowChat: (id: string, options: unknown) => { + calls.push([id, options]) + return { id, options } + }, + agenticChat: () => undefined, + }, + options: { featureFlags: { configured: true } }, + }, + {}, + ) + expect(calls).toEqual([["duo-workflow", { featureFlags: { configured: true }, workflowDefinition: undefined }]]) + }), + ) + + it.effect("uses agenticChat with provider aiGatewayHeaders and feature flags for normal models", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: [string, unknown][] = [] + yield* plugin.add(GitLabPlugin) + yield* plugin.trigger( + "aisdk.language", + { + model: model("gitlab", "claude", { + options: { headers: { h: "v" }, body: {}, aisdk: { provider: {}, request: {} } }, + }), + sdk: { + workflowChat: () => undefined, + agenticChat: (id: string, options: unknown) => { + const selected = options as { + aiGatewayHeaders?: Record + featureFlags?: Record + } + calls.push([ + id, + { aiGatewayHeaders: { ...selected.aiGatewayHeaders }, featureFlags: { ...selected.featureFlags } }, + ]) + }, + }, + options: { aiGatewayHeaders: { fallback: "header" }, featureFlags: { duo_agent_platform: true } }, + }, + {}, + ) + expect(calls).toEqual([ + ["claude", { aiGatewayHeaders: { fallback: "header" }, featureFlags: { duo_agent_platform: true } }], + ]) + }), + ) +}) diff --git a/packages/core/test/plugin/provider-google-vertex-anthropic.test.ts b/packages/core/test/plugin/provider-google-vertex-anthropic.test.ts new file mode 100644 index 00000000000..6bcece53c95 --- /dev/null +++ b/packages/core/test/plugin/provider-google-vertex-anthropic.test.ts @@ -0,0 +1,147 @@ +import { describe, expect } from "bun:test" +import { Effect } from "effect" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { GoogleVertexAnthropicPlugin } from "@opencode-ai/core/plugin/provider/google-vertex" +import { fakeSelectorSdk, it, model, provider, withEnv } from "./provider-helper" + +describe("GoogleVertexAnthropicPlugin", () => { + it.effect("resolves legacy project and location env on provider update", () => + withEnv( + { + GOOGLE_CLOUD_PROJECT: "cloud-project", + GCP_PROJECT: "gcp-project", + GCLOUD_PROJECT: "gcloud-project", + GOOGLE_CLOUD_LOCATION: "cloud-location", + VERTEX_LOCATION: "vertex-location", + GOOGLE_VERTEX_LOCATION: "google-vertex-location", + }, + () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(GoogleVertexAnthropicPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { provider: provider("google-vertex-anthropic"), cancel: false }, + ) + expect(result.provider.options.aisdk.provider.project).toBe("cloud-project") + expect(result.provider.options.aisdk.provider.location).toBe("cloud-location") + }), + ), + ) + + it.effect("keeps configured project and location over env fallback", () => + withEnv({ GOOGLE_CLOUD_PROJECT: "env-project", GOOGLE_CLOUD_LOCATION: "env-location" }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(GoogleVertexAnthropicPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("google-vertex-anthropic", { + options: { + headers: {}, + body: {}, + aisdk: { provider: { project: "configured-project", location: "configured-location" }, request: {} }, + }, + }), + cancel: false, + }, + ) + expect(result.provider.options.aisdk.provider.project).toBe("configured-project") + expect(result.provider.options.aisdk.provider.location).toBe("configured-location") + }), + ), + ) + + it.effect("creates SDKs from legacy env fallback and default location", () => + withEnv( + { + GOOGLE_CLOUD_PROJECT: undefined, + GCP_PROJECT: "gcp-project", + GCLOUD_PROJECT: "gcloud-project", + GOOGLE_CLOUD_LOCATION: undefined, + VERTEX_LOCATION: undefined, + GOOGLE_VERTEX_LOCATION: "ignored-location", + }, + () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(GoogleVertexAnthropicPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("google-vertex-anthropic", "claude-sonnet-4-5"), + package: "@ai-sdk/google-vertex/anthropic", + options: { name: "google-vertex-anthropic" }, + }, + {}, + ) + expect(result.sdk.languageModel("claude-sonnet-4-5").config.baseURL).toBe( + "https://aiplatform.googleapis.com/v1/projects/gcp-project/locations/global/publishers/anthropic/models", + ) + }), + ), + ) + + it.effect("uses GOOGLE_CLOUD_LOCATION before VERTEX_LOCATION when creating SDKs", () => + withEnv( + { GOOGLE_CLOUD_PROJECT: "project", GOOGLE_CLOUD_LOCATION: "cloud-location", VERTEX_LOCATION: "vertex-location" }, + () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(GoogleVertexAnthropicPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("google-vertex-anthropic", "claude-sonnet-4-5"), + package: "@ai-sdk/google-vertex/anthropic", + options: { name: "google-vertex-anthropic" }, + }, + {}, + ) + expect(result.sdk.languageModel("claude-sonnet-4-5").config.baseURL).toBe( + "https://cloud-location-aiplatform.googleapis.com/v1/projects/project/locations/cloud-location/publishers/anthropic/models", + ) + }), + ), + ) + + it.effect("trims model IDs before selecting language models", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(GoogleVertexAnthropicPlugin) + yield* plugin.trigger( + "aisdk.language", + { + model: model("google-vertex-anthropic", " claude-sonnet-4-5 "), + sdk: { languageModel: fakeSelectorSdk(calls).languageModel }, + options: {}, + }, + {}, + ) + expect(calls).toEqual(["languageModel:claude-sonnet-4-5"]) + }), + ) + + it.effect("ignores non Vertex Anthropic providers for language selection", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(GoogleVertexAnthropicPlugin) + const result = yield* plugin.trigger( + "aisdk.language", + { + model: model("google-vertex", "claude-sonnet-4-5"), + sdk: { languageModel: fakeSelectorSdk(calls).languageModel }, + options: {}, + }, + {}, + ) + expect(calls).toEqual([]) + expect(result.language).toBeUndefined() + }), + ) +}) diff --git a/packages/core/test/plugin/provider-google-vertex.test.ts b/packages/core/test/plugin/provider-google-vertex.test.ts new file mode 100644 index 00000000000..3bd60fd7211 --- /dev/null +++ b/packages/core/test/plugin/provider-google-vertex.test.ts @@ -0,0 +1,300 @@ +import { describe, expect, mock } from "bun:test" +import { Effect } from "effect" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { GoogleVertexPlugin } from "@opencode-ai/core/plugin/provider/google-vertex" +import { fakeSelectorSdk, it, model, provider, withEnv } from "./provider-helper" + +const vertexOptions: Record[] = [] + +void mock.module("@ai-sdk/google-vertex", () => ({ + createVertex: (options: Record) => { + vertexOptions.push(options) + return { + languageModel: (modelID: string) => ({ modelID, provider: "google-vertex", specificationVersion: "v3" }), + } + }, +})) + +void mock.module("google-auth-library", () => ({ + GoogleAuth: class { + async getApplicationDefault() { + return { + credential: { + async getAccessToken() { + return { token: "vertex-token" } + }, + }, + } + } + }, +})) + +describe("GoogleVertexPlugin", () => { + it.effect("resolves project and location from env using legacy precedence", () => + withEnv( + { + GOOGLE_CLOUD_PROJECT: "google-cloud-project", + GCP_PROJECT: "gcp-project", + GCLOUD_PROJECT: "gcloud-project", + GOOGLE_VERTEX_LOCATION: "google-vertex-location", + GOOGLE_CLOUD_LOCATION: "google-cloud-location", + VERTEX_LOCATION: "vertex-location", + }, + () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(GoogleVertexPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("google-vertex", { + endpoint: { + type: "aisdk", + package: "@ai-sdk/openai-compatible", + url: "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}", + }, + }), + cancel: false, + }, + ) + expect(result.provider.options.aisdk.provider.project).toBe("google-cloud-project") + expect(result.provider.options.aisdk.provider.location).toBe("google-vertex-location") + expect(result.provider.endpoint).toEqual({ + type: "aisdk", + package: "@ai-sdk/openai-compatible", + url: "https://google-vertex-location-aiplatform.googleapis.com/v1/projects/google-cloud-project/locations/google-vertex-location", + }) + }), + ), + ) + + it.effect("resolves the advertised GOOGLE_VERTEX_PROJECT env for provider updates and SDKs", () => + withEnv( + { + GOOGLE_VERTEX_PROJECT: "vertex-project", + GOOGLE_CLOUD_PROJECT: undefined, + GCP_PROJECT: undefined, + GCLOUD_PROJECT: undefined, + GOOGLE_VERTEX_LOCATION: "europe-west4", + GOOGLE_CLOUD_LOCATION: undefined, + VERTEX_LOCATION: undefined, + }, + () => + Effect.gen(function* () { + vertexOptions.length = 0 + const plugin = yield* PluginV2.Service + yield* plugin.add(GoogleVertexPlugin) + const updated = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("google-vertex", { + endpoint: { + type: "aisdk", + package: "@ai-sdk/openai-compatible", + url: "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}", + }, + }), + cancel: false, + }, + ) + yield* plugin.trigger( + "aisdk.sdk", + { + model: model("google-vertex", "gemini", { + endpoint: { type: "aisdk", package: "@ai-sdk/google-vertex" }, + }), + package: "@ai-sdk/google-vertex", + options: { name: "google-vertex" }, + }, + {}, + ) + + expect(updated.provider.options.aisdk.provider.project).toBe("vertex-project") + expect(updated.provider.endpoint).toEqual({ + type: "aisdk", + package: "@ai-sdk/openai-compatible", + url: "https://europe-west4-aiplatform.googleapis.com/v1/projects/vertex-project/locations/europe-west4", + }) + expect(vertexOptions[0].project).toBe("vertex-project") + expect(vertexOptions[0].location).toBe("europe-west4") + }), + ), + ) + + it.effect("keeps configured project and location over env and uses global endpoint", () => + withEnv( + { + GOOGLE_CLOUD_PROJECT: "env-project", + GCP_PROJECT: "env-gcp-project", + GCLOUD_PROJECT: "env-gcloud-project", + GOOGLE_VERTEX_LOCATION: "env-location", + GOOGLE_CLOUD_LOCATION: "env-google-cloud-location", + VERTEX_LOCATION: "env-vertex-location", + }, + () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(GoogleVertexPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("google-vertex", { + endpoint: { + type: "aisdk", + package: "@ai-sdk/openai-compatible", + url: "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}", + }, + options: { + headers: {}, + body: {}, + aisdk: { provider: { project: "config-project", location: "global" }, request: {} }, + }, + }), + cancel: false, + }, + ) + expect(result.provider.options.aisdk.provider.project).toBe("config-project") + expect(result.provider.options.aisdk.provider.location).toBe("global") + expect(result.provider.endpoint).toEqual({ + type: "aisdk", + package: "@ai-sdk/openai-compatible", + url: "https://aiplatform.googleapis.com/v1/projects/config-project/locations/global", + }) + }), + ), + ) + + it.effect("defaults location to us-central1 when only project is configured", () => + withEnv( + { + GOOGLE_CLOUD_PROJECT: undefined, + GCP_PROJECT: undefined, + GCLOUD_PROJECT: undefined, + GOOGLE_VERTEX_LOCATION: undefined, + GOOGLE_CLOUD_LOCATION: undefined, + VERTEX_LOCATION: undefined, + }, + () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(GoogleVertexPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("google-vertex", { + options: { headers: {}, body: {}, aisdk: { provider: { project: "config-project" }, request: {} } }, + }), + cancel: false, + }, + ) + expect(result.provider.options.aisdk.provider.project).toBe("config-project") + expect(result.provider.options.aisdk.provider.location).toBe("us-central1") + }), + ), + ) + + it.effect("does not pass Google auth fetch to the native Vertex SDK", () => + withEnv( + { + GOOGLE_CLOUD_PROJECT: "env-project", + GOOGLE_VERTEX_LOCATION: "env-location", + }, + () => + Effect.gen(function* () { + vertexOptions.length = 0 + const plugin = yield* PluginV2.Service + yield* plugin.add(GoogleVertexPlugin) + yield* plugin.trigger( + "aisdk.sdk", + { + model: model("google-vertex", "gemini", { + endpoint: { type: "aisdk", package: "@ai-sdk/google-vertex" }, + }), + package: "@ai-sdk/google-vertex", + options: { name: "google-vertex" }, + }, + {}, + ) + expect(vertexOptions).toHaveLength(1) + expect(vertexOptions[0].project).toBe("env-project") + expect(vertexOptions[0].location).toBe("env-location") + expect(vertexOptions[0].fetch).toBeUndefined() + }), + ), + ) + + it.effect("keeps Google auth fetch for OpenAI-compatible Vertex endpoints", () => + Effect.gen(function* () { + const fetchCalls: { input: Parameters[0]; init?: RequestInit }[] = [] + const plugin = yield* PluginV2.Service + yield* plugin.add(GoogleVertexPlugin) + yield* plugin.add({ + id: PluginV2.ID.make("capture-openai-compatible"), + effect: Effect.succeed({ + "aisdk.sdk": (evt) => + Effect.promise(async () => { + if (evt.model.providerID !== "google-vertex") return + if (evt.package !== "@ai-sdk/openai-compatible") return + expect(typeof evt.options.fetch).toBe("function") + await evt.options.fetch("https://vertex.example", { + headers: { "x-test": "1" }, + }) + }), + }), + }) + const originalFetch = fetch + ;(globalThis as typeof globalThis & { fetch: typeof fetch }).fetch = (async ( + input: Parameters[0], + init?: RequestInit, + ) => { + fetchCalls.push({ input, init }) + return new Response("ok") + }) as typeof fetch + yield* Effect.acquireUseRelease( + Effect.void, + () => + plugin.trigger( + "aisdk.sdk", + { + model: model("google-vertex", "gemini", { + endpoint: { type: "aisdk", package: "@ai-sdk/openai-compatible" }, + }), + package: "@ai-sdk/openai-compatible", + options: { name: "google-vertex" }, + }, + {}, + ), + () => + Effect.sync(() => { + ;(globalThis as typeof globalThis & { fetch: typeof fetch }).fetch = originalFetch + }), + ) + expect(fetchCalls).toHaveLength(1) + expect(fetchCalls[0].input).toBe("https://vertex.example") + expect(new Headers(fetchCalls[0].init?.headers).get("authorization")).toBe("Bearer vertex-token") + expect(new Headers(fetchCalls[0].init?.headers).get("x-test")).toBe("1") + }), + ) + + it.effect("trims model IDs before selecting language models", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(GoogleVertexPlugin) + yield* plugin.trigger( + "aisdk.language", + { + model: model("google-vertex", " gemini-2.5-pro "), + sdk: { languageModel: fakeSelectorSdk(calls).languageModel }, + options: {}, + }, + {}, + ) + expect(calls).toEqual(["languageModel:gemini-2.5-pro"]) + }), + ) +}) diff --git a/packages/core/test/plugin/provider-google.test.ts b/packages/core/test/plugin/provider-google.test.ts new file mode 100644 index 00000000000..fdb7bf75eea --- /dev/null +++ b/packages/core/test/plugin/provider-google.test.ts @@ -0,0 +1,70 @@ +import { describe, expect } from "bun:test" +import { Effect, Layer } from "effect" +import { AISDK } from "@opencode-ai/core/aisdk" +import { ModelV2 } from "@opencode-ai/core/model" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { GooglePlugin } from "@opencode-ai/core/plugin/provider/google" +import { testEffect } from "../lib/effect" +import { it, model } from "./provider-helper" + +const itWithAISDK = testEffect(AISDK.layer.pipe(Layer.provideMerge(PluginV2.defaultLayer))) + +describe("GooglePlugin", () => { + it.effect("creates a Google Generative AI SDK for @ai-sdk/google using the provider ID as SDK name", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(GooglePlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("custom-google", "gemini"), + package: "@ai-sdk/google", + options: { name: "custom-google", apiKey: "test" }, + }, + {}, + ) + expect(result.sdk).toBeDefined() + expect(result.sdk?.languageModel("gemini").provider).toBe("custom-google") + }), + ) + + it.effect("ignores non-Google SDK packages", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(GooglePlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { model: model("google", "gemini"), package: "@ai-sdk/google-vertex", options: { name: "google" } }, + {}, + ) + expect(result.sdk).toBeUndefined() + }), + ) + + itWithAISDK.effect("uses default languageModel loading with provider ID parity", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const aisdk = yield* AISDK.Service + yield* plugin.add(GooglePlugin) + const language = yield* aisdk.language( + model("custom-google", "alias", { + apiID: ModelV2.ID.make("gemini-api"), + endpoint: { + type: "aisdk", + package: "@ai-sdk/google", + }, + options: { + headers: {}, + body: {}, + aisdk: { + provider: { apiKey: "test" }, + request: {}, + }, + }, + }), + ) + expect(language.modelId).toBe("gemini-api") + expect(language.provider).toBe("custom-google") + }), + ) +}) diff --git a/packages/core/test/plugin/provider-groq.test.ts b/packages/core/test/plugin/provider-groq.test.ts new file mode 100644 index 00000000000..579d70da59a --- /dev/null +++ b/packages/core/test/plugin/provider-groq.test.ts @@ -0,0 +1,101 @@ +import { describe, expect } from "bun:test" +import { createGroq } from "@ai-sdk/groq" +import { Effect, Layer } from "effect" +import { AISDK } from "@opencode-ai/core/aisdk" +import { ModelV2 } from "@opencode-ai/core/model" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { GroqPlugin } from "@opencode-ai/core/plugin/provider/groq" +import { it, model } from "./provider-helper" +import { testEffect } from "../lib/effect" + +const aisdkIt = testEffect(AISDK.layer.pipe(Layer.provideMerge(PluginV2.defaultLayer))) + +describe("GroqPlugin", () => { + it.effect("creates a Groq SDK for @ai-sdk/groq", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(GroqPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { model: model("groq", "llama"), package: "@ai-sdk/groq", options: { name: "groq" } }, + {}, + ) + expect(result.sdk).toBeDefined() + }), + ) + + it.effect("ignores non-Groq SDK packages", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(GroqPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { model: model("groq", "llama"), package: "@ai-sdk/openai-compatible", options: { name: "groq" } }, + {}, + ) + expect(result.sdk).toBeUndefined() + }), + ) + + it.effect("only matches the bundled @ai-sdk/groq package exactly", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(GroqPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { model: model("groq", "llama"), package: "@ai-sdk/groq/compat", options: { name: "groq" } }, + {}, + ) + expect(result.sdk).toBeUndefined() + }), + ) + + it.effect("matches the old bundled Groq SDK provider naming", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(GroqPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("custom-groq", "llama"), + package: "@ai-sdk/groq", + options: { name: "custom-groq", apiKey: "test" }, + }, + {}, + ) + const expected = createGroq({ name: "custom-groq", apiKey: "test" } as Parameters[0] & { + name: string + }).languageModel("llama") + const actual = result.sdk?.languageModel("llama") + expect(actual?.provider).toBe(expected.provider) + expect(actual?.modelId).toBe(expected.modelId) + }), + ) + + aisdkIt.effect("uses the default languageModel(apiID) behavior", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const aisdk = yield* AISDK.Service + yield* plugin.add(GroqPlugin) + const result = yield* aisdk.language( + model("groq", "alias", { + apiID: ModelV2.ID.make("llama-api"), + endpoint: { + type: "aisdk", + package: "@ai-sdk/groq", + }, + options: { + headers: {}, + body: {}, + aisdk: { + provider: { apiKey: "test" }, + request: {}, + }, + }, + }), + ) + expect(result.modelId).toBe("llama-api") + expect(result.provider).toBe("groq.chat") + }), + ) +}) diff --git a/packages/core/test/plugin/provider-helper.ts b/packages/core/test/plugin/provider-helper.ts new file mode 100644 index 00000000000..0d67075ac8e --- /dev/null +++ b/packages/core/test/plugin/provider-helper.ts @@ -0,0 +1,100 @@ +import { Npm } from "@opencode-ai/core/npm" +import type { LanguageModelV3 } from "@ai-sdk/provider" +import { expect } from "bun:test" +import { Effect, Layer, Option } from "effect" +import { ModelV2 } from "@opencode-ai/core/model" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { ProviderV2 } from "@opencode-ai/core/provider" +import { testEffect } from "../lib/effect" + +export const fixtureProvider = new URL("./fixtures/provider-factory.ts", import.meta.url).href + +export const npmLayer = Layer.succeed( + Npm.Service, + Npm.Service.of({ + add: () => Effect.succeed({ directory: "", entrypoint: Option.none() }), + install: () => Effect.void, + which: () => Effect.succeed(Option.none()), + }), +) + +export const it = testEffect(Layer.mergeAll(PluginV2.defaultLayer, npmLayer)) + +export function provider(providerID: string, options?: Partial) { + return new ProviderV2.Info({ + ...ProviderV2.Info.empty(ProviderV2.ID.make(providerID)), + endpoint: { + type: "aisdk", + package: "test-provider", + }, + ...options, + options: { + headers: {}, + body: {}, + aisdk: { + provider: {}, + request: {}, + }, + ...options?.options, + }, + }) +} + +export function model(providerID: string, modelID: string, options?: Partial) { + return new ModelV2.Info({ + ...ModelV2.Info.empty(ProviderV2.ID.make(providerID), ModelV2.ID.make(modelID)), + apiID: ModelV2.ID.make(modelID), + endpoint: { + type: "aisdk", + package: "test-provider", + }, + ...options, + options: { + headers: {}, + body: {}, + aisdk: { + provider: {}, + request: {}, + }, + ...options?.options, + }, + }) +} + +export function withEnv(vars: Record, fx: () => Effect.Effect) { + return Effect.acquireUseRelease( + Effect.sync(() => { + const previous = Object.fromEntries(Object.keys(vars).map((key) => [key, process.env[key]])) + for (const [key, value] of Object.entries(vars)) { + if (value === undefined) delete process.env[key] + else process.env[key] = value + } + return previous + }), + () => fx(), + (previous) => + Effect.sync(() => { + for (const [key, value] of Object.entries(previous)) { + if (value === undefined) delete process.env[key] + else process.env[key] = value + } + }), + ) +} + +export function fakeSelectorSdk(calls: string[]) { + const make = (method: string) => (id: string) => { + calls.push(`${method}:${id}`) + return { modelId: id, provider: method, specificationVersion: "v3" } as unknown as LanguageModelV3 + } + return { + responses: make("responses"), + messages: make("messages"), + chat: make("chat"), + languageModel: make("languageModel"), + } +} + +export function expectPluginRegistered(ids: string[], id: string) { + expect(ids).toContain(PluginV2.ID.make(id)) +} diff --git a/packages/core/test/plugin/provider-kilo.test.ts b/packages/core/test/plugin/provider-kilo.test.ts new file mode 100644 index 00000000000..33b2593bf9b --- /dev/null +++ b/packages/core/test/plugin/provider-kilo.test.ts @@ -0,0 +1,178 @@ +import { describe, expect } from "bun:test" +import { Effect } from "effect" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { ProviderPlugins } from "@opencode-ai/core/plugin/provider" +import { KiloPlugin } from "@opencode-ai/core/plugin/provider/kilo" +import { expectPluginRegistered, it, model, provider, withEnv } from "./provider-helper" + +describe("KiloPlugin", () => { + it.effect("is registered so legacy referer headers can be applied", () => + Effect.sync(() => + expectPluginRegistered( + ProviderPlugins.map((item) => item.id), + "kilo", + ), + ), + ) + + it.effect("applies legacy referer headers only to kilo", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(KiloPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("kilo", { + options: { headers: { Existing: "value" }, body: {}, aisdk: { provider: {}, request: {} } }, + }), + cancel: false, + }, + ) + const ignored = yield* plugin.trigger("provider.update", {}, { provider: provider("openrouter"), cancel: false }) + expect(result.provider.options.headers).toEqual({ + Existing: "value", + "HTTP-Referer": "https://kilo.ai/", + "X-Title": "Kilo Code", + }) + expect(ignored.provider.options.headers).toEqual({}) + }), + ) + + it.effect("uses the exact legacy Kilo header casing and set", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(KiloPlugin) + const result = yield* plugin.trigger("provider.update", {}, { provider: provider("kilo"), cancel: false }) + + expect(result.provider.options.headers).toEqual({ + "HTTP-Referer": "https://kilo.ai/", + "X-Title": "Kilo Code", + }) + expect(result.provider.options.headers).not.toHaveProperty("http-referer") + expect(result.provider.options.headers).not.toHaveProperty("x-title") + expect(result.provider.options.headers).not.toHaveProperty("X-Source") + }), + ) + + it.effect("uses the legacy provider-id guard instead of endpoint package matching", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(KiloPlugin) + const matchingID = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("kilo", { + endpoint: { type: "aisdk", package: "not-kilo" }, + }), + cancel: false, + }, + ) + const matchingPackage = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("custom-kilo", { + endpoint: { type: "aisdk", package: "kilo" }, + }), + cancel: false, + }, + ) + + expect(matchingID.provider.options.headers).toEqual({ + "HTTP-Referer": "https://kilo.ai/", + "X-Title": "Kilo Code", + }) + expect(matchingPackage.provider.options.headers).toEqual({}) + }), + ) + + it.effect("routes the Kilo catalog through the Kilo Gateway SDK", () => + withEnv({ KILO_API_KEY: undefined, KILO_ORG_ID: undefined }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(KiloPlugin) + const updated = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("kilo", { + endpoint: { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://api.kilo.ai/api/gateway" }, + options: { + headers: {}, + body: {}, + aisdk: { provider: { apiKey: "stored-token" }, request: {} }, + }, + }), + cancel: false, + }, + ) + + expect(updated.provider.endpoint).toEqual({ + type: "aisdk", + package: "@kilocode/kilo-gateway", + url: "https://api.kilo.ai/api/openrouter", + }) + expect(updated.provider.options.aisdk.provider.kilocodeToken).toBe("stored-token") + + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("kilo", "kilo-auto/free"), + package: "@kilocode/kilo-gateway", + options: updated.provider.options.aisdk.provider, + }, + {}, + ) + expect(result.sdk).toBeDefined() + expect(typeof result.sdk.languageModel).toBe("function") + expect(typeof result.sdk.anthropic).toBe("function") + }), + ), + ) + + it.effect("keeps authenticated credentials ahead of inherited environment keys", () => + withEnv({ KILO_API_KEY: "environment-token", KILO_ORG_ID: "environment-org" }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(KiloPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("kilo", { + enabled: { via: "auth", service: "kilo" }, + options: { + headers: {}, + body: {}, + aisdk: { + provider: { apiKey: "authenticated-token", kilocodeOrganizationId: "authenticated-org" }, + request: {}, + }, + }, + }), + cancel: false, + }, + ) + + expect(result.provider.enabled).toEqual({ via: "auth", service: "kilo" }) + expect(result.provider.options.aisdk.provider.kilocodeToken).toBe("authenticated-token") + expect(result.provider.options.aisdk.provider.kilocodeOrganizationId).toBe("environment-org") + }), + ), + ) + + it.effect("keeps anonymous Kilo models available without credentials", () => + withEnv({ KILO_API_KEY: undefined, KILO_ORG_ID: undefined }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(KiloPlugin) + const result = yield* plugin.trigger("provider.update", {}, { provider: provider("kilo"), cancel: false }) + + expect(result.provider.enabled).toEqual({ via: "custom", data: { anonymous: true } }) + expect(result.provider.options.aisdk.provider.kilocodeToken).toBe("anonymous") + }), + ), + ) +}) diff --git a/packages/core/test/plugin/provider-llmgateway.test.ts b/packages/core/test/plugin/provider-llmgateway.test.ts new file mode 100644 index 00000000000..4c69aee7864 --- /dev/null +++ b/packages/core/test/plugin/provider-llmgateway.test.ts @@ -0,0 +1,63 @@ +import { describe, expect } from "bun:test" +import { Effect } from "effect" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { ProviderPlugins } from "@opencode-ai/core/plugin/provider" +import { LLMGatewayPlugin } from "@opencode-ai/core/plugin/provider/llmgateway" +import { expectPluginRegistered, it, provider } from "./provider-helper" + +describe("LLMGatewayPlugin", () => { + it.effect("is registered so legacy referer headers can be applied", () => + Effect.sync(() => + expectPluginRegistered( + ProviderPlugins.map((item) => item.id), + "llmgateway", + ), + ), + ) + + it.effect("applies legacy referer headers only to enabled llmgateway", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(LLMGatewayPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("llmgateway", { + enabled: { via: "env", name: "LLMGATEWAY_API_KEY" }, + options: { headers: { Existing: "value" }, body: {}, aisdk: { provider: {}, request: {} } }, + }), + cancel: false, + }, + ) + const ignored = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("openrouter", { + enabled: { via: "env", name: "OPENROUTER_API_KEY" }, + }), + cancel: false, + }, + ) + expect(result.provider.options.headers).toEqual({ + Existing: "value", + "HTTP-Referer": "https://kilo.ai/", + "X-Title": "Kilo Code", + "X-Source": "kilo", + }) + expect(ignored.provider.options.headers).toEqual({}) + }), + ) + + it.effect("does not apply legacy headers to a disabled llmgateway provider", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(LLMGatewayPlugin) + const result = yield* plugin.trigger("provider.update", {}, { provider: provider("llmgateway"), cancel: false }) + + expect(result.provider.enabled).toBe(false) + expect(result.provider.options.headers).toEqual({}) + }), + ) +}) diff --git a/packages/core/test/plugin/provider-mistral.test.ts b/packages/core/test/plugin/provider-mistral.test.ts new file mode 100644 index 00000000000..f24ff53e5be --- /dev/null +++ b/packages/core/test/plugin/provider-mistral.test.ts @@ -0,0 +1,106 @@ +import { describe, expect } from "bun:test" +import { Effect } from "effect" +import { ModelV2 } from "@opencode-ai/core/model" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { MistralPlugin } from "@opencode-ai/core/plugin/provider/mistral" +import { fakeSelectorSdk, it, model } from "./provider-helper" + +describe("MistralPlugin", () => { + it.effect("creates a Mistral SDK for @ai-sdk/mistral", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(MistralPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { model: model("mistral", "mistral-large"), package: "@ai-sdk/mistral", options: { name: "mistral" } }, + {}, + ) + expect(result.sdk).toBeDefined() + }), + ) + + it.effect("ignores non-Mistral SDK packages", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(MistralPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("mistral", "mistral-large"), + package: "@ai-sdk/openai-compatible", + options: { name: "mistral" }, + }, + {}, + ) + expect(result.sdk).toBeUndefined() + }), + ) + + it.effect("matches the old bundled Mistral SDK provider name for the bundled provider ID", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const providers: string[] = [] + yield* plugin.add(MistralPlugin) + yield* plugin.add({ + id: PluginV2.ID.make("mistral-sdk-inspector"), + effect: Effect.succeed({ + "aisdk.sdk": (evt) => + Effect.sync(() => { + providers.push(evt.sdk.languageModel("mistral-large").provider) + }), + }), + }) + const result = yield* plugin.trigger( + "aisdk.sdk", + { model: model("mistral", "mistral-large"), package: "@ai-sdk/mistral", options: { name: "mistral" } }, + {}, + ) + expect(result.sdk).toBeDefined() + expect(providers).toEqual(["mistral.chat"]) + }), + ) + + it.effect("matches the old bundled Mistral SDK provider name for custom provider IDs", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const providers: string[] = [] + yield* plugin.add(MistralPlugin) + yield* plugin.add({ + id: PluginV2.ID.make("mistral-sdk-inspector"), + effect: Effect.succeed({ + "aisdk.sdk": (evt) => + Effect.sync(() => { + providers.push(evt.sdk.languageModel("mistral-large").provider) + }), + }), + }) + yield* plugin.trigger( + "aisdk.sdk", + { + model: model("custom-mistral", "mistral-large"), + package: "@ai-sdk/mistral", + options: { name: "custom-mistral" }, + }, + {}, + ) + expect(providers).toEqual(["mistral.chat"]) + }), + ) + + it.effect("leaves Mistral language selection on the default sdk.languageModel(apiID) path", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + const sdk = fakeSelectorSdk(calls) + yield* plugin.add(MistralPlugin) + const result = yield* plugin.trigger( + "aisdk.language", + { model: model("mistral", "alias", { apiID: ModelV2.ID.make("mistral-large") }), sdk, options: {} }, + {}, + ) + const language = result.language ?? sdk.languageModel(result.model.apiID) + expect(calls).toEqual(["languageModel:mistral-large"]) + expect(language).toBeDefined() + }), + ) +}) diff --git a/packages/core/test/plugin/provider-nvidia.test.ts b/packages/core/test/plugin/provider-nvidia.test.ts new file mode 100644 index 00000000000..0ce3d370cba --- /dev/null +++ b/packages/core/test/plugin/provider-nvidia.test.ts @@ -0,0 +1,93 @@ +import { describe, expect } from "bun:test" +import { Effect } from "effect" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { ProviderPlugins } from "@opencode-ai/core/plugin/provider" +import { NvidiaPlugin } from "@opencode-ai/core/plugin/provider/nvidia" +import { expectPluginRegistered, it, provider } from "./provider-helper" + +describe("NvidiaPlugin", () => { + it.effect("is registered so legacy referer headers can be applied", () => + Effect.sync(() => + expectPluginRegistered( + ProviderPlugins.map((item) => item.id), + "nvidia", + ), + ), + ) + + it.effect("applies NVIDIA tracking headers only to nvidia", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(NvidiaPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("nvidia", { + options: { headers: { Existing: "value" }, body: {}, aisdk: { provider: {}, request: {} } }, + }), + cancel: false, + }, + ) + const ignored = yield* plugin.trigger("provider.update", {}, { provider: provider("openrouter"), cancel: false }) + expect(result.provider.options.headers).toEqual({ + Existing: "value", + "HTTP-Referer": "https://kilo.ai/", + "X-Title": "Kilo Code", + "X-BILLING-INVOKE-ORIGIN": "KiloCode", + }) + expect(ignored.provider.options.headers).toEqual({}) + }), + ) + + it.effect("adds billing origin for custom NVIDIA endpoints", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(NvidiaPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("nvidia", { + endpoint: { type: "aisdk", package: "test-provider", url: "http://localhost:8000/v1" }, + options: { headers: {}, body: {}, aisdk: { provider: {}, request: {} } }, + }), + cancel: false, + }, + ) + + expect(result.provider.options.headers).toEqual({ + "HTTP-Referer": "https://kilo.ai/", + "X-Title": "Kilo Code", + "X-BILLING-INVOKE-ORIGIN": "KiloCode", + }) + }), + ) + + it.effect("preserves an explicit NVIDIA billing origin header", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(NvidiaPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("nvidia", { + options: { + headers: { "X-BILLING-INVOKE-ORIGIN": "CustomOrigin" }, + body: {}, + aisdk: { provider: { baseURL: "https://integrate.api.nvidia.com/v1" }, request: {} }, + }, + }), + cancel: false, + }, + ) + + expect(result.provider.options.headers).toEqual({ + "HTTP-Referer": "https://kilo.ai/", + "X-Title": "Kilo Code", + "X-BILLING-INVOKE-ORIGIN": "CustomOrigin", + }) + }), + ) +}) diff --git a/packages/core/test/plugin/provider-openai-compatible.test.ts b/packages/core/test/plugin/provider-openai-compatible.test.ts new file mode 100644 index 00000000000..e8bf1f7575f --- /dev/null +++ b/packages/core/test/plugin/provider-openai-compatible.test.ts @@ -0,0 +1,101 @@ +import { describe, expect } from "bun:test" +import { Effect } from "effect" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { OpenAICompatiblePlugin } from "@opencode-ai/core/plugin/provider/openai-compatible" +import { it, model } from "./provider-helper" + +describe("OpenAICompatiblePlugin", () => { + it.effect("preserves explicit includeUsage false and defaults it to true", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(OpenAICompatiblePlugin) + const defaulted = yield* plugin.trigger( + "aisdk.sdk", + { model: model("custom", "model"), package: "@ai-sdk/openai-compatible", options: { name: "custom" } }, + {}, + ) + const disabled = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("custom", "model"), + package: "@ai-sdk/openai-compatible", + options: { name: "custom", includeUsage: false }, + }, + {}, + ) + expect(defaulted.options.includeUsage).toBe(true) + expect(disabled.options.includeUsage).toBe(false) + }), + ) + + it.effect("defaults includeUsage for OpenAI-compatible package matches", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(OpenAICompatiblePlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("custom", "model"), + package: "file:///tmp/@ai-sdk/openai-compatible-provider.js", + options: { name: "custom" }, + }, + {}, + ) + expect(result.options.includeUsage).toBe(true) + }), + ) + + it.effect("uses the provider ID as the OpenAI-compatible provider name", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const observed: string[] = [] + yield* plugin.add(OpenAICompatiblePlugin) + yield* plugin.add({ + id: PluginV2.ID.make("inspector"), + effect: Effect.succeed({ + "aisdk.sdk": (evt) => + Effect.sync(() => { + observed.push(evt.sdk.languageModel("model").provider) + }), + }), + }) + yield* plugin.trigger( + "aisdk.sdk", + { + model: model("custom-provider", "model"), + package: "@ai-sdk/openai-compatible", + options: { name: "custom-provider", baseURL: "https://example.com/v1" }, + }, + {}, + ) + expect(observed).toEqual(["custom-provider.chat"]) + }), + ) + + it.effect("does not overwrite an SDK created by an earlier provider-specific plugin", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const sentinel = { languageModel: (modelID: string) => ({ modelID }) } + yield* plugin.add({ + id: PluginV2.ID.make("sentinel"), + effect: Effect.succeed({ + "aisdk.sdk": (evt) => + Effect.sync(() => { + evt.sdk = sentinel + }), + }), + }) + yield* plugin.add(OpenAICompatiblePlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("cloudflare-workers-ai", "model"), + package: "@ai-sdk/openai-compatible", + options: { name: "cloudflare-workers-ai" }, + }, + {}, + ) + expect(result.sdk).toBe(sentinel) + }), + ) +}) diff --git a/packages/core/test/plugin/provider-openai.test.ts b/packages/core/test/plugin/provider-openai.test.ts new file mode 100644 index 00000000000..31d6dd0b6d1 --- /dev/null +++ b/packages/core/test/plugin/provider-openai.test.ts @@ -0,0 +1,100 @@ +import { describe, expect } from "bun:test" +import { Effect } from "effect" +import { ModelV2 } from "@opencode-ai/core/model" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { OpenAIPlugin } from "@opencode-ai/core/plugin/provider/openai" +import { fakeSelectorSdk, it, model } from "./provider-helper" + +describe("OpenAIPlugin", () => { + it.effect("creates an OpenAI SDK for @ai-sdk/openai using the provider ID as SDK name", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(OpenAIPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("custom-openai", "gpt-5"), + package: "@ai-sdk/openai", + options: { name: "custom-openai", apiKey: "test" }, + }, + {}, + ) + expect(result.sdk?.responses("gpt-5").provider).toBe("custom-openai.responses") + }), + ) + + it.effect("ignores non-OpenAI SDK packages", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(OpenAIPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { model: model("openai", "gpt-5"), package: "@ai-sdk/openai-compatible", options: { name: "openai" } }, + {}, + ) + expect(result.sdk).toBeUndefined() + }), + ) + + it.effect("uses the Responses API for language models", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(OpenAIPlugin) + const result = yield* plugin.trigger( + "aisdk.language", + { + model: model("openai", "alias", { apiID: ModelV2.ID.make("gpt-5") }), + sdk: fakeSelectorSdk(calls), + options: {}, + }, + {}, + ) + expect(calls).toEqual(["responses:gpt-5"]) + expect(result.language).toBeDefined() + }), + ) + + it.effect("ignores non-OpenAI providers", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(OpenAIPlugin) + const result = yield* plugin.trigger( + "aisdk.language", + { model: model("anthropic", "gpt-5"), sdk: fakeSelectorSdk(calls), options: {} }, + {}, + ) + expect(calls).toEqual([]) + expect(result.language).toBeUndefined() + }), + ) + + it.effect("cancels gpt-5-chat-latest during model updates", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(OpenAIPlugin) + const normal = yield* plugin.trigger("model.update", {}, { model: model("openai", "gpt-5"), cancel: false }) + const filtered = yield* plugin.trigger( + "model.update", + {}, + { model: model("openai", "gpt-5-chat-latest"), cancel: false }, + ) + expect(normal.cancel).toBe(false) + expect(filtered.cancel).toBe(true) + }), + ) + + it.effect("does not cancel gpt-5-chat-latest for non-OpenAI providers", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(OpenAIPlugin) + const result = yield* plugin.trigger( + "model.update", + {}, + { model: model("custom-openai", "gpt-5-chat-latest"), cancel: false }, + ) + expect(result.cancel).toBe(false) + }), + ) +}) diff --git a/packages/core/test/plugin/provider-opencode.test.ts b/packages/core/test/plugin/provider-opencode.test.ts new file mode 100644 index 00000000000..8271a4a5c82 --- /dev/null +++ b/packages/core/test/plugin/provider-opencode.test.ts @@ -0,0 +1,197 @@ +import { describe, expect } from "bun:test" +import { DateTime, Effect, Layer, Option } from "effect" +import { Catalog } from "@opencode-ai/core/catalog" +import { Instance } from "@opencode-ai/core/instance" +import { ModelV2 } from "@opencode-ai/core/model" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { OpencodePlugin } from "@opencode-ai/core/plugin/provider/opencode" +import { ProviderV2 } from "@opencode-ai/core/provider" +import { it, model, provider, withEnv } from "./provider-helper" + +const cost = (input: number, output = 0) => [{ input, output, cache: { read: 0, write: 0 } }] +const instanceLayer = Layer.succeed(Instance.Service, Instance.Service.of({ directory: "test" })) + +describe("OpencodePlugin", () => { + it.effect("uses a public key and cancels paid models without credentials", () => + withEnv({ OPENCODE_API_KEY: undefined }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(OpencodePlugin) + const updated = yield* plugin.trigger("provider.update", {}, { provider: provider("opencode"), cancel: false }) + const paid = yield* plugin.trigger( + "model.update", + {}, + { model: model("opencode", "paid", { cost: cost(1) }), cancel: false }, + ) + expect(updated.provider.options.aisdk.provider.apiKey).toBe("public") + expect(paid.cancel).toBe(true) + }), + ), + ) + + it.effect("keeps free models without credentials", () => + withEnv({ OPENCODE_API_KEY: undefined }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(OpencodePlugin) + yield* plugin.trigger("provider.update", {}, { provider: provider("opencode"), cancel: false }) + const free = yield* plugin.trigger( + "model.update", + {}, + { model: model("opencode", "free", { cost: cost(0) }), cancel: false }, + ) + expect(free.cancel).toBe(false) + }), + ), + ) + + it.effect("treats output-only cost as free without credentials", () => + withEnv({ OPENCODE_API_KEY: undefined }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(OpencodePlugin) + yield* plugin.trigger("provider.update", {}, { provider: provider("opencode"), cancel: false }) + const outputOnly = yield* plugin.trigger( + "model.update", + {}, + { model: model("opencode", "output-only", { cost: cost(0, 1) }), cancel: false }, + ) + expect(outputOnly.cancel).toBe(false) + }), + ), + ) + + it.effect("uses OPENCODE_API_KEY as credentials", () => + withEnv({ OPENCODE_API_KEY: "secret" }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(OpencodePlugin) + const updated = yield* plugin.trigger("provider.update", {}, { provider: provider("opencode"), cancel: false }) + const paid = yield* plugin.trigger( + "model.update", + {}, + { model: model("opencode", "paid", { cost: cost(1) }), cancel: false }, + ) + expect(updated.provider.options.aisdk.provider.apiKey).toBeUndefined() + expect(paid.cancel).toBe(false) + }), + ), + ) + + it.effect("uses configured provider env vars as credentials", () => + withEnv({ OPENCODE_API_KEY: undefined, CUSTOM_OPENCODE_API_KEY: "secret" }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(OpencodePlugin) + const updated = yield* plugin.trigger( + "provider.update", + {}, + { provider: provider("opencode", { env: ["CUSTOM_OPENCODE_API_KEY"] }), cancel: false }, + ) + const paid = yield* plugin.trigger( + "model.update", + {}, + { model: model("opencode", "paid", { cost: cost(1) }), cancel: false }, + ) + expect(updated.provider.options.aisdk.provider.apiKey).toBeUndefined() + expect(paid.cancel).toBe(false) + }), + ), + ) + + it.effect("uses configured apiKey as credentials", () => + withEnv({ OPENCODE_API_KEY: undefined }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(OpencodePlugin) + const updated = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("opencode", { + options: { + headers: {}, + body: {}, + aisdk: { + provider: { apiKey: "configured" }, + request: {}, + }, + }, + }), + cancel: false, + }, + ) + const paid = yield* plugin.trigger( + "model.update", + {}, + { model: model("opencode", "paid", { cost: cost(1) }), cancel: false }, + ) + expect(updated.provider.options.aisdk.provider.apiKey).toBe("configured") + expect(paid.cancel).toBe(false) + }), + ), + ) + + it.effect("uses auth-enabled providers as credentials", () => + withEnv({ OPENCODE_API_KEY: undefined }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(OpencodePlugin) + const updated = yield* plugin.trigger( + "provider.update", + {}, + { provider: provider("opencode", { enabled: { via: "auth", service: "opencode" } }), cancel: false }, + ) + const paid = yield* plugin.trigger( + "model.update", + {}, + { model: model("opencode", "paid", { cost: cost(1) }), cancel: false }, + ) + expect(updated.provider.options.aisdk.provider.apiKey).toBeUndefined() + expect(paid.cancel).toBe(false) + }), + ), + ) + + it.effect("ignores non-opencode providers and models", () => + withEnv({ OPENCODE_API_KEY: undefined }, () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(OpencodePlugin) + const updated = yield* plugin.trigger("provider.update", {}, { provider: provider("openai"), cancel: false }) + const paid = yield* plugin.trigger( + "model.update", + {}, + { model: model("openai", "paid", { cost: cost(1) }), cancel: false }, + ) + expect(updated.provider.options.aisdk.provider.apiKey).toBeUndefined() + expect(paid.cancel).toBe(false) + }), + ), + ) + + it.effect("prefers gpt-5-nano as the opencode small model", () => + Effect.gen(function* () { + const catalog = yield* Catalog.Service + const providerID = ProviderV2.ID.opencode + + yield* catalog.provider.update(providerID, () => {}) + yield* catalog.model.update(providerID, ModelV2.ID.make("cheap-mini"), (model) => { + model.capabilities.input = ["text"] + model.capabilities.output = ["text"] + model.cost = cost(1, 1) + model.time.released = DateTime.makeUnsafe(Date.now()) + }) + yield* catalog.model.update(providerID, ModelV2.ID.make("gpt-5-nano"), (model) => { + model.capabilities.input = ["text"] + model.capabilities.output = ["text"] + model.cost = cost(10, 10) + model.time.released = DateTime.makeUnsafe(Date.now()) + }) + + const selected = yield* catalog.model.small(providerID) + + expect(Option.getOrUndefined(selected)?.id).toBe(ModelV2.ID.make("gpt-5-nano")) + }).pipe(Effect.provide(Catalog.defaultLayer.pipe(Layer.provide(instanceLayer)))), + ) +}) diff --git a/packages/core/test/plugin/provider-openrouter.test.ts b/packages/core/test/plugin/provider-openrouter.test.ts new file mode 100644 index 00000000000..91db253aa01 --- /dev/null +++ b/packages/core/test/plugin/provider-openrouter.test.ts @@ -0,0 +1,105 @@ +import { describe, expect } from "bun:test" +import { Effect } from "effect" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { ProviderPlugins } from "@opencode-ai/core/plugin/provider" +import { OpenRouterPlugin } from "@opencode-ai/core/plugin/provider/openrouter" +import { expectPluginRegistered, it, model, provider } from "./provider-helper" + +describe("OpenRouterPlugin", () => { + it.effect("is registered so legacy OpenRouter behavior can be applied", () => + Effect.sync(() => + expectPluginRegistered( + ProviderPlugins.map((item) => item.id), + "openrouter", + ), + ), + ) + + it.effect("applies legacy referer headers only to openrouter", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(OpenRouterPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("openrouter", { + options: { headers: { Existing: "value" }, body: {}, aisdk: { provider: {}, request: {} } }, + }), + cancel: false, + }, + ) + const ignored = yield* plugin.trigger("provider.update", {}, { provider: provider("nvidia"), cancel: false }) + expect(result.provider.options.headers).toEqual({ + Existing: "value", + "HTTP-Referer": "https://kilo.ai/", + "X-Title": "Kilo Code", + }) + expect(ignored.provider.options.headers).toEqual({}) + }), + ) + + it.effect("creates an SDK only for the OpenRouter package", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(OpenRouterPlugin) + + const ignored = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("openrouter", "openai/gpt-5"), + package: "@ai-sdk/openai-compatible", + options: { name: "openrouter" }, + }, + {}, + ) + expect(ignored.sdk).toBeUndefined() + + const result = yield* plugin.trigger( + "aisdk.sdk", + { model: model("custom", "openai/gpt-5"), package: "@openrouter/ai-sdk-provider", options: { name: "custom" } }, + {}, + ) + expect(result.sdk).toBeDefined() + }), + ) + + it.effect("filters OpenRouter's gpt-5 chat alias", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(OpenRouterPlugin) + const result = yield* plugin.trigger( + "model.update", + {}, + { model: model("openrouter", "openai/gpt-5-chat"), cancel: false }, + ) + const regular = yield* plugin.trigger( + "model.update", + {}, + { model: model("openrouter", "openai/gpt-5"), cancel: false }, + ) + const ignored = yield* plugin.trigger( + "model.update", + {}, + { model: model("openai", "openai/gpt-5-chat"), cancel: false }, + ) + + expect(result.cancel).toBe(true) + expect(regular.cancel).toBe(false) + expect(ignored.cancel).toBe(false) + }), + ) + + it.effect("does not filter gpt-5-chat-latest for non-OpenRouter providers", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(OpenRouterPlugin) + const result = yield* plugin.trigger( + "model.update", + {}, + { model: model("custom-openrouter", "gpt-5-chat-latest"), cancel: false }, + ) + expect(result.cancel).toBe(false) + }), + ) +}) diff --git a/packages/core/test/plugin/provider-perplexity.test.ts b/packages/core/test/plugin/provider-perplexity.test.ts new file mode 100644 index 00000000000..d03f583375d --- /dev/null +++ b/packages/core/test/plugin/provider-perplexity.test.ts @@ -0,0 +1,107 @@ +import { describe, expect } from "bun:test" +import { Effect } from "effect" +import { ModelV2 } from "@opencode-ai/core/model" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { PerplexityPlugin } from "@opencode-ai/core/plugin/provider/perplexity" +import { fakeSelectorSdk, it, model } from "./provider-helper" + +describe("PerplexityPlugin", () => { + it.effect("creates a Perplexity SDK for the exact @ai-sdk/perplexity package", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(PerplexityPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { model: model("perplexity", "sonar"), package: "@ai-sdk/perplexity", options: { name: "perplexity" } }, + {}, + ) + expect(result.sdk).toBeDefined() + }), + ) + + it.effect("ignores packages that are not the bundled Perplexity package", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(PerplexityPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("perplexity", "sonar"), + package: "@ai-sdk/perplexity-compatible", + options: { name: "perplexity" }, + }, + {}, + ) + expect(result.sdk).toBeUndefined() + }), + ) + + it.effect("uses the Perplexity provider ID as the SDK name for the bundled provider", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const providers: string[] = [] + yield* plugin.add(PerplexityPlugin) + yield* plugin.add({ + id: PluginV2.ID.make("perplexity-sdk-inspector"), + effect: Effect.succeed({ + "aisdk.sdk": (evt) => + Effect.sync(() => { + providers.push(evt.sdk.languageModel("sonar").provider) + }), + }), + }) + yield* plugin.trigger( + "aisdk.sdk", + { model: model("perplexity", "sonar"), package: "@ai-sdk/perplexity", options: { name: "perplexity" } }, + {}, + ) + expect(providers).toEqual(["perplexity"]) + }), + ) + + it.effect("creates bundled Perplexity SDKs for custom provider IDs", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const providers: string[] = [] + yield* plugin.add(PerplexityPlugin) + yield* plugin.add({ + id: PluginV2.ID.make("custom-perplexity-sdk-inspector"), + effect: Effect.succeed({ + "aisdk.sdk": (evt) => + Effect.sync(() => { + providers.push(evt.sdk.languageModel("sonar").provider) + }), + }), + }) + yield* plugin.trigger( + "aisdk.sdk", + { + model: model("custom-perplexity", "sonar"), + package: "@ai-sdk/perplexity", + options: { name: "custom-perplexity" }, + }, + {}, + ) + expect(providers).toEqual(["perplexity"]) + }), + ) + + it.effect("leaves Perplexity language selection to the default languageModel fallback", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(PerplexityPlugin) + const result = yield* plugin.trigger( + "aisdk.language", + { + model: model("perplexity", "alias", { apiID: ModelV2.ID.make("sonar") }), + sdk: fakeSelectorSdk(calls), + options: {}, + }, + {}, + ) + expect(calls).toEqual([]) + expect(result.language).toBeUndefined() + }), + ) +}) diff --git a/packages/core/test/plugin/provider-sap-ai-core.test.ts b/packages/core/test/plugin/provider-sap-ai-core.test.ts new file mode 100644 index 00000000000..565b9280ab9 --- /dev/null +++ b/packages/core/test/plugin/provider-sap-ai-core.test.ts @@ -0,0 +1,127 @@ +import { describe, expect } from "bun:test" +import { Effect } from "effect" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { SapAICorePlugin } from "@opencode-ai/core/plugin/provider/sap-ai-core" +import { fixtureProvider, it, model, npmLayer, withEnv } from "./provider-helper" + +const pluginWithNpm = { id: SapAICorePlugin.id, effect: SapAICorePlugin.effect.pipe(Effect.provide(npmLayer)) } + +describe("SapAICorePlugin", () => { + it.effect("copies serviceKey option into AICORE_SERVICE_KEY but keeps SDK options to deployment metadata", () => + withEnv( + { AICORE_SERVICE_KEY: undefined, AICORE_DEPLOYMENT_ID: "deployment", AICORE_RESOURCE_GROUP: "resource-group" }, + () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(pluginWithNpm) + const sdk = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("sap-ai-core", "sap-model"), + package: fixtureProvider, + options: { name: "sap-ai-core", serviceKey: "service-key" }, + }, + {}, + ) + expect(process.env.AICORE_SERVICE_KEY).toBe("service-key") + expect(sdk.sdk.options).toEqual({ deploymentId: "deployment", resourceGroup: "resource-group" }) + }), + ), + ) + + it.effect("preserves existing AICORE_SERVICE_KEY over serviceKey option", () => + withEnv( + { + AICORE_SERVICE_KEY: "env-service-key", + AICORE_DEPLOYMENT_ID: "deployment", + AICORE_RESOURCE_GROUP: "resource-group", + }, + () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(pluginWithNpm) + const sdk = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("sap-ai-core", "sap-model"), + package: fixtureProvider, + options: { name: "sap-ai-core", serviceKey: "option-service-key" }, + }, + {}, + ) + expect(process.env.AICORE_SERVICE_KEY).toBe("env-service-key") + expect(sdk.sdk.options).toEqual({ deploymentId: "deployment", resourceGroup: "resource-group" }) + }), + ), + ) + + it.effect("omits deployment and resourceGroup SDK options when no service key is available", () => + withEnv( + { AICORE_SERVICE_KEY: undefined, AICORE_DEPLOYMENT_ID: "deployment", AICORE_RESOURCE_GROUP: "resource-group" }, + () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(pluginWithNpm) + const sdk = yield* plugin.trigger( + "aisdk.sdk", + { model: model("sap-ai-core", "sap-model"), package: fixtureProvider, options: { name: "sap-ai-core" } }, + {}, + ) + expect(process.env.AICORE_SERVICE_KEY).toBeUndefined() + expect(sdk.sdk.options).toEqual({}) + }), + ), + ) + + it.effect("uses the callable SDK for language selection", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(pluginWithNpm) + const sdk = Object.assign((modelID: string) => ({ modelID, provider: "callable" }), { + languageModel() { + throw new Error("SAP AI Core should call the SDK directly") + }, + }) + const language = yield* plugin.trigger( + "aisdk.language", + { model: model("sap-ai-core", "sap-model"), sdk, options: {} }, + {}, + ) + expect(language.language as unknown).toEqual({ modelID: "sap-model", provider: "callable" }) + }), + ) + + it.effect("ignores non-SAP AI Core providers", () => + withEnv( + { AICORE_SERVICE_KEY: undefined, AICORE_DEPLOYMENT_ID: "deployment", AICORE_RESOURCE_GROUP: "resource-group" }, + () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(pluginWithNpm) + const sdk = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("openai", "sap-model"), + package: fixtureProvider, + options: { name: "openai", serviceKey: "service-key" }, + }, + {}, + ) + const language = yield* plugin.trigger( + "aisdk.language", + { + model: model("openai", "sap-model"), + sdk: () => { + throw new Error("SAP AI Core should ignore other providers") + }, + options: {}, + }, + {}, + ) + expect(process.env.AICORE_SERVICE_KEY).toBeUndefined() + expect(sdk.sdk).toBeUndefined() + expect(language.language).toBeUndefined() + }), + ), + ) +}) diff --git a/packages/core/test/plugin/provider-togetherai.test.ts b/packages/core/test/plugin/provider-togetherai.test.ts new file mode 100644 index 00000000000..65090037bef --- /dev/null +++ b/packages/core/test/plugin/provider-togetherai.test.ts @@ -0,0 +1,97 @@ +import { describe, expect } from "bun:test" +import { Effect } from "effect" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { TogetherAIPlugin } from "@opencode-ai/core/plugin/provider/togetherai" +import { fakeSelectorSdk, it, model } from "./provider-helper" + +describe("TogetherAIPlugin", () => { + it.effect("creates a TogetherAI SDK for @ai-sdk/togetherai", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(TogetherAIPlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { model: model("togetherai", "model"), package: "@ai-sdk/togetherai", options: { name: "togetherai" } }, + {}, + ) + expect(result.sdk).toBeDefined() + }), + ) + + it.effect("matches the old bundled provider package exactly", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(TogetherAIPlugin) + + const ignored = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("togetherai", "model"), + package: "file:///tmp/@ai-sdk/togetherai-provider.js", + options: { name: "togetherai" }, + }, + {}, + ) + expect(ignored.sdk).toBeUndefined() + + const result = yield* plugin.trigger( + "aisdk.sdk", + { model: model("togetherai", "model"), package: "@ai-sdk/togetherai", options: { name: "togetherai" } }, + {}, + ) + expect(result.sdk).toBeDefined() + }), + ) + + it.effect("creates bundled TogetherAI SDKs for custom provider IDs", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const observed: string[] = [] + yield* plugin.add(TogetherAIPlugin) + yield* plugin.add({ + id: PluginV2.ID.make("inspector"), + effect: Effect.succeed({ + "aisdk.sdk": (evt) => + Effect.sync(() => { + observed.push(evt.sdk.languageModel("model").provider) + }), + }), + }) + + yield* plugin.trigger( + "aisdk.sdk", + { + model: model("custom-togetherai", "model"), + package: "@ai-sdk/togetherai", + options: { name: "custom-togetherai" }, + }, + {}, + ) + + expect(observed).toEqual(["togetherai.chat"]) + }), + ) + + it.effect("defaults language selection to sdk.languageModel with the model API ID", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(TogetherAIPlugin) + + const result = yield* plugin.trigger( + "aisdk.language", + { + model: model("togetherai", "meta-llama/Llama-3.3-70B-Instruct-Turbo"), + sdk: { languageModel: fakeSelectorSdk(calls).languageModel }, + options: {}, + }, + {}, + ) + + expect(result.language).toBeUndefined() + expect(calls).toEqual([]) + expect(result.language ?? fakeSelectorSdk(calls).languageModel(result.model.apiID)).toBeDefined() + expect(calls).toEqual(["languageModel:meta-llama/Llama-3.3-70B-Instruct-Turbo"]) + }), + ) +}) diff --git a/packages/core/test/plugin/provider-venice.test.ts b/packages/core/test/plugin/provider-venice.test.ts new file mode 100644 index 00000000000..ff4a922ab1e --- /dev/null +++ b/packages/core/test/plugin/provider-venice.test.ts @@ -0,0 +1,86 @@ +import { describe, expect } from "bun:test" +import { Effect } from "effect" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { VenicePlugin } from "@opencode-ai/core/plugin/provider/venice" +import { fakeSelectorSdk, it, model } from "./provider-helper" + +describe("VenicePlugin", () => { + it.effect("creates a Venice SDK for venice-ai-sdk-provider", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(VenicePlugin) + const result = yield* plugin.trigger( + "aisdk.sdk", + { model: model("venice", "model"), package: "venice-ai-sdk-provider", options: { name: "venice" } }, + {}, + ) + expect(result.sdk).toBeDefined() + }), + ) + + it.effect("uses the model provider ID as the bundled Venice SDK name", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const observed: string[] = [] + yield* plugin.add(VenicePlugin) + yield* plugin.add({ + id: PluginV2.ID.make("inspector"), + effect: Effect.succeed({ + "aisdk.sdk": (evt) => + Effect.sync(() => { + observed.push(evt.sdk.languageModel("model").provider) + }), + }), + }) + const result = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("custom-venice", "model"), + package: "venice-ai-sdk-provider", + options: { name: "custom-venice", apiKey: "test" }, + }, + {}, + ) + expect(result.sdk).toBeDefined() + expect(observed).toEqual(["custom-venice.chat"]) + }), + ) + + it.effect("only handles the bundled venice-ai-sdk-provider package", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(VenicePlugin) + const similar = yield* plugin.trigger( + "aisdk.sdk", + { + model: model("venice", "model"), + package: "file:///tmp/venice-ai-sdk-provider.js", + options: { name: "venice" }, + }, + {}, + ) + const other = yield* plugin.trigger( + "aisdk.sdk", + { model: model("venice", "model"), package: "@ai-sdk/openai-compatible", options: { name: "venice" } }, + {}, + ) + expect(similar.sdk).toBeUndefined() + expect(other.sdk).toBeUndefined() + }), + ) + + it.effect("leaves Venice language selection to the default languageModel fallback", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + yield* plugin.add(VenicePlugin) + const result = yield* plugin.trigger( + "aisdk.language", + { model: model("venice", "alias"), sdk: fakeSelectorSdk(calls), options: {} }, + {}, + ) + expect(calls).toEqual([]) + expect(result.language).toBeUndefined() + }), + ) +}) diff --git a/packages/core/test/plugin/provider-vercel.test.ts b/packages/core/test/plugin/provider-vercel.test.ts new file mode 100644 index 00000000000..53045ca47f3 --- /dev/null +++ b/packages/core/test/plugin/provider-vercel.test.ts @@ -0,0 +1,62 @@ +import { describe, expect } from "bun:test" +import { Effect } from "effect" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { VercelPlugin } from "@opencode-ai/core/plugin/provider/vercel" +import { it, model, provider } from "./provider-helper" + +describe("VercelPlugin", () => { + it.effect("applies legacy lower-case referer headers", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(VercelPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("vercel", { + options: { headers: { Existing: "1" }, body: {}, aisdk: { provider: {}, request: {} } }, + }), + cancel: false, + }, + ) + expect(result.provider.options.headers).toEqual({ + Existing: "1", + "http-referer": "https://kilo.ai/", + "x-title": "Kilo Code", + }) + }), + ) + + it.effect("does not add legacy upper-case referer headers", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(VercelPlugin) + const result = yield* plugin.trigger("provider.update", {}, { provider: provider("vercel"), cancel: false }) + expect(result.provider.options.headers).not.toHaveProperty("HTTP-Referer") + expect(result.provider.options.headers).not.toHaveProperty("X-Title") + }), + ) + + it.effect("creates @ai-sdk/vercel SDKs for custom provider IDs", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(VercelPlugin) + const event = yield* plugin.trigger( + "aisdk.sdk", + { model: model("custom-vercel", "v0-1.0-md"), package: "@ai-sdk/vercel", options: { name: "custom-vercel" } }, + {}, + ) + expect(event.sdk).toBeDefined() + expect(event.sdk.languageModel("v0-1.0-md").provider).toBe("vercel.chat") + }), + ) + + it.effect("ignores non-Vercel providers", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(VercelPlugin) + const result = yield* plugin.trigger("provider.update", {}, { provider: provider("gateway"), cancel: false }) + expect(result.provider.options.headers).toEqual({}) + }), + ) +}) diff --git a/packages/core/test/plugin/provider-xai.test.ts b/packages/core/test/plugin/provider-xai.test.ts new file mode 100644 index 00000000000..63af32dae7d --- /dev/null +++ b/packages/core/test/plugin/provider-xai.test.ts @@ -0,0 +1,115 @@ +import { describe, expect } from "bun:test" +import { Effect } from "effect" +import { ModelV2 } from "@opencode-ai/core/model" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { XAIPlugin } from "@opencode-ai/core/plugin/provider/xai" +import { ProviderV2 } from "@opencode-ai/core/provider" +import { testEffect } from "../lib/effect" +import { fakeSelectorSdk } from "./provider-helper" + +const it = testEffect(PluginV2.defaultLayer) + +const model = new ModelV2.Info({ + ...ModelV2.Info.empty(ProviderV2.ID.make("xai"), ModelV2.ID.make("grok-4")), + apiID: ModelV2.ID.make("grok-4"), + endpoint: { + type: "aisdk", + package: "@ai-sdk/xai", + }, +}) + +describe("XAIPlugin", () => { + it.effect("creates an xAI SDK only for @ai-sdk/xai", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(XAIPlugin) + + const ignored = yield* plugin.trigger( + "aisdk.sdk", + { model, package: "@ai-sdk/openai-compatible", options: {} }, + {}, + ) + + const result = yield* plugin.trigger("aisdk.sdk", { model, package: "@ai-sdk/xai", options: {} }, {}) + + expect(ignored.sdk).toBeUndefined() + expect(typeof result.sdk?.responses).toBe("function") + }), + ) + + it.effect("creates xAI SDKs for custom provider IDs", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const providers: string[] = [] + + yield* plugin.add(XAIPlugin) + yield* plugin.add( + PluginV2.define({ + id: PluginV2.ID.make("xai-sdk-name-observer"), + effect: Effect.gen(function* () { + return { + "aisdk.sdk": Effect.fn(function* (evt) { + if (!evt.sdk) return + providers.push(evt.sdk.responses("grok-4").provider) + }), + } + }), + }), + ) + + yield* plugin.trigger( + "aisdk.sdk", + { + model: new ModelV2.Info({ ...model, providerID: ProviderV2.ID.make("custom-xai") }), + package: "@ai-sdk/xai", + options: {}, + }, + {}, + ) + + expect(providers).toEqual(["xai.responses"]) + }), + ) + + it.effect("uses responses with the model apiID for xAI language models", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + + yield* plugin.add(XAIPlugin) + const result = yield* plugin.trigger( + "aisdk.language", + { + model: new ModelV2.Info({ ...model, id: ModelV2.ID.make("alias"), apiID: ModelV2.ID.make("grok-4") }), + sdk: fakeSelectorSdk(calls), + options: {}, + }, + {}, + ) + + expect(calls).toEqual(["responses:grok-4"]) + expect(result.language).toBeDefined() + }), + ) + + it.effect("ignores non-xAI providers", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + const calls: string[] = [] + + yield* plugin.add(XAIPlugin) + const result = yield* plugin.trigger( + "aisdk.language", + { + model: new ModelV2.Info({ ...model, providerID: ProviderV2.ID.openai }), + sdk: fakeSelectorSdk(calls), + options: {}, + }, + {}, + ) + + expect(calls).toEqual([]) + expect(result.language).toBeUndefined() + }), + ) +}) diff --git a/packages/core/test/plugin/provider-zenmux.test.ts b/packages/core/test/plugin/provider-zenmux.test.ts new file mode 100644 index 00000000000..be90baee926 --- /dev/null +++ b/packages/core/test/plugin/provider-zenmux.test.ts @@ -0,0 +1,103 @@ +import { describe, expect } from "bun:test" +import { Effect } from "effect" +import { PluginV2 } from "@opencode-ai/core/plugin" +import { ProviderPlugins } from "@opencode-ai/core/plugin/provider" +import { ZenmuxPlugin } from "@opencode-ai/core/plugin/provider/zenmux" +import { expectPluginRegistered, it, provider } from "./provider-helper" + +describe("ZenmuxPlugin", () => { + it.effect("is registered so legacy referer headers can be applied", () => + Effect.sync(() => + expectPluginRegistered( + ProviderPlugins.map((item) => item.id), + "zenmux", + ), + ), + ) + + it.effect("applies the exact legacy Zenmux headers", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(ZenmuxPlugin) + const result = yield* plugin.trigger("provider.update", {}, { provider: provider("zenmux"), cancel: false }) + expect(result.provider.options.headers).toEqual({ "HTTP-Referer": "https://kilo.ai/", "X-Title": "Kilo Code" }) + expect(Object.keys(result.provider.options.headers).sort()).toEqual(["HTTP-Referer", "X-Title"]) + expect(result.cancel).toBe(false) + }), + ) + + it.effect("merges legacy Zenmux headers with existing headers", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(ZenmuxPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("zenmux", { + options: { headers: { Existing: "value" }, body: {}, aisdk: { provider: {}, request: {} } }, + }), + cancel: false, + }, + ) + + expect(result.provider.options.headers).toEqual({ + Existing: "value", + "HTTP-Referer": "https://kilo.ai/", + "X-Title": "Kilo Code", + }) + }), + ) + + it.effect("lets configured Zenmux legacy headers override defaults", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(ZenmuxPlugin) + const result = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("zenmux", { + options: { + headers: { "HTTP-Referer": "https://example.com/", "X-Title": "custom-title" }, + body: {}, + aisdk: { provider: {}, request: {} }, + }, + }), + cancel: false, + }, + ) + + expect(result.provider.options.headers).toEqual({ + "HTTP-Referer": "https://example.com/", + "X-Title": "custom-title", + }) + }), + ) + + it.effect("guards legacy Zenmux headers to the exact zenmux provider id", () => + Effect.gen(function* () { + const plugin = yield* PluginV2.Service + yield* plugin.add(ZenmuxPlugin) + const ignored = yield* plugin.trigger( + "provider.update", + {}, + { + provider: provider("openrouter", { + options: { + headers: { "HTTP-Referer": "https://example.com/", "X-Title": "custom-title" }, + body: {}, + aisdk: { provider: {}, request: {} }, + }, + }), + cancel: false, + }, + ) + + expect(ignored.provider.options.headers).toEqual({ + "HTTP-Referer": "https://example.com/", + "X-Title": "custom-title", + }) + }), + ) +}) diff --git a/packages/core/test/process/process.test.ts b/packages/core/test/process/process.test.ts new file mode 100644 index 00000000000..f6a52b7a687 --- /dev/null +++ b/packages/core/test/process/process.test.ts @@ -0,0 +1,292 @@ +import { describe, expect } from "bun:test" +import { realpathSync } from "node:fs" +import { tmpdir } from "node:os" +import { Effect, Exit, Stream } from "effect" +import { ChildProcess } from "effect/unstable/process" +import { AppProcess } from "@opencode-ai/core/process" +import { testEffect } from "../lib/effect" + +const it = testEffect(AppProcess.defaultLayer) + +const NODE = process.execPath +const cmd = (...args: string[]) => ChildProcess.make(NODE, args) + +describe("AppProcess", () => { + describe("run", () => { + it.effect( + "captures stdout and exit code zero", + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const result = yield* svc.run(cmd("-e", "process.stdout.write('hi\\n')")) + expect(result.exitCode).toBe(0) + expect(result.stdout.toString("utf8")).toBe("hi\n") + expect(result.stdoutTruncated).toBe(false) + expect(result.stderrTruncated).toBe(false) + }), + ) + + it.effect( + "non-zero exit returns RunResult; caller can require success", + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const result = yield* svc.run(cmd("-e", "process.exit(1)")) + expect(result.exitCode).toBe(1) + }), + ) + + it.effect( + "requireSuccess fails on non-zero exit", + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const exit = yield* Effect.exit( + svc.run(cmd("-e", "process.exit(1)")).pipe(Effect.flatMap(AppProcess.requireSuccess)), + ) + expect(Exit.isFailure(exit)).toBe(true) + if (Exit.isFailure(exit)) { + const reason = exit.cause.reasons[0] + if (reason && reason._tag === "Fail") { + expect(reason.error).toBeInstanceOf(AppProcess.AppProcessError) + expect((reason.error as AppProcess.AppProcessError).exitCode).toBe(1) + } else { + throw new Error("expected fail reason") + } + } + }), + ) + + it.effect( + "requireSuccess succeeds on exit 0", + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const result = yield* svc.run(cmd("-e", "process.exit(0)")).pipe(Effect.flatMap(AppProcess.requireSuccess)) + expect(result.exitCode).toBe(0) + }), + ) + + it.effect( + "requireExitIn allowlists multiple exit codes", + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const requireZeroOrOne = AppProcess.requireExitIn([0, 1]) + const okZero = yield* svc.run(cmd("-e", "process.exit(0)")).pipe(Effect.flatMap(requireZeroOrOne)) + expect(okZero.exitCode).toBe(0) + const okOne = yield* svc.run(cmd("-e", "process.exit(1)")).pipe(Effect.flatMap(requireZeroOrOne)) + expect(okOne.exitCode).toBe(1) + const exit = yield* Effect.exit(svc.run(cmd("-e", "process.exit(2)")).pipe(Effect.flatMap(requireZeroOrOne))) + expect(Exit.isFailure(exit)).toBe(true) + if (Exit.isFailure(exit)) { + const reason = exit.cause.reasons[0] + if (reason && reason._tag === "Fail") { + expect(reason.error).toBeInstanceOf(AppProcess.AppProcessError) + expect((reason.error as AppProcess.AppProcessError).exitCode).toBe(2) + } + } + }), + ) + + it.effect( + "truncates stdout when maxOutputBytes is set", + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const result = yield* svc.run(cmd("-e", "process.stdout.write('0123456789')"), { maxOutputBytes: 5 }) + expect(result.exitCode).toBe(0) + expect(result.stdoutTruncated).toBe(true) + expect(result.stderrTruncated).toBe(false) + expect(result.stdout.length).toBe(5) + expect(result.stdout.toString("utf8")).toBe("01234") + }), + ) + + it.effect( + "truncates stderr when maxErrorBytes is set", + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const result = yield* svc.run(cmd("-e", "process.stderr.write('0123456789')"), { maxErrorBytes: 5 }) + expect(result.exitCode).toBe(0) + expect(result.stdoutTruncated).toBe(false) + expect(result.stderrTruncated).toBe(true) + expect(result.stderr.length).toBe(5) + expect(result.stderr.toString("utf8")).toBe("01234") + }), + ) + + it.effect( + "result includes command description", + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const result = yield* svc.run(cmd("-e", "process.stdout.write('hi')")) + expect(result.command).toBe(`${NODE} -e process.stdout.write('hi')`) + }), + ) + }) + + describe("inherited platform methods", () => { + it.effect( + "string returns stdout as string", + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const out = yield* svc.string(cmd("-e", "process.stdout.write('hi\\n')")) + expect(out).toBe("hi\n") + }), + ) + + it.effect( + "lines returns the platform's array of lines", + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const out = yield* svc.lines(cmd("-e", "process.stdout.write('a\\nb\\n')")) + expect(Array.from(out)).toEqual(["a", "b"]) + }), + ) + }) + + describe("run with stdin option", () => { + const echoStdin = "process.stdin.on('data', c => process.stdout.write(c))" + + it.effect( + "feeds a string to stdin and returns it on stdout", + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const result = yield* svc.run(cmd("-e", echoStdin), { stdin: "hello" }) + expect(result.exitCode).toBe(0) + expect(result.stdout.toString("utf8")).toBe("hello") + }), + ) + + it.effect( + "feeds a Uint8Array to stdin", + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const bytes = new TextEncoder().encode("bytes") + const result = yield* svc.run(cmd("-e", echoStdin), { stdin: bytes }) + expect(result.exitCode).toBe(0) + expect(result.stdout.toString("utf8")).toBe("bytes") + }), + ) + + it.effect( + "feeds a Stream of Uint8Array chunks to stdin", + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const enc = new TextEncoder() + const stream = Stream.fromIterable([enc.encode("one"), enc.encode("-two"), enc.encode("-three")]) + const result = yield* svc.run(cmd("-e", echoStdin), { stdin: stream }) + expect(result.exitCode).toBe(0) + expect(result.stdout.toString("utf8")).toBe("one-two-three") + }), + ) + + it.effect( + "completes correctly with empty input", + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const result = yield* svc.run(cmd("-e", echoStdin), { stdin: "" }) + expect(result.exitCode).toBe(0) + expect(result.stdout.toString("utf8")).toBe("") + }), + ) + + it.effect( + "carries existing Command options like env", + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const script = + "process.stdout.write(process.env.FEED + ':'); process.stdin.on('data', c => process.stdout.write(c))" + const command = ChildProcess.make(NODE, ["-e", script], { env: { FEED: "envset" }, extendEnv: true }) + const result = yield* svc.run(command, { stdin: "payload" }) + expect(result.exitCode).toBe(0) + expect(result.stdout.toString("utf8")).toBe("envset:payload") + }), + ) + + it.effect( + "carries existing Command options like cwd", + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const dir = realpathSync(tmpdir()) + const script = + "process.stdout.write(process.cwd() + '|'); process.stdin.on('data', c => process.stdout.write(c))" + const command = ChildProcess.make(NODE, ["-e", script], { cwd: dir }) + const result = yield* svc.run(command, { stdin: "ok" }) + expect(result.exitCode).toBe(0) + const [cwd, stdin] = result.stdout.toString("utf8").split("|") + expect(realpathSync(cwd)).toBe(dir) + expect(stdin).toBe("ok") + }), + ) + }) + + describe("runStream", () => { + it.live( + "emits lines incrementally and ends cleanly on exit 0", + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const result = yield* svc + .runStream(cmd("-e", "console.log('one'); console.log('two'); console.log('three')")) + .pipe(Stream.runCollect) + expect(Array.from(result)).toEqual(["one", "two", "three"]) + }), + ) + + it.live( + "okExitCodes determines whether a non-zero exit fails the stream", + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const allowed = yield* svc + .runStream(cmd("-e", "console.log('only'); process.exit(1)"), { okExitCodes: [0, 1] }) + .pipe(Stream.runCollect) + expect(Array.from(allowed)).toEqual(["only"]) + const exit = yield* Effect.exit( + svc + .runStream(cmd("-e", "console.log('a'); process.exit(2)"), { okExitCodes: [0, 1] }) + .pipe(Stream.runCollect), + ) + expect(Exit.isFailure(exit)).toBe(true) + if (Exit.isFailure(exit)) { + const reason = exit.cause.reasons[0] + if (reason && reason._tag === "Fail") { + expect(reason.error).toBeInstanceOf(AppProcess.AppProcessError) + } + } + }), + ) + + it.live( + "without okExitCodes, never fails on exit code", + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const result = yield* svc.runStream(cmd("-e", "console.log('only'); process.exit(7)")).pipe(Stream.runCollect) + expect(Array.from(result)).toEqual(["only"]) + }), + ) + + it.live( + "AbortSignal interrupts the stream", + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const controller = new AbortController() + controller.abort() + const exit = yield* Effect.exit( + svc + .runStream(cmd("-e", "setInterval(() => {}, 60_000)"), { signal: controller.signal }) + .pipe(Stream.runCollect), + ) + expect(Exit.isFailure(exit)).toBe(true) + }), + ) + }) + + describe("spawn (inherited)", () => { + it.live( + "returns the platform ChildProcessHandle for advanced use", + Effect.scoped( + Effect.gen(function* () { + const svc = yield* AppProcess.Service + const handle = yield* svc.spawn(cmd("-e", "setInterval(() => {}, 1_000)")) + expect(yield* handle.isRunning).toBe(true) + yield* handle.kill() + }), + ), + ) + }) +}) diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 4bb417f5b41..25530eb0e6f 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -3,6 +3,7 @@ "extends": "@tsconfig/bun/tsconfig.json", "compilerOptions": { "noUncheckedIndexedAccess": false, - "types": ["bun"] + "types": ["bun"], + "lib": ["ESNext", "DOM", "DOM.Iterable"] } } diff --git a/packages/http-recorder/README.md b/packages/http-recorder/README.md index f6aaed4358d..f527dd0cca0 100644 --- a/packages/http-recorder/README.md +++ b/packages/http-recorder/README.md @@ -70,19 +70,15 @@ Cassettes are normal source files — review them, diff them, commit them. ## Request matching -By default, requests match on canonicalized method, URL, headers, and JSON -body (object keys sorted). Two dispatch strategies are available: +Replay walks the cassette in record order via an internal cursor: the Nth +request executed at runtime is served by the Nth recorded interaction, and +each one is validated as the cursor advances. Request equality is computed +on canonicalized method, URL, headers, and JSON body (object keys sorted). -- **`match`** (default) — find the first recorded interaction whose request - matches the incoming request. Same request twice returns the same response. -- **`sequential`** — return interactions in the order they were recorded, - validating each one matches as the cursor advances. Use for ordered flows - where the same URL is hit multiple times with meaningful state changes - (pagination, retries, polling). - -```ts -HttpRecorder.cassetteLayer("flow/poll-until-done", { dispatch: "sequential" }) -``` +This is deliberately strict — content-based dispatch was removed because +it silently returns the first recorded response for repeated identical +requests, masking state changes that retry/polling/cache-hit tests need to +observe. If you reorder requests in a test, re-record the cassette. Supply your own matcher via `match: (incoming, recorded) => boolean` for custom equivalence (e.g. ignoring a timestamp field in the body). @@ -170,11 +166,11 @@ import { Effect } from "effect" const audit = Effect.gen(function* () { const cassettes = yield* HttpRecorder.Cassette.Service - const entries = yield* cassettes.list() - const issues = yield* Effect.forEach(entries, (entry) => + const names = yield* cassettes.list() + const issues = yield* Effect.forEach(names, (name) => cassettes - .read(entry.name) - .pipe(Effect.map((interactions) => ({ name: entry.name, findings: HttpRecorder.secretFindings(interactions) }))), + .read(name) + .pipe(Effect.map((interactions) => ({ name, findings: HttpRecorder.secretFindings(interactions) }))), ) return issues.filter((i) => i.findings.length > 0) }) @@ -194,21 +190,19 @@ type RecordReplayOptions = { directory?: string // default: /test/fixtures/recordings metadata?: Record // merged into cassette.metadata redactor?: Redactor // default: Redactor.defaults() - dispatch?: "match" | "sequential" // default: "match" match?: (incoming, recorded) => boolean // custom matcher } ``` ## Layout -| File | Purpose | -| -------------- | -------------------------------------------------------------------------------- | -| `effect.ts` | `cassetteLayer` / `recordingLayer` — the `HttpClient` adapter. | -| `websocket.ts` | `makeWebSocketExecutor` — WebSocket record/replay. | -| `cassette.ts` | `Cassette.Service` — reads/writes cassette files, accumulates state. | -| `recorder.ts` | Shared transport plumbing: `UnsafeCassetteError`, `appendOrFail`, `ReplayState`. | -| `redactor.ts` | Composable `Redactor` — headers, url, body redaction. | -| `redaction.ts` | Lower-level header/URL primitives + secret pattern detection. | -| `schema.ts` | Effect Schema definitions for the cassette JSON format. | -| `storage.ts` | Path resolution, JSON encode/decode, sync existence check. | -| `matching.ts` | Request matcher, canonicalization, dispatch strategies, mismatch diagnostics. | +| File | Purpose | +| -------------- | --------------------------------------------------------------------------- | +| `effect.ts` | `cassetteLayer` / `recordingLayer` — the `HttpClient` adapter. | +| `websocket.ts` | `makeWebSocketExecutor` — WebSocket record/replay. | +| `cassette.ts` | `Cassette.Service` — `fileSystem` / `memory` adapters, error types. | +| `recorder.ts` | Shared transport plumbing: `resolveAutoMode`, `ReplayState`. | +| `redactor.ts` | Composable `Redactor` — headers, url, body redaction. | +| `redaction.ts` | Lower-level header/URL primitives + secret pattern detection. | +| `schema.ts` | Effect Schema definitions for the cassette JSON format. | +| `matching.ts` | Request matcher, canonicalization, sequential cursor, mismatch diagnostics. | diff --git a/packages/http-recorder/src/cassette.ts b/packages/http-recorder/src/cassette.ts index 3897f0222c4..331f3d4b600 100644 --- a/packages/http-recorder/src/cassette.ts +++ b/packages/http-recorder/src/cassette.ts @@ -1,7 +1,7 @@ import { Context, Effect, FileSystem, Layer, Schema } from "effect" import * as fs from "node:fs" import * as path from "node:path" -import { secretFindings, type SecretFinding } from "./redaction" +import { secretFindings, SecretFindingSchema, type SecretFinding } from "./redaction" import { decodeCassette, encodeCassette, type Cassette, type CassetteMetadata, type Interaction } from "./schema" const DEFAULT_RECORDINGS_DIR = path.resolve(process.cwd(), "test", "fixtures", "recordings") @@ -14,13 +14,24 @@ export class CassetteNotFoundError extends Schema.TaggedErrorClass +export class UnsafeCassetteError extends Schema.TaggedErrorClass()("UnsafeCassetteError", { + cassetteName: Schema.String, + findings: Schema.Array(SecretFindingSchema), +}) { + override get message() { + return `Refusing to write cassette "${this.cassetteName}" because it contains possible secrets: ${this.findings + .map((finding) => `${finding.path} (${finding.reason})`) + .join(", ")}` + } } export interface Interface { readonly read: (name: string) => Effect.Effect, CassetteNotFoundError> - readonly append: (name: string, interaction: Interaction, metadata?: CassetteMetadata) => Effect.Effect + readonly append: ( + name: string, + interaction: Interaction, + metadata?: CassetteMetadata, + ) => Effect.Effect readonly exists: (name: string) => Effect.Effect readonly list: () => Effect.Effect> } @@ -44,6 +55,9 @@ const formatCassette = (cassette: Cassette) => `${JSON.stringify(encodeCassette( const parseCassette = (raw: string) => decodeCassette(JSON.parse(raw)) +const failIfUnsafe = (name: string, findings: ReadonlyArray) => + findings.length === 0 ? Effect.void : Effect.fail(new UnsafeCassetteError({ cassetteName: name, findings })) + export const fileSystem = ( options: { readonly directory?: string } = {}, ): Layer.Layer => @@ -92,11 +106,9 @@ export const fileSystem = ( entry.findings.push(...secretFindings(interaction)) const cassette = buildCassette(name, entry.interactions, metadata) const findings = [...entry.findings, ...secretFindings(cassette.metadata ?? {})] - if (findings.length === 0) { - yield* ensureDirectory(name) - yield* fs.writeFileString(cassettePath(name), formatCassette(cassette)).pipe(Effect.orDie) - } - return { findings } + yield* failIfUnsafe(name, findings) + yield* ensureDirectory(name) + yield* fs.writeFileString(cassettePath(name), formatCassette(cassette)).pipe(Effect.orDie) }), exists: (name) => fs.access(cassettePath(name)).pipe( @@ -133,17 +145,17 @@ export const memory = (initial: Record> = {}) stored.has(name) ? Effect.succeed(stored.get(name) ?? []) : Effect.fail(new CassetteNotFoundError({ cassetteName: name })), - append: (name, interaction, metadata) => - Effect.sync(() => { - const existing = stored.get(name) - if (existing) existing.push(interaction) - else stored.set(name, [interaction]) - const findings = accumulatedFindings.get(name) - if (findings) findings.push(...secretFindings(interaction)) - else accumulatedFindings.set(name, [...secretFindings(interaction)]) - if (metadata) accumulatedFindings.get(name)!.push(...secretFindings({ name, ...metadata })) - return { findings: accumulatedFindings.get(name) ?? [] } - }), + append: (name, interaction, metadata) => { + const existing = stored.get(name) + if (existing) existing.push(interaction) + else stored.set(name, [interaction]) + const existingFindings = accumulatedFindings.get(name) + const findings = existingFindings ?? [] + if (!existingFindings) accumulatedFindings.set(name, findings) + findings.push(...secretFindings(interaction)) + if (metadata) findings.push(...secretFindings({ name, ...metadata })) + return failIfUnsafe(name, findings) + }, exists: (name) => Effect.sync(() => stored.has(name)), list: () => Effect.sync(() => Array.from(stored.keys()).toSorted()), }) diff --git a/packages/http-recorder/src/effect.ts b/packages/http-recorder/src/effect.ts index e6c3ccbc154..e9cb60cc2e2 100644 --- a/packages/http-recorder/src/effect.ts +++ b/packages/http-recorder/src/effect.ts @@ -11,8 +11,8 @@ import { UrlParams, } from "effect/unstable/http" import * as CassetteService from "./cassette" -import { defaultMatcher, selectMatch, selectSequential, type RequestMatcher } from "./matching" -import { appendOrFail, makeReplayState, resolveAutoMode } from "./recorder" +import { defaultMatcher, selectSequential, type RequestMatcher } from "./matching" +import { makeReplayState, resolveAutoMode } from "./recorder" import { defaults, type Redactor } from "./redactor" import { redactUrl } from "./redaction" import { httpInteractions, type CassetteMetadata, type HttpInteraction, type ResponseSnapshot } from "./schema" @@ -24,7 +24,6 @@ export interface RecordReplayOptions { readonly directory?: string readonly metadata?: CassetteMetadata readonly redactor?: Redactor - readonly dispatch?: "match" | "sequential" readonly match?: RequestMatcher } @@ -71,7 +70,6 @@ export const recordingLayer = ( const match = options.match ?? defaultMatcher const requested = options.mode ?? "auto" const mode = requested === "auto" ? yield* resolveAutoMode(cassetteService, name) : requested - const sequential = options.dispatch === "sequential" const replay = yield* makeReplayState(cassetteService, name, httpInteractions) const snapshotRequest = (request: HttpClientRequest.HttpClientRequest) => @@ -102,9 +100,11 @@ export const recordingLayer = ( ...captured, }), } - yield* appendOrFail(cassetteService, name, interaction, options.metadata).pipe( - Effect.catchTag("UnsafeCassetteError", (error) => Effect.fail(transportError(request, error.message))), - ) + yield* cassetteService + .append(name, interaction, options.metadata) + .pipe( + Effect.catchTag("UnsafeCassetteError", (error) => Effect.fail(transportError(request, error.message))), + ) return HttpClientResponse.fromWeb( request, new Response(decodeResponseBody(interaction.response), interaction.response), @@ -119,14 +119,12 @@ export const recordingLayer = ( transportError(request, `Fixture "${name}" not found. Run locally to record it (CI=true forces replay).`), ), ) - const result = sequential - ? selectSequential(interactions, incoming, match, yield* replay.cursor) - : selectMatch(interactions, incoming, match) + const result = selectSequential(interactions, incoming, match, yield* replay.cursor) if (!result.interaction) return yield* Effect.fail( transportError(request, `Fixture "${name}" does not match the current request: ${result.detail}.`), ) - if (sequential) yield* replay.advance + yield* replay.advance return HttpClientResponse.fromWeb( request, new Response(decodeResponseBody(result.interaction.response), result.interaction.response), diff --git a/packages/http-recorder/src/index.ts b/packages/http-recorder/src/index.ts index 4b47e4513d4..d05098996ec 100644 --- a/packages/http-recorder/src/index.ts +++ b/packages/http-recorder/src/index.ts @@ -7,10 +7,9 @@ export type { WebSocketFrame, WebSocketInteraction, } from "./schema" -export { CassetteNotFoundError, hasCassetteSync } from "./cassette" +export { CassetteNotFoundError, hasCassetteSync, UnsafeCassetteError } from "./cassette" export { defaultMatcher, type RequestMatcher } from "./matching" export { redactHeaders, redactUrl, secretFindings, type SecretFinding } from "./redaction" -export { UnsafeCassetteError } from "./recorder" export { cassetteLayer, recordingLayer, type RecordReplayMode, type RecordReplayOptions } from "./effect" export { makeWebSocketExecutor, diff --git a/packages/http-recorder/src/matching.ts b/packages/http-recorder/src/matching.ts index 9af85a2f3ad..1dced17d071 100644 --- a/packages/http-recorder/src/matching.ts +++ b/packages/http-recorder/src/matching.ts @@ -36,7 +36,7 @@ export const canonicalSnapshot = (snapshot: RequestSnapshot): string => export const defaultMatcher: RequestMatcher = (incoming, recorded) => canonicalSnapshot(incoming) === canonicalSnapshot(recorded) -const safeText = (value: unknown) => { +export const safeText = (value: unknown) => { if (value === undefined) return "undefined" if (secretFindings(value).length > 0) return JSON.stringify(REDACTED) const text = JSON.stringify(value) @@ -92,24 +92,6 @@ export const requestDiff = (expected: RequestSnapshot, received: RequestSnapshot return lines } -export const mismatchDetail = (interactions: ReadonlyArray, incoming: RequestSnapshot): string => { - if (interactions.length === 0) return "cassette has no recorded HTTP interactions" - const ranked = interactions - .map((interaction, index) => ({ index, lines: requestDiff(interaction.request, incoming) })) - .toSorted((a, b) => a.lines.length - b.lines.length || a.index - b.index) - const best = ranked[0] - return ["no recorded interaction matched", `closest interaction: #${best.index + 1}`, ...best.lines].join("\n") -} - -export const selectMatch = ( - interactions: ReadonlyArray, - incoming: RequestSnapshot, - match: RequestMatcher, -): { readonly interaction: HttpInteraction | undefined; readonly detail: string } => { - const interaction = interactions.find((candidate) => match(incoming, candidate.request)) - return { interaction, detail: interaction ? "" : mismatchDetail(interactions, incoming) } -} - export const selectSequential = ( interactions: ReadonlyArray, incoming: RequestSnapshot, diff --git a/packages/http-recorder/src/recorder.ts b/packages/http-recorder/src/recorder.ts index 460b427c2a0..0a13282c2d7 100644 --- a/packages/http-recorder/src/recorder.ts +++ b/packages/http-recorder/src/recorder.ts @@ -1,47 +1,22 @@ -import { Effect, Ref, Schema, Scope } from "effect" +import { Effect, Ref, Scope } from "effect" import type * as CassetteService from "./cassette" import type { CassetteNotFoundError } from "./cassette" -import { SecretFindingSchema } from "./redaction" -import type { CassetteMetadata, Interaction } from "./schema" - -export class UnsafeCassetteError extends Schema.TaggedErrorClass()("UnsafeCassetteError", { - cassetteName: Schema.String, - findings: Schema.Array(SecretFindingSchema), -}) { - override get message() { - return `Refusing to write cassette "${this.cassetteName}" because it contains possible secrets: ${this.findings - .map((finding) => `${finding.path} (${finding.reason})`) - .join(", ")}` - } -} - -export type ResolvedMode = "record" | "replay" | "passthrough" +import type { Interaction } from "./schema" const isCI = () => { const value = process.env.CI return value !== undefined && value !== "" && value !== "false" && value !== "0" } -export const resolveAutoMode = (cassette: CassetteService.Interface, name: string): Effect.Effect => +export const resolveAutoMode = ( + cassette: CassetteService.Interface, + name: string, +): Effect.Effect<"record" | "replay" | "passthrough"> => Effect.gen(function* () { if (isCI()) return "replay" return (yield* cassette.exists(name)) ? "replay" : "record" }) -export const appendOrFail = ( - cassette: CassetteService.Interface, - name: string, - interaction: Interaction, - metadata: CassetteMetadata | undefined, -): Effect.Effect => - cassette - .append(name, interaction, metadata) - .pipe( - Effect.flatMap(({ findings }) => - findings.length === 0 ? Effect.void : Effect.fail(new UnsafeCassetteError({ cassetteName: name, findings })), - ), - ) - export interface ReplayState { readonly load: Effect.Effect, CassetteNotFoundError> readonly cursor: Effect.Effect diff --git a/packages/http-recorder/src/redaction.ts b/packages/http-recorder/src/redaction.ts index b6aa8b3b87b..9d89fff2f5f 100644 --- a/packages/http-recorder/src/redaction.ts +++ b/packages/http-recorder/src/redaction.ts @@ -1,3 +1,5 @@ +import { Schema } from "effect" + export const REDACTED = "[REDACTED]" const DEFAULT_REDACT_HEADERS = [ @@ -95,8 +97,6 @@ export const redactHeaders = ( ) } -import { Schema } from "effect" - export const SecretFindingSchema = Schema.Struct({ path: Schema.String, reason: Schema.String, diff --git a/packages/http-recorder/src/websocket.ts b/packages/http-recorder/src/websocket.ts index f7529b48880..2cccc6749c7 100644 --- a/packages/http-recorder/src/websocket.ts +++ b/packages/http-recorder/src/websocket.ts @@ -1,9 +1,10 @@ import { Effect, Option, Ref, Scope, Stream } from "effect" import type { Headers } from "effect/unstable/http" import * as CassetteService from "./cassette" -import { canonicalizeJson, decodeJson } from "./matching" -import { appendOrFail, makeReplayState, resolveAutoMode } from "./recorder" +import { canonicalizeJson, decodeJson, safeText } from "./matching" +import { makeReplayState, resolveAutoMode } from "./recorder" import type { RecordReplayMode } from "./effect" +import { redactUrl } from "./redaction" import { defaults, type Redactor } from "./redactor" import { webSocketInteractions, type CassetteMetadata, type WebSocketFrame } from "./schema" @@ -53,7 +54,7 @@ const decodeFrameText = (frame: WebSocketFrame) => const assertEqual = (message: string, actual: unknown, expected: unknown) => Effect.sync(() => { if (JSON.stringify(actual) === JSON.stringify(expected)) return - throw new Error(`${message}: expected ${JSON.stringify(expected)}, received ${JSON.stringify(actual)}`) + throw new Error(`${message}: expected ${safeText(expected)}, received ${safeText(actual)}`) }) const jsonOrText = (value: string) => Option.match(decodeJson(value), { onNone: () => value, onSome: canonicalizeJson }) @@ -61,7 +62,7 @@ const jsonOrText = (value: string) => Option.match(decodeJson(value), { onNone: const compareClientMessage = (actual: string, expected: WebSocketFrame | undefined, index: number, asJson: boolean) => { if (!expected) return Effect.sync(() => { - throw new Error(`Unexpected WebSocket client frame ${index + 1}: ${actual}`) + throw new Error(`Unexpected WebSocket client frame ${index + 1}: ${safeText(actual)}`) }) const expectedText = decodeFrameText(expected) if (!asJson) return assertEqual(`WebSocket client frame ${index + 1}`, actual, expectedText) @@ -98,12 +99,13 @@ export const makeWebSocketExecutor = ( const closeOnce = Effect.gen(function* () { if (yield* Ref.getAndSet(closed, true)) return yield* connection.close - yield* appendOrFail( - options.cassette, - options.name, - { transport: "websocket", open: openSnapshot(request), client, server }, - options.metadata, - ).pipe(Effect.orDie) + yield* options.cassette + .append( + options.name, + { transport: "websocket", open: openSnapshot(request), client, server }, + options.metadata, + ) + .pipe(Effect.orDie) }) return { sendText: (message) => @@ -111,10 +113,7 @@ export const makeWebSocketExecutor = ( .sendText(message) .pipe(Effect.tap(() => Effect.sync(() => client.push(encodeFrame(message))))), messages: connection.messages.pipe( - Stream.map((message) => { - server.push(encodeFrame(message)) - return message - }), + Stream.tap((message) => Effect.sync(() => server.push(encodeFrame(message)))), ), close: closeOnce, } @@ -130,20 +129,22 @@ export const makeWebSocketExecutor = ( const interactions = yield* replay.load.pipe(Effect.orDie) const index = yield* replay.cursor const interaction = interactions[index] - if (!interaction) return yield* Effect.die(new Error(`No recorded WebSocket interaction for ${request.url}`)) + if (!interaction) + return yield* Effect.die(new Error(`No recorded WebSocket interaction for ${redactUrl(request.url)}`)) yield* replay.advance yield* assertEqual(`WebSocket open frame ${index + 1}`, openSnapshot(request), interaction.open) const messageIndex = yield* Ref.make(0) return { sendText: (message) => Effect.gen(function* () { - const current = yield* Ref.getAndUpdate(messageIndex, (value) => value + 1) + const current = yield* Ref.get(messageIndex) yield* compareClientMessage( message, interaction.client[current], current, options.compareClientMessagesAsJson === true, ) + yield* Ref.update(messageIndex, (value) => value + 1) }), messages: Stream.fromIterable(interaction.server).pipe(Stream.map(decodeFrameMessage)), close: Effect.gen(function* () { diff --git a/packages/http-recorder/sst-env.d.ts b/packages/http-recorder/sst-env.d.ts index 64441936d7a..f25b9714550 100644 --- a/packages/http-recorder/sst-env.d.ts +++ b/packages/http-recorder/sst-env.d.ts @@ -7,4 +7,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/http-recorder/test/record-replay.test.ts b/packages/http-recorder/test/record-replay.test.ts index 7613563fd05..72512868ee6 100644 --- a/packages/http-recorder/test/record-replay.test.ts +++ b/packages/http-recorder/test/record-replay.test.ts @@ -230,41 +230,19 @@ describe("http-recorder", () => { ) }) - test("default matcher dispatches multi-interaction cassettes by request shape", async () => { - await run( - Effect.gen(function* () { - expect(yield* post("https://example.test/echo", { step: 2 })).toBe('{"reply":"second"}') - expect(yield* post("https://example.test/echo", { step: 1 })).toBe('{"reply":"first"}') - }), - ) - }) - - test("sequential dispatch returns recorded responses in order for identical requests", async () => { - await runWith( - "record-replay/retry", - { dispatch: "sequential" }, - Effect.gen(function* () { - expect(yield* post("https://example.test/poll", { id: "job_1" })).toBe('{"status":"pending"}') - expect(yield* post("https://example.test/poll", { id: "job_1" })).toBe('{"status":"complete"}') - }), - ) - }) - - test("default matcher returns the first match for identical requests", async () => { + test("replay returns recorded responses in order for identical requests", async () => { await runWith( "record-replay/retry", {}, Effect.gen(function* () { expect(yield* post("https://example.test/poll", { id: "job_1" })).toBe('{"status":"pending"}') - expect(yield* post("https://example.test/poll", { id: "job_1" })).toBe('{"status":"pending"}') + expect(yield* post("https://example.test/poll", { id: "job_1" })).toBe('{"status":"complete"}') }), ) }) - test("sequential dispatch reports cursor exhaustion when more requests are made than recorded", async () => { - await runWith( - "record-replay/multi-step", - { dispatch: "sequential" }, + test("replay reports cursor exhaustion when more requests are made than recorded", async () => { + await run( Effect.gen(function* () { yield* post("https://example.test/echo", { step: 1 }) yield* post("https://example.test/echo", { step: 2 }) @@ -274,10 +252,8 @@ describe("http-recorder", () => { ) }) - test("sequential dispatch still validates each recorded request", async () => { - await runWith( - "record-replay/multi-step", - { dispatch: "sequential" }, + test("replay validates each recorded request in order", async () => { + await run( Effect.gen(function* () { yield* post("https://example.test/echo", { step: 1 }) const exit = yield* Effect.exit(post("https://example.test/echo", { step: 3 })) @@ -331,14 +307,13 @@ describe("http-recorder", () => { } }) - test("mismatch diagnostics show closest redacted request differences", async () => { + test("mismatch diagnostics show redacted request differences against the expected interaction", async () => { await run( Effect.gen(function* () { const exit = yield* Effect.exit( post("https://example.test/echo?api_key=secret-value", { step: 3, token: "sk-123456789012345678901234" }), ) const message = failureText(exit) - expect(message).toContain("closest interaction: #1") expect(message).toContain("url:") expect(message).toContain("https://example.test/echo?api_key=%5BREDACTED%5D") expect(message).toContain("body:") @@ -348,4 +323,118 @@ describe("http-recorder", () => { }), ) }) + + test("auto mode records to disk when the cassette is missing", async () => { + const directory = fs.mkdtempSync(path.join(os.tmpdir(), "http-recorder-auto-record-")) + using server = Bun.serve({ + port: 0, + fetch: () => new Response('{"reply":"recorded"}', { headers: { "content-type": "application/json" } }), + }) + const url = `http://127.0.0.1:${server.port}/echo` + // CI=true forces replay; clear it so we exercise the local-dev auto-record path. + const previous = process.env.CI + delete process.env.CI + try { + const result = await runWith("auto-record", { directory, mode: "auto" }, post(url, { step: 1 })) + expect(result).toBe('{"reply":"recorded"}') + expect(fs.existsSync(path.join(directory, "auto-record.json"))).toBe(true) + } finally { + if (previous !== undefined) process.env.CI = previous + } + }) + + test("passthrough mode bypasses the recorder entirely", async () => { + using server = Bun.serve({ port: 0, fetch: () => new Response("from-upstream") }) + const url = `http://127.0.0.1:${server.port}/path` + const directory = fs.mkdtempSync(path.join(os.tmpdir(), "http-recorder-passthrough-")) + + const result = await runWith("passthrough-noop", { directory, mode: "passthrough" }, post(url, {})) + expect(result).toBe("from-upstream") + expect(fs.existsSync(path.join(directory, "passthrough-noop.json"))).toBe(false) + }) + + test("UnsafeCassetteError fails the request when a recording would write a known secret", async () => { + using server = Bun.serve({ port: 0, fetch: () => new Response("Bearer abcdefghijklmnopqrstuvwxyz1234") }) + const url = `http://127.0.0.1:${server.port}/leaky` + const directory = fs.mkdtempSync(path.join(os.tmpdir(), "http-recorder-unsafe-")) + + const exit = await Effect.runPromise( + Effect.exit( + post(url, { ok: true }).pipe( + Effect.provide(HttpRecorder.cassetteLayer("unsafe-record", { directory, mode: "record" })), + ), + ), + ) + expect(Exit.isFailure(exit)).toBe(true) + expect(failureText(exit)).toContain("contains possible secrets") + expect(fs.existsSync(path.join(directory, "unsafe-record.json"))).toBe(false) + }) + + test("Cassette.list enumerates recorded cassette names", async () => { + const directory = fs.mkdtempSync(path.join(os.tmpdir(), "http-recorder-list-")) + await seedCassetteDirectory(directory, "alpha/one", [ + { + transport: "http", + request: { method: "GET", url: "https://x.test/a", headers: {}, body: "" }, + response: { status: 200, headers: {}, body: "a" }, + }, + ]) + await seedCassetteDirectory(directory, "beta", [ + { + transport: "http", + request: { method: "GET", url: "https://x.test/b", headers: {}, body: "" }, + response: { status: 200, headers: {}, body: "b" }, + }, + ]) + + const names = await Effect.runPromise( + Effect.gen(function* () { + const cassette = yield* HttpRecorder.Cassette.Service + return yield* cassette.list() + }).pipe(Effect.provide(HttpRecorder.Cassette.fileSystem({ directory })), Effect.provide(NodeFileSystem.layer)), + ) + expect(names).toEqual(["alpha/one", "beta"]) + }) + + test("WebSocket replay decodes binary frames recorded as base64", async () => { + const binaryServer = new Uint8Array([1, 2, 3, 4]) + await Effect.runPromise( + Effect.scoped( + Effect.gen(function* () { + const cassette = yield* HttpRecorder.Cassette.Service + const executor = yield* HttpRecorder.makeWebSocketExecutor({ + name: "ws/binary", + cassette, + live: { open: () => Effect.die(new Error("unexpected live WebSocket open")) }, + }) + const connection = yield* executor.open({ + url: "wss://example.test/binary", + headers: Headers.fromInput({}), + }) + const messages: Array = [] + yield* connection.messages.pipe(Stream.runForEach((m) => Effect.sync(() => messages.push(m)))) + yield* connection.close + + expect(messages).toHaveLength(1) + expect(messages[0]).toBeInstanceOf(Uint8Array) + expect(Array.from(messages[0] as Uint8Array)).toEqual([1, 2, 3, 4]) + }).pipe( + Effect.provide( + HttpRecorder.Cassette.memory({ + "ws/binary": [ + { + transport: "websocket", + open: { url: "wss://example.test/binary", headers: {} }, + client: [], + server: [ + { kind: "binary", body: Buffer.from(binaryServer).toString("base64"), bodyEncoding: "base64" }, + ], + }, + ], + }), + ), + ), + ), + ) + }) }) diff --git a/packages/kilo-docs/lychee.toml b/packages/kilo-docs/lychee.toml index a5a47fe1c66..fe1be68c3a2 100644 --- a/packages/kilo-docs/lychee.toml +++ b/packages/kilo-docs/lychee.toml @@ -31,6 +31,7 @@ exclude = [ # API endpoint prefixes are valid but return 4xx to plain GET link checks. '^https?://ai-gateway\.vercel\.sh/v1/?$', '^https?://api\.voyageai\.com/v1/embeddings/?$', + '^https?://inference\.do-ai\.run/v1/?$', '^https?://generativelanguage\.googleapis\.com/v1beta/openai/?$', '^https?://search\.parallel\.ai/mcp/?$', # xAI API and OAuth endpoints require request parameters or reject plain link checks. diff --git a/packages/kilo-docs/markdoc/nodes/heading.markdoc.ts b/packages/kilo-docs/markdoc/nodes/heading.markdoc.ts index 3c5741af8d0..ca17196bb5d 100644 --- a/packages/kilo-docs/markdoc/nodes/heading.markdoc.ts +++ b/packages/kilo-docs/markdoc/nodes/heading.markdoc.ts @@ -12,12 +12,7 @@ function generateID(children, attributes) { if (attributes.id && typeof attributes.id === "string") { return attributes.id } - return children - .map(text) - .join(" ") - .replace(/[?/]/g, "") - .replace(/\s+/g, "-") - .toLowerCase() + return children.map(text).join(" ").replace(/[?/]/g, "").replace(/\s+/g, "-").toLowerCase() } export const heading = { diff --git a/packages/kilo-docs/pages/code-with-ai/platforms/cli-reference.md b/packages/kilo-docs/pages/code-with-ai/platforms/cli-reference.md index 20bde638bfb..67600a5e1e5 100644 --- a/packages/kilo-docs/pages/code-with-ai/platforms/cli-reference.md +++ b/packages/kilo-docs/pages/code-with-ai/platforms/cli-reference.md @@ -207,6 +207,7 @@ Commands: kilo debug snapshot snapshot debugging utilities kilo debug startup print startup timing kilo debug agent show agent configuration details + kilo debug v2 debug v2 catalog and built-in plugins kilo debug info show debug information kilo debug paths show global paths (data, config, cache, state) kilo debug wait wait indefinitely (for debugging) @@ -509,6 +510,16 @@ Options: --params Tool params as JSON or a JS object literal [string] ``` +### kilo debug v2 + +``` +debug v2 catalog and built-in plugins + +Options: + --help Show help [boolean] + --version Show version number [boolean] +``` + ### kilo debug info ``` diff --git a/packages/kilo-docs/source-links.md b/packages/kilo-docs/source-links.md index 3704e123e2e..21afcdcab96 100644 --- a/packages/kilo-docs/source-links.md +++ b/packages/kilo-docs/source-links.md @@ -7,6 +7,8 @@ - +- + - - @@ -43,10 +45,10 @@ - +- + - -- - - - @@ -94,6 +96,8 @@ - +- + - - @@ -116,8 +120,6 @@ - -- - - @@ -126,10 +128,6 @@ - -- - -- - - @@ -143,6 +141,8 @@ - +- + - - @@ -151,12 +151,6 @@ - -- - -- - -- - - - diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index 638058d01ad..1e227f90038 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -5,6 +5,7 @@ import type { Session, SessionStatus, Event, + GlobalEvent, TextPartInput, FilePartInput, Config, @@ -25,6 +26,8 @@ import { } from "./services/telemetry" import { sessionToWebview, + applySessionPatch, + sessionPatchToWebview, indexProvidersById, filterVisibleAgents, buildSettingPath, @@ -158,6 +161,81 @@ const mapAgent = (a: Agent) => ({ const SESSION_SCOPED_PART_EVENTS = new Set(["message.part.updated", "message.part.delta", "message.part.removed"]) const isSessionScopedPartEvent = (type: string) => SESSION_SCOPED_PART_EVENTS.has(type) +type SyncPayload = Extract +type LegacySyncEvent = + | { + id: string + type: "message.updated" + properties: Extract["data"] + } + | { + id: string + type: "message.removed" + properties: Extract["data"] + } + | { + id: string + type: "message.part.updated" + properties: Extract["data"] + } + | { + id: string + type: "message.part.removed" + properties: Extract["data"] + } + | { + id: string + type: "session.created" + properties: Extract["data"] + } + | { + id: string + type: "session.updated" + properties: Extract["data"] + } + | { + id: string + type: "session.deleted" + properties: Extract["data"] + } + +type ProviderEvent = Event | LegacySyncEvent + +function isLegacySyncEvent(event: ProviderEvent): event is LegacySyncEvent { + return ( + event.type === "message.updated" || + event.type === "message.removed" || + event.type === "message.part.updated" || + event.type === "message.part.removed" || + event.type === "session.created" || + event.type === "session.updated" || + event.type === "session.deleted" + ) +} + +function unwrapSyncEvent(event: GlobalEvent["payload"]): ProviderEvent | undefined { + if (event.type !== "sync") return event + + switch (event.name) { + case "message.updated.1": + return { id: event.id, type: "message.updated", properties: event.data } + case "message.removed.1": + return { id: event.id, type: "message.removed", properties: event.data } + case "message.part.updated.1": + return { id: event.id, type: "message.part.updated", properties: event.data } + case "message.part.removed.1": + return { id: event.id, type: "message.part.removed", properties: event.data } + case "session.created.1": + return { id: event.id, type: "session.created", properties: event.data } + case "session.updated.1": + return { id: event.id, type: "session.updated", properties: event.data } + case "session.deleted.1": + return { id: event.id, type: "session.deleted", properties: event.data } + default: + return undefined + } +} + export class KiloProvider implements vscode.WebviewViewProvider, TelemetryPropertiesProvider { public static readonly viewType = "kilo-code.SidebarProvider" private readonly instanceId = crypto.randomUUID() @@ -1165,7 +1243,10 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper // Subscribe to SSE events for this webview (filtered by tracked sessions) this.unsubscribeEvent = this.connectionService.onEventFiltered( - (event, directory) => { + (payload, directory) => { + const event = unwrapSyncEvent(payload) + if (!event) return false + // Preserve the request origin even when a worktree session is not tracked yet. // Manual replies must target the Instance that owns the pending permission. if (event.type === "permission.asked" && directory) { @@ -1177,7 +1258,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper // Remote status events are global and should always pass through if (event.type === "kilo-sessions.remote-status-changed") return true - const sessionId = this.connectionService.resolveEventSessionId(event) + const sessionId = this.resolveEventSessionId(event) // message.part.* events are always session-scoped; drop if session unknown. if (!sessionId) return !isSessionScopedPartEvent(event.type) @@ -1194,8 +1275,9 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper return this.trackedSessionIds.has(sessionId) }, - (event, directory) => { - this.handleEvent(event, directory) + (payload, directory) => { + const event = unwrapSyncEvent(payload) + if (event) this.handleEvent(event, directory) }, ) @@ -2868,11 +2950,79 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper ]) } + private mapSyncEventToWebviewMessage(event: LegacySyncEvent) { + switch (event.type) { + case "message.updated": { + const info = event.properties.info + return { + type: "messageCreated" as const, + message: { + ...info, + createdAt: new Date(info.time.created).toISOString(), + }, + } + } + case "message.removed": + return { + type: "messageRemoved" as const, + sessionID: event.properties.sessionID, + messageID: event.properties.messageID, + } + case "message.part.updated": + return { + type: "partUpdated" as const, + sessionID: event.properties.sessionID, + messageID: event.properties.part.messageID, + part: event.properties.part, + } + case "message.part.removed": + return { + type: "partRemoved" as const, + sessionID: event.properties.sessionID, + messageID: event.properties.messageID, + partID: event.properties.partID, + } + case "session.created": + return { + type: "sessionCreated" as const, + session: this.sessionToWebview(event.properties.info), + } + case "session.updated": + return { + type: "sessionUpdated" as const, + session: + this.currentSession?.id === event.properties.sessionID + ? this.sessionToWebview(this.currentSession) + : sessionPatchToWebview(event.properties.sessionID, event.properties.info), + } + case "session.deleted": + return null + } + } + + private resolveEventSessionId(event: ProviderEvent): string | undefined { + switch (event.type) { + case "session.created": + case "session.updated": + case "session.deleted": + return event.properties.sessionID + case "message.updated": + this.connectionService.recordMessageSessionId(event.properties.info.id, event.properties.sessionID) + return event.properties.sessionID + case "message.removed": + case "message.part.updated": + case "message.part.removed": + return event.properties.sessionID + default: + return this.connectionService.resolveEventSessionId(event) + } + } + /** * Handle SSE events from the CLI backend. * Filters events by project ID and tracked session IDs so each webview only sees its own sessions. */ - private handleEvent(event: Event, directory?: string): void { + private handleEvent(event: ProviderEvent, directory?: string): void { if (event.type === "kilo-sessions.remote-status-changed") { this.remoteService?.updateFromEvent({ enabled: event.properties.enabled, connected: event.properties.connected }) return @@ -2881,7 +3031,16 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper // Drop session events from other projects before any tracking logic. // This must come first: the trackedSessionIds guard below would otherwise // let a foreign session through if it was accidentally tracked. - if (isEventFromForeignProject(event, this.projectID)) return + if (!isLegacySyncEvent(event) && isEventFromForeignProject(event, this.projectID)) return + if ( + this.projectID && + (event.type === "session.created" || event.type === "session.updated") && + event.properties.info.projectID !== undefined && + event.properties.info.projectID !== null && + event.properties.info.projectID !== this.projectID + ) { + return + } if (event.type === "mcp.browser.open.failed") { McpOAuth.openMcpOAuthUrlOnce(event.properties.url) @@ -2913,7 +3072,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper return } - const sessionID = this.connectionService.resolveEventSessionId(event) + const sessionID = this.resolveEventSessionId(event) // Events without sessionID (server.connected, server.heartbeat, indexing.status) → always forward // Events with sessionID → only forward if this webview tracks that session @@ -2952,9 +3111,9 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper this.contextSessionID = event.properties.info.id this.trackedSessionIds.add(event.properties.info.id) } - if (event.type === "session.updated" && this.currentSession?.id === event.properties.info.id) { - this.setCurrentSession(event.properties.info) - this.contextSessionID = event.properties.info.id + if (event.type === "session.updated" && this.currentSession?.id === event.properties.sessionID) { + this.setCurrentSession(applySessionPatch(this.currentSession, event.properties.info)) + this.contextSessionID = event.properties.sessionID } // Auto-adopt child sessions as soon as the task tool part reveals their ID. @@ -2976,13 +3135,27 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper } } - handleNetworkEvent(event.type as string, event.properties as any, this.client, (s) => this.getWorkspaceDirectory(s)) + if (!isLegacySyncEvent(event)) { + const props = event.properties + handleNetworkEvent( + event.type, + { + id: "id" in props && typeof props.id === "string" ? props.id : undefined, + sessionID: "sessionID" in props && typeof props.sessionID === "string" ? props.sessionID : undefined, + requestID: "requestID" in props && typeof props.requestID === "string" ? props.requestID : undefined, + }, + this.client, + (s) => this.getWorkspaceDirectory(s), + ) + } if (event.type === "indexing.status" && directory) { if (!sameDirectory(directory, this.getWorkspaceDirectory(this.currentSession?.id))) return } - const msg = mapSSEEventToWebviewMessage(event, sessionID) + const msg = isLegacySyncEvent(event) + ? this.mapSyncEventToWebviewMessage(event) + : mapSSEEventToWebviewMessage(event, sessionID) if (!msg) return if (msg.type === "partUpdated") { this.streams.push({ ...msg, part: this.slimPart(msg.part) }) diff --git a/packages/kilo-vscode/src/MarketplacePanelProvider.ts b/packages/kilo-vscode/src/MarketplacePanelProvider.ts index c38ea033f24..cbdd5f53de4 100644 --- a/packages/kilo-vscode/src/MarketplacePanelProvider.ts +++ b/packages/kilo-vscode/src/MarketplacePanelProvider.ts @@ -1,6 +1,6 @@ import * as os from "os" import * as vscode from "vscode" -import type { Event, SessionStatus } from "@kilocode/sdk/v2/client" +import type { GlobalEvent, SessionStatus } from "@kilocode/sdk/v2/client" import { buildWebviewHtml, getWebviewFontSize } from "./utils" import { watchFontSizeConfig } from "./kilo-provider/font-size" import { mapSSEEventToWebviewMessage } from "./kilo-provider-utils" @@ -114,7 +114,9 @@ export class MarketplacePanelProvider implements vscode.Disposable { this.connection.onLanguageChanged((locale) => this.post({ type: "languageChanged", locale })), this.connection.onEventFiltered( (event) => event.type === "session.status", - (event) => this.handleStatus(event), + (event) => { + if (event.type === "session.status") this.handleStatus(event) + }, ), ) void this.connect() @@ -236,8 +238,7 @@ export class MarketplacePanelProvider implements vscode.Disposable { this.post({ type: "marketplaceRemoveResult", ...result }) } - private handleStatus(event: Event): void { - if (event.type !== "session.status") return + private handleStatus(event: Extract): void { const sid = event.properties.sessionID this.statuses.set(sid, event.properties.status.type) const msg = mapSSEEventToWebviewMessage(event, sid) diff --git a/packages/kilo-vscode/src/commands/toggle-auto-approve.ts b/packages/kilo-vscode/src/commands/toggle-auto-approve.ts index 29e6ef891c6..0e88331ae78 100644 --- a/packages/kilo-vscode/src/commands/toggle-auto-approve.ts +++ b/packages/kilo-vscode/src/commands/toggle-auto-approve.ts @@ -1,5 +1,5 @@ import * as vscode from "vscode" -import type { KiloClient, Event } from "@kilocode/sdk/v2/client" +import type { KiloClient } from "@kilocode/sdk/v2/client" import type { KiloConnectionService } from "../services/cli-backend/connection-service" /** @@ -83,7 +83,7 @@ export function registerToggleAutoApprove( return active } - const unsubscribe = connectionService.onEvent((event: Event, directory?: string) => { + const unsubscribe = connectionService.onEvent((event, directory) => { if (!active) return if (event.type !== "permission.asked") return const client = tryGetClient(connectionService) diff --git a/packages/kilo-vscode/src/kilo-provider-utils.ts b/packages/kilo-vscode/src/kilo-provider-utils.ts index c8ceef403ba..eb28b773e81 100644 --- a/packages/kilo-vscode/src/kilo-provider-utils.ts +++ b/packages/kilo-vscode/src/kilo-provider-utils.ts @@ -1,4 +1,16 @@ -import type { Session, Agent, Event, ProviderListResponse } from "@kilocode/sdk/v2/client" +import type { + Session, + Agent, + Event, + ProviderListResponse, + SyncEventMessageUpdated, + SyncEventMessageRemoved, + SyncEventMessagePartUpdated, + SyncEventMessagePartRemoved, + SyncEventSessionCreated, + SyncEventSessionUpdated, + SyncEventSessionDeleted, +} from "@kilocode/sdk/v2/client" import { prettifyError } from "zod/v4" import type { CloudSessionMessage, IndexingStatus } from "./services/cli-backend/types" import type { PartBatch, PartUpdate } from "./kilo-provider/session-stream-scheduler" @@ -200,6 +212,73 @@ export function sessionToWebview(session: Session) { } } +type SessionPatch = SyncEventSessionUpdated["data"]["info"] +export type WebviewSessionPatch = Partial> & { id: string } + +function set(target: T, key: K, value: T[K] | null | undefined): void { + if (value === undefined || value === null) return + target[key] = value +} + +function update(target: T, key: K, value: T[K] | null | undefined): void { + if (value === undefined) return + if (value === null) { + Reflect.deleteProperty(target, key) + return + } + target[key] = value +} + +function share(session: Session, url: string | null | undefined): void { + if (url === undefined) return + if (url === null) { + delete session.share + return + } + session.share = { url } +} + +export function applySessionPatch(current: Session, patch: SessionPatch): Session { + const next: Session = { ...current, time: { ...current.time } } + + set(next, "slug", patch.slug) + set(next, "projectID", patch.projectID) + set(next, "directory", patch.directory) + set(next, "title", patch.title) + set(next, "version", patch.version) + update(next, "workspaceID", patch.workspaceID) + update(next, "path", patch.path) + update(next, "parentID", patch.parentID) + update(next, "summary", patch.summary) + update(next, "cost", patch.cost) + update(next, "tokens", patch.tokens) + share(next, patch.share?.url) + update(next, "agent", patch.agent) + update(next, "model", patch.model) + update(next, "permission", patch.permission) + update(next, "revert", patch.revert) + set(next.time, "created", patch.time?.created) + set(next.time, "updated", patch.time?.updated) + update(next.time, "compacting", patch.time?.compacting) + update(next.time, "archived", patch.time?.archived) + + return next +} + +export function sessionPatchToWebview(sessionID: string, patch: SessionPatch): WebviewSessionPatch { + return { + id: sessionID, + ...(patch.parentID !== undefined && { parentID: patch.parentID }), + ...(patch.title !== undefined && patch.title !== null && { title: patch.title }), + ...(patch.time?.created !== undefined && + patch.time.created !== null && { createdAt: new Date(patch.time.created).toISOString() }), + ...(patch.time?.updated !== undefined && + patch.time.updated !== null && { updatedAt: new Date(patch.time.updated).toISOString() }), + ...(patch.revert !== undefined && { revert: patch.revert }), + ...(patch.summary !== undefined && { summary: patch.summary }), + } +} + export function indexProvidersById(all: ProviderInfo[]): Record { const normalized: Record = {} for (const provider of all) { @@ -375,6 +454,17 @@ export function sameDirectory(a: string, b: string): boolean { return path.relative(left.toLowerCase(), right.toLowerCase()) === "" } +type SyncEvent = + | SyncEventMessageUpdated + | SyncEventMessageRemoved + | SyncEventMessagePartUpdated + | SyncEventMessagePartRemoved + | SyncEventSessionCreated + | SyncEventSessionUpdated + | SyncEventSessionDeleted + +type StreamEvent = Event | SyncEvent + export type WebviewMessage = | PartUpdate | PartBatch @@ -424,84 +514,92 @@ export type WebviewMessage = | { type: "permissionResolved"; permissionID: string } | { type: "permissionError"; permissionID: string; stale?: boolean } | { type: "sessionCreated"; session: ReturnType; draftID?: string } - | { type: "sessionUpdated"; session: ReturnType } + | { type: "sessionUpdated"; session: WebviewSessionPatch } + | { type: "sessionDeleted"; sessionID: string } | { type: "messageRemoved"; sessionID: string; messageID: string } | { type: "sessionError"; sessionID?: string; error?: unknown } | null -type PartEvent = Extract +type PartEvent = + | Extract + | SyncEventMessagePartUpdated + | SyncEventMessagePartRemoved function mapPartEvent(event: PartEvent, sessionID: string | undefined): WebviewMessage { - if (!sessionID) return null - if (event.type === "message.part.updated") { - const part = event.properties.part as { messageID?: string; sessionID?: string } - return { - type: "partUpdated", - sessionID, - messageID: part.messageID || "", - part: event.properties.part, + if (event.type === "sync") { + if (event.name === "message.part.updated.1") { + const part = event.data.part + return { + type: "partUpdated", + sessionID: event.data.sessionID, + messageID: part.messageID, + part, + } } - } - if (event.type === "message.part.delta") { - const props = event.properties return { - type: "partUpdated", - sessionID: props.sessionID, - messageID: props.messageID, - part: { id: props.partID, type: "text", messageID: props.messageID, text: props.delta }, - delta: { type: "text-delta", textDelta: props.delta }, + type: "partRemoved", + sessionID: event.data.sessionID, + messageID: event.data.messageID, + partID: event.data.partID, } } + if (!sessionID) return null const props = event.properties return { - type: "partRemoved", + type: "partUpdated", sessionID: props.sessionID, messageID: props.messageID, - partID: props.partID, + part: { id: props.partID, type: "text", messageID: props.messageID, text: props.delta }, + delta: { type: "text-delta", textDelta: props.delta }, } } -export function mapSSEEventToWebviewMessage(event: Event, sessionID: string | undefined): WebviewMessage { - if ( - event.type === "message.part.updated" || - event.type === "message.part.delta" || - event.type === "message.part.removed" - ) { - return mapPartEvent(event, sessionID) - } - switch (event.type) { - case "message.updated": { - const info = event.properties.info - return { - type: "messageCreated", - message: { - ...info, - createdAt: new Date(info.time.created).toISOString(), - }, - } - } - case "message.removed": { - const props = event.properties as { sessionID: string; messageID: string } - return { - type: "messageRemoved", - sessionID: props.sessionID, - messageID: props.messageID, +export function mapSSEEventToWebviewMessage(event: StreamEvent, sessionID: string | undefined): WebviewMessage { + if (event.type === "sync") { + switch (event.name) { + case "message.updated.1": { + const info = event.data.info + return { + type: "messageCreated", + message: { + ...info, + createdAt: new Date(info.time.created).toISOString(), + }, + } } + case "message.removed.1": + return { + type: "messageRemoved", + sessionID: event.data.sessionID, + messageID: event.data.messageID, + } + case "message.part.updated.1": + case "message.part.removed.1": + return mapPartEvent(event, sessionID) + case "session.created.1": + return { + type: "sessionCreated", + session: sessionToWebview(event.data.info), + } + case "session.updated.1": + return null + case "session.deleted.1": + return { + type: "sessionDeleted", + sessionID: event.data.sessionID, + } } + } + if (event.type === "message.part.delta") return mapPartEvent(event, sessionID) + switch (event.type) { case "session.status": { const info = event.properties.status - // "offline" is not yet in the SDK SessionStatus type (pending SDK regeneration), - // so we use string comparison to forward the message field for offline status. - const status = info.type as string + const status = info.type const extra = - status === "retry" - ? { - attempt: (info as any).attempt as number, - message: (info as any).message as string, - next: (info as any).next as number, - } - : status === "offline" - ? { message: (info as any).message as string } + info.type === "retry" + ? { attempt: info.attempt, message: info.message, next: info.next } + : info.type === "offline" + ? { message: info.message } : {} return { type: "sessionStatus" as const, @@ -583,16 +681,6 @@ export function mapSSEEventToWebviewMessage(event: Event, sessionID: string | un error: event.properties.error, } } - case "session.created": - return { - type: "sessionCreated", - session: sessionToWebview(event.properties.info), - } - case "session.updated": - return { - type: "sessionUpdated", - session: sessionToWebview(event.properties.info), - } case "indexing.status": return { type: "indexingStatusLoaded", @@ -623,10 +711,12 @@ export function mapCloudSessionMessageToWebviewMessage(message: CloudSessionMess * Returns true when the event carries a projectID that does not match the expected one. * When expectedProjectID is undefined (not yet resolved), nothing is filtered. */ -export function isEventFromForeignProject(event: Event, expectedProjectID: string | undefined): boolean { - if (!expectedProjectID) return false - if (event.type === "session.created" || event.type === "session.updated") { - return event.properties.info.projectID !== expectedProjectID +export function isEventFromForeignProject(event: StreamEvent, expectedProjectID: string | undefined): boolean { + if (!expectedProjectID || event.type !== "sync") return false + if (event.name === "session.created.1" || event.name === "session.deleted.1") { + return event.data.info.projectID !== expectedProjectID } - return false + if (event.name !== "session.updated.1") return false + const project = event.data.info.projectID + return project != null && project !== expectedProjectID } diff --git a/packages/kilo-vscode/src/services/cli-backend/connection-service.ts b/packages/kilo-vscode/src/services/cli-backend/connection-service.ts index 82fcd1b8e53..8c7d36f3a8a 100644 --- a/packages/kilo-vscode/src/services/cli-backend/connection-service.ts +++ b/packages/kilo-vscode/src/services/cli-backend/connection-service.ts @@ -1,14 +1,14 @@ import * as vscode from "vscode" import { ServerManager } from "./server-manager" -import { createKiloClient, type KiloClient, type Event } from "@kilocode/sdk/v2/client" -import { SdkSSEAdapter } from "./sdk-sse-adapter" +import { createKiloClient, type KiloClient } from "@kilocode/sdk/v2/client" +import { SdkSSEAdapter, type SSEPayload } from "./sdk-sse-adapter" import type { ServerConfig } from "./types" import { resolveEventSessionId as resolveEventSessionIdPure } from "./connection-utils" export type ConnectionState = "connecting" | "connected" | "disconnected" | "error" -type SSEEventListener = (event: Event, directory?: string) => void +type SSEEventListener = (event: SSEPayload, directory?: string) => void type StateListener = (state: ConnectionState, error?: Error) => void -type SSEEventFilter = (event: Event, directory?: string) => boolean +type SSEEventFilter = (event: SSEPayload, directory?: string) => boolean type NotificationDismissListener = (notificationId: string) => void type LanguageChangeListener = (locale: string) => void type ProfileChangeListener = (data: unknown) => void @@ -234,7 +234,7 @@ export class KiloConnectionService { * Best-effort sessionID extraction for an SSE event. * Returns undefined for global events. */ - resolveEventSessionId(event: Event): string | undefined { + resolveEventSessionId(event: SSEPayload): string | undefined { return resolveEventSessionIdPure( event, (messageId) => this.messageSessionIdsByMessageId.get(messageId), diff --git a/packages/kilo-vscode/src/services/cli-backend/connection-utils.ts b/packages/kilo-vscode/src/services/cli-backend/connection-utils.ts index 1b0a1dcfa62..308beb15bcc 100644 --- a/packages/kilo-vscode/src/services/cli-backend/connection-utils.ts +++ b/packages/kilo-vscode/src/services/cli-backend/connection-utils.ts @@ -1,66 +1,61 @@ -import type { Event } from "@kilocode/sdk/v2/client" +import type { GlobalEvent } from "@kilocode/sdk/v2/client" + +export type SSEPayload = GlobalEvent["payload"] +type SyncPayload = Extract +type TransientPayload = Exclude /** * Pure session ID resolution for SSE events. - * The lookupMessageSessionId callback is used for message.part.updated fallback lookup, - * and onMessageUpdated is called when message.updated is encountered so the caller can - * record the messageID -> sessionID mapping. + * The lookupMessageSessionId callback remains part of the public resolver contract for + * transient events that may only carry a message ID, and onMessageUpdated records the + * messageID -> sessionID mapping from versioned message updates. */ export function resolveEventSessionId( - event: Event, + event: SSEPayload, lookupMessageSessionId: (messageId: string) => string | undefined, onMessageUpdated?: (messageId: string, sessionId: string) => void, ): string | undefined { + if (event.type === "sync") { + return resolveSyncSessionId(event, onMessageUpdated) + } + + void lookupMessageSessionId + return resolveTransientSessionId(event) +} + +function resolveSyncSessionId( + event: SyncPayload, + onMessageUpdated?: (messageId: string, sessionId: string) => void, +): string | undefined { + if (event.name === "message.updated.1") { + onMessageUpdated?.(event.data.info.id, event.data.sessionID) + } + return event.data.sessionID +} + +function resolveTransientSessionId(event: TransientPayload): string | undefined { switch (event.type) { - case "session.created": - case "session.updated": - return event.properties.info.id case "session.status": + case "session.turn.open": case "session.turn.close": case "session.idle": case "session.error": case "todo.updated": - return event.properties.sessionID - case "message.updated": - onMessageUpdated?.(event.properties.info.id, event.properties.info.sessionID) - return event.properties.info.sessionID - case "message.part.updated": { - const part = event.properties.part as { messageID?: string; sessionID?: string } - if (part.sessionID) { - return part.sessionID - } - if (!part.messageID) { - return undefined - } - return lookupMessageSessionId(part.messageID) - } case "message.part.delta": - return event.properties.sessionID - case "message.part.removed": - return event.properties.sessionID case "permission.asked": case "permission.replied": case "question.asked": case "question.replied": case "question.rejected": - return event.properties.sessionID - default: - return resolveSuggestionSessionId(event) - } -} - -function resolveSuggestionSessionId(event: Event): string | undefined { - switch (event.type) { case "suggestion.shown": case "suggestion.accepted": case "suggestion.dismissed": + case "session.network.asked": + case "session.network.replied": + case "session.network.rejected": + case "session.network.restored": return event.properties.sessionID default: - // session.network.* events are not yet in the SDK Event type union - // (pending SDK regeneration). Handle them via string comparison. - if ((event.type as string).startsWith("session.network.")) { - return (event.properties as { sessionID: string }).sessionID - } return undefined } } diff --git a/packages/kilo-vscode/src/services/cli-backend/sdk-sse-adapter.ts b/packages/kilo-vscode/src/services/cli-backend/sdk-sse-adapter.ts index 07533a28602..9e7b8cc191d 100644 --- a/packages/kilo-vscode/src/services/cli-backend/sdk-sse-adapter.ts +++ b/packages/kilo-vscode/src/services/cli-backend/sdk-sse-adapter.ts @@ -1,6 +1,7 @@ -import type { KiloClient, GlobalEvent, Event } from "@kilocode/sdk/v2/client" +import type { KiloClient, GlobalEvent } from "@kilocode/sdk/v2/client" -export type SSEEventHandler = (event: Event, directory?: string) => void +export type SSEPayload = GlobalEvent["payload"] +export type SSEEventHandler = (event: SSEPayload, directory?: string) => void export type SSEErrorHandler = (error: Error) => void export type SSEStateHandler = (state: "connecting" | "connected" | "disconnected") => void @@ -179,9 +180,7 @@ export class SdkSSEAdapter { this.notifyState("connected") } - // The SDK yields GlobalEvent = { directory, payload: Event }. - const globalEvent = event as GlobalEvent - this.notifyEvent(globalEvent.payload as Event, globalEvent.directory) + this.notifyEvent(event.payload, event.directory) } console.log( @@ -237,7 +236,7 @@ export class SdkSSEAdapter { // ── Notify helpers ───────────────────────────────────────────────── - private notifyEvent(event: Event, directory?: string): void { + private notifyEvent(event: SSEPayload, directory?: string): void { for (const handler of this.handlers) { try { handler(event, directory) diff --git a/packages/kilo-vscode/tests/unit/connection-utils.test.ts b/packages/kilo-vscode/tests/unit/connection-utils.test.ts index 4a29c068831..46d74e8a231 100644 --- a/packages/kilo-vscode/tests/unit/connection-utils.test.ts +++ b/packages/kilo-vscode/tests/unit/connection-utils.test.ts @@ -1,193 +1,166 @@ -import { describe, it, expect } from "bun:test" +import { describe, expect, it } from "bun:test" +import type { GlobalEvent } from "@kilocode/sdk/v2/client" import { resolveEventSessionId } from "../../src/services/cli-backend/connection-utils" -import type { Event } from "@kilocode/sdk/v2/client" const noLookup = (_: string) => undefined -/** Helper to create a partial Event for testing — only the fields accessed by resolveEventSessionId matter. */ -function event(partial: Record): Event { - return partial as unknown as Event +type Payload = GlobalEvent["payload"] + +const message = { + id: "m1", + sessionID: "s5", + role: "user", + time: { created: 0 }, + agent: "build", + model: { providerID: "kilo", modelID: "test" }, +} as const + +const part = { + id: "p1", + sessionID: "s6", + messageID: "m1", + type: "text", + text: "", +} as const + +function sync(event: Extract): Payload { + return event } describe("resolveEventSessionId", () => { - it("returns session id from session.created", () => { - const e = event({ - type: "session.created", - properties: { - info: { id: "s1", title: "", directory: "", time: { created: 0, updated: 0 } }, + it("returns the session ID from session.created.1", () => { + const event = sync({ + type: "sync", + name: "session.created.1", + id: "e1", + seq: 0, + aggregateID: "sessionID", + data: { + sessionID: "s1", + info: { + id: "s1", + slug: "session", + projectID: "project", + directory: "/workspace", + title: "Session", + version: "1", + time: { created: 0, updated: 0 }, + }, }, }) - expect(resolveEventSessionId(e, noLookup)).toBe("s1") - }) - it("returns session id from session.updated", () => { - const e = event({ - type: "session.updated", - properties: { - info: { id: "s2", title: "", directory: "", time: { created: 0, updated: 0 } }, - }, - }) - expect(resolveEventSessionId(e, noLookup)).toBe("s2") + expect(resolveEventSessionId(event, noLookup)).toBe("s1") }) - it("returns sessionID from session.status", () => { - const e = event({ - type: "session.status", - properties: { sessionID: "s3", status: { type: "idle" } }, + it("returns the session ID from session.updated.1", () => { + const event = sync({ + type: "sync", + name: "session.updated.1", + id: "e2", + seq: 1, + aggregateID: "sessionID", + data: { sessionID: "s2", info: { title: "Updated" } }, }) - expect(resolveEventSessionId(e, noLookup)).toBe("s3") - }) - it("returns sessionID from todo.updated", () => { - const e = event({ - type: "todo.updated", - properties: { sessionID: "s4", todos: [] }, - }) - expect(resolveEventSessionId(e, noLookup)).toBe("s4") + expect(resolveEventSessionId(event, noLookup)).toBe("s2") }) - it("returns sessionID from session.turn.close", () => { - const e = event({ - type: "session.turn.close", - properties: { sessionID: "s-turn", reason: "interrupted" }, + it("records message.updated.1 mappings", () => { + const event = sync({ + type: "sync", + name: "message.updated.1", + id: "e3", + seq: 2, + aggregateID: "sessionID", + data: { sessionID: "s5", info: message }, }) - expect(resolveEventSessionId(e, noLookup)).toBe("s-turn") - }) + const recorded: Array<[string, string]> = [] - it("returns sessionID from message.updated and calls onMessageUpdated", () => { - const e = event({ - type: "message.updated", - properties: { - info: { id: "m1", sessionID: "s5", role: "assistant", time: { created: 0 } }, - }, - }) - const recorded: [string, string][] = [] - const result = resolveEventSessionId(e, noLookup, (mid, sid) => recorded.push([mid, sid])) - expect(result).toBe("s5") + expect(resolveEventSessionId(event, noLookup, (mid, sid) => recorded.push([mid, sid]))).toBe("s5") expect(recorded).toEqual([["m1", "s5"]]) }) - it("message.updated does not require onMessageUpdated callback", () => { - const e = event({ - type: "message.updated", - properties: { - info: { id: "m1", sessionID: "s5", role: "assistant", time: { created: 0 } }, - }, + it("does not require a message mapping callback", () => { + const event = sync({ + type: "sync", + name: "message.updated.1", + id: "e4", + seq: 3, + aggregateID: "sessionID", + data: { sessionID: "s5", info: message }, }) - expect(() => resolveEventSessionId(e, noLookup)).not.toThrow() - }) - it("returns sessionID directly from message.part.updated when part has sessionID", () => { - const e = event({ - type: "message.part.updated", - properties: { - part: { type: "text", id: "p1", text: "", sessionID: "s6", messageID: "m1" }, - }, - }) - expect(resolveEventSessionId(e, noLookup)).toBe("s6") + expect(() => resolveEventSessionId(event, noLookup)).not.toThrow() }) - it("falls back to lookup when message.part.updated has no sessionID but has messageID", () => { - const e = event({ - type: "message.part.updated", - properties: { - part: { type: "text", id: "p1", text: "", messageID: "m2" }, - }, + it("returns the envelope session ID from message.part.updated.1", () => { + const event = sync({ + type: "sync", + name: "message.part.updated.1", + id: "e5", + seq: 4, + aggregateID: "sessionID", + data: { sessionID: "s6", part, time: 0 }, }) - const lookup = (id: string) => (id === "m2" ? "s7" : undefined) - expect(resolveEventSessionId(e, lookup)).toBe("s7") - }) - it("returns undefined for message.part.updated with no sessionID and messageID not in map", () => { - const e = event({ - type: "message.part.updated", - properties: { - part: { type: "text", id: "p1", text: "", messageID: "unknown" }, - }, - }) - expect(resolveEventSessionId(e, noLookup)).toBeUndefined() + expect(resolveEventSessionId(event, noLookup)).toBe("s6") }) - it("returns undefined for message.part.updated with no messageID and no sessionID", () => { - const e = event({ - type: "message.part.updated", - properties: { - part: { type: "text", id: "p1", text: "" }, - }, - }) - expect(resolveEventSessionId(e, noLookup)).toBeUndefined() - }) + it("routes transient session events", () => { + const event = { + id: "e6", + type: "session.status", + properties: { sessionID: "s3", status: { type: "idle" } }, + } satisfies Payload - it("returns sessionID from permission.asked", () => { - const e = event({ - type: "permission.asked", - properties: { - id: "p1", - sessionID: "s8", - permission: "read_file", - patterns: [], - metadata: {}, - always: [], - }, - }) - expect(resolveEventSessionId(e, noLookup)).toBe("s8") + expect(resolveEventSessionId(event, noLookup)).toBe("s3") }) - it("returns sessionID from question.asked", () => { - const e = event({ - type: "question.asked", - properties: { id: "q1", sessionID: "s9", questions: [] }, - }) - expect(resolveEventSessionId(e, noLookup)).toBe("s9") - }) + it("routes transient message deltas", () => { + const event = { + id: "e7", + type: "message.part.delta", + properties: { sessionID: "s4", messageID: "m2", partID: "p2", field: "text", delta: "x" }, + } satisfies Payload - it("returns sessionID from question.replied", () => { - const e = event({ - type: "question.replied", - properties: { sessionID: "s10", requestID: "r1", answers: [] }, - }) - expect(resolveEventSessionId(e, noLookup)).toBe("s10") + expect(resolveEventSessionId(event, noLookup)).toBe("s4") }) - it("returns sessionID from question.rejected", () => { - const e = event({ - type: "question.rejected", - properties: { sessionID: "s11", requestID: "r2" }, - }) - expect(resolveEventSessionId(e, noLookup)).toBe("s11") - }) + it("routes session.network events", () => { + const event = { + id: "e8", + type: "session.network.restored", + properties: { sessionID: "s7" }, + } satisfies Payload - it("returns sessionID from suggestion.shown", () => { - const e = event({ - type: "suggestion.shown", - properties: { id: "sug_1", sessionID: "s12", text: "Review?", actions: [] }, - }) - expect(resolveEventSessionId(e, noLookup)).toBe("s12") - }) - - it("returns sessionID from suggestion.accepted", () => { - const e = event({ - type: "suggestion.accepted", - properties: { sessionID: "s13", requestID: "sug_1", index: 0, action: { label: "Start", prompt: "x" } }, - }) - expect(resolveEventSessionId(e, noLookup)).toBe("s13") + expect(resolveEventSessionId(event, noLookup)).toBe("s7") }) - it("returns sessionID from suggestion.dismissed", () => { - const e = event({ + it("routes permission, question, and suggestion events", () => { + const permission = { + id: "e9", + type: "permission.replied", + properties: { sessionID: "s8", requestID: "p1", reply: "once" }, + } satisfies Payload + const question = { + id: "e10", + type: "question.rejected", + properties: { sessionID: "s9", requestID: "q1" }, + } satisfies Payload + const suggestion = { + id: "e11", type: "suggestion.dismissed", - properties: { sessionID: "s14", requestID: "sug_2" }, - }) - expect(resolveEventSessionId(e, noLookup)).toBe("s14") - }) + properties: { sessionID: "s10", requestID: "sg1" }, + } satisfies Payload - it("returns undefined for unknown event types (global events)", () => { - const e = event({ type: "server.connected", properties: {} }) - expect(resolveEventSessionId(e, noLookup)).toBeUndefined() + expect(resolveEventSessionId(permission, noLookup)).toBe("s8") + expect(resolveEventSessionId(question, noLookup)).toBe("s9") + expect(resolveEventSessionId(suggestion, noLookup)).toBe("s10") }) - it("returns undefined for another unknown event type", () => { - const e = event({ type: "server.heartbeat", properties: {} }) - expect(resolveEventSessionId(e, noLookup)).toBeUndefined() + it("returns undefined for global events", () => { + const event = { id: "e12", type: "server.connected", properties: {} } satisfies Payload + + expect(resolveEventSessionId(event, noLookup)).toBeUndefined() }) }) diff --git a/packages/kilo-vscode/tests/unit/kilo-provider-utils.test.ts b/packages/kilo-vscode/tests/unit/kilo-provider-utils.test.ts index ef4aa2ab749..f2ee5c2f7e0 100644 --- a/packages/kilo-vscode/tests/unit/kilo-provider-utils.test.ts +++ b/packages/kilo-vscode/tests/unit/kilo-provider-utils.test.ts @@ -1,6 +1,8 @@ import { describe, it, expect } from "bun:test" import { sessionToWebview, + applySessionPatch, + sessionPatchToWebview, indexProvidersById, filterVisibleAgents, buildSettingPath, @@ -18,8 +20,8 @@ import type { Agent, Provider, Event, - EventMessagePartUpdated, - EventMessageUpdated, + SyncEventMessagePartUpdated, + SyncEventMessageUpdated, EventSessionStatus, EventSessionTurnClose, EventPermissionAsked, @@ -31,8 +33,8 @@ import type { EventSuggestionShown, EventSuggestionAccepted, EventSuggestionDismissed, - EventSessionCreated, - EventSessionUpdated, + SyncEventSessionCreated, + SyncEventSessionUpdated, EventServerConnected, TextPart, AssistantMessage, @@ -158,6 +160,115 @@ describe("sessionToWebview", () => { }) }) +describe("applySessionPatch", () => { + it("applies values without dropping unrelated session fields", () => { + const session = makeSession({ + workspaceID: "workspace-1", + cost: 1, + tokens: { input: 1, output: 2, reasoning: 3, cache: { read: 4, write: 5 } }, + }) + + const result = applySessionPatch(session, { title: "Updated", cost: 2, time: { updated: 1700002000000 } }) + + expect(result.title).toBe("Updated") + expect(result.cost).toBe(2) + expect(result.workspaceID).toBe("workspace-1") + expect(result.tokens).toEqual(session.tokens) + expect(result.time).toEqual({ created: 1700000000000, updated: 1700002000000 }) + }) + + it("clears optional fields when the patch contains null", () => { + const session = makeSession({ + workspaceID: "workspace-1", + summary: { additions: 1, deletions: 2, files: 3 }, + cost: 4, + tokens: { input: 1, output: 2, reasoning: 3, cache: { read: 4, write: 5 } }, + share: { url: "https://example.com" }, + agent: "code", + model: { id: "model-1", providerID: "provider-1" }, + permission: [], + revert: { messageID: "msg-1" }, + time: { created: 1, updated: 2, compacting: 3, archived: 4 }, + }) + + const result = applySessionPatch(session, { + workspaceID: null, + summary: null, + cost: null, + tokens: null, + share: { url: null }, + agent: null, + model: null, + permission: null, + revert: null, + time: { compacting: null, archived: null }, + }) + + expect(result.workspaceID).toBeUndefined() + expect(result.summary).toBeUndefined() + expect(result.cost).toBeUndefined() + expect(result.tokens).toBeUndefined() + expect(result.share).toBeUndefined() + expect(result.agent).toBeUndefined() + expect(result.model).toBeUndefined() + expect(result.permission).toBeUndefined() + expect(result.revert).toBeUndefined() + expect(result.time).toEqual({ created: 1, updated: 2 }) + }) + + it("ignores null patches for required session fields", () => { + const session = makeSession() + const result = applySessionPatch(session, { + slug: null, + projectID: null, + directory: null, + title: null, + version: null, + time: { created: null, updated: null }, + }) + + expect(result.slug).toBe(session.slug) + expect(result.projectID).toBe(session.projectID) + expect(result.directory).toBe(session.directory) + expect(result.title).toBe(session.title) + expect(result.version).toBe(session.version) + expect(result.time).toEqual(session.time) + }) + + it("does not mutate the original session", () => { + const session = makeSession({ revert: { messageID: "msg-1" }, time: { created: 1, updated: 2, compacting: 3 } }) + + applySessionPatch(session, { revert: null, time: { updated: 4, compacting: null } }) + + expect(session.revert).toEqual({ messageID: "msg-1" }) + expect(session.time).toEqual({ created: 1, updated: 2, compacting: 3 }) + }) +}) + +describe("sessionPatchToWebview", () => { + it("converts relevant patch timestamps and preserves explicit clears", () => { + const result = sessionPatchToWebview("sess-1", { + parentID: null, + summary: null, + revert: null, + time: { created: 1700000000000, updated: 1700001000000 }, + }) + + expect(result).toEqual({ + id: "sess-1", + parentID: null, + summary: null, + revert: null, + createdAt: new Date(1700000000000).toISOString(), + updatedAt: new Date(1700001000000).toISOString(), + }) + }) + + it("omits fields not present in the patch", () => { + expect(sessionPatchToWebview("sess-1", { cost: 2 })).toEqual({ id: "sess-1" }) + }) +}) + describe("indexProvidersById", () => { it("indexes providers by id", () => { const result = indexProvidersById([makeProvider("openai"), makeProvider("anthropic")]) @@ -243,11 +354,17 @@ describe("buildSettingPath", () => { }) describe("mapSSEEventToWebviewMessage", () => { - it("maps message.part.updated to partUpdated", () => { - const event: EventMessagePartUpdated = { - type: "message.part.updated", - properties: { + it("maps message.part.updated.1 to partUpdated", () => { + const event: SyncEventMessagePartUpdated = { + type: "sync", + name: "message.part.updated.1", + id: "evt-part", + seq: 1, + aggregateID: "sessionID", + data: { + sessionID: "sess-1", part: makeTextPart({ text: "hello" }), + time: 1700000000000, }, } const msg = mapSSEEventToWebviewMessage(event, "sess-1") @@ -258,18 +375,33 @@ describe("mapSSEEventToWebviewMessage", () => { } }) - it("returns null for message.part.updated when sessionID is undefined", () => { - const event: EventMessagePartUpdated = { - type: "message.part.updated", - properties: { part: makeTextPart({ text: "" }) }, + it("maps message.part.updated.1 without a tracked sessionID", () => { + const event: SyncEventMessagePartUpdated = { + type: "sync", + name: "message.part.updated.1", + id: "evt-part", + seq: 1, + aggregateID: "sessionID", + data: { + sessionID: "sess-1", + part: makeTextPart({ text: "" }), + time: 1700000000000, + }, } - expect(mapSSEEventToWebviewMessage(event, undefined)).toBeNull() + const msg = mapSSEEventToWebviewMessage(event, undefined) + expect(msg?.type).toBe("partUpdated") + if (msg?.type === "partUpdated") expect(msg.sessionID).toBe("sess-1") }) - it("maps message.updated to messageCreated with ISO date", () => { - const event: EventMessageUpdated = { - type: "message.updated", - properties: { + it("maps message.updated.1 to messageCreated with ISO date", () => { + const event: SyncEventMessageUpdated = { + type: "sync", + name: "message.updated.1", + id: "evt-message", + seq: 1, + aggregateID: "sessionID", + data: { + sessionID: "sess-1", info: makeAssistantMessage({ cost: 0.001 }), }, } @@ -496,10 +628,14 @@ describe("mapSSEEventToWebviewMessage", () => { expect(msg?.type).toBe("suggestionResolved") }) - it("maps session.created to sessionCreated with ISO dates", () => { - const event: EventSessionCreated = { - type: "session.created", - properties: { info: makeSession() }, + it("maps session.created.1 to sessionCreated with ISO dates", () => { + const event: SyncEventSessionCreated = { + type: "sync", + name: "session.created.1", + id: "evt-session", + seq: 1, + aggregateID: "sessionID", + data: { sessionID: "sess-1", info: makeSession() }, } const msg = mapSSEEventToWebviewMessage(event, "sess-1") expect(msg?.type).toBe("sessionCreated") @@ -508,13 +644,16 @@ describe("mapSSEEventToWebviewMessage", () => { } }) - it("maps session.updated to sessionUpdated with ISO dates", () => { - const event: EventSessionUpdated = { - type: "session.updated", - properties: { info: makeSession({ id: "sess-2" }) }, + it("ignores session.updated.1 after backend state reconciliation", () => { + const event: SyncEventSessionUpdated = { + type: "sync", + name: "session.updated.1", + id: "evt-session", + seq: 1, + aggregateID: "sessionID", + data: { sessionID: "sess-2", info: { id: "sess-2", time: { updated: 1700001000000 } } }, } - const msg = mapSSEEventToWebviewMessage(event, "sess-2") - expect(msg?.type).toBe("sessionUpdated") + expect(mapSSEEventToWebviewMessage(event, "sess-2")).toBeNull() }) it("returns null for server.connected (no webview message)", () => { @@ -529,37 +668,46 @@ describe("mapSSEEventToWebviewMessage", () => { }) describe("isEventFromForeignProject", () => { - const session = (projectID: string) => - ({ - id: "s1", - projectID, - title: "test", - directory: "/workspace", - time: { created: 0, updated: 0 }, - }) as unknown as Session + function created(projectID: string): SyncEventSessionCreated { + return { + type: "sync", + name: "session.created.1", + id: "evt-session", + seq: 1, + aggregateID: "sessionID", + data: { sessionID: "sess-1", info: makeSession({ projectID }) }, + } + } - it("drops session.created from a different project", () => { - const event: Event = { type: "session.created", properties: { info: session("project-B") } } - expect(isEventFromForeignProject(event, "project-A")).toBe(true) + function updated(projectID: string): SyncEventSessionUpdated { + return { + type: "sync", + name: "session.updated.1", + id: "evt-session", + seq: 1, + aggregateID: "sessionID", + data: { sessionID: "sess-1", info: { projectID } }, + } + } + + it("drops session.created.1 from a different project", () => { + expect(isEventFromForeignProject(created("project-B"), "project-A")).toBe(true) }) - it("drops session.updated from a different project", () => { - const event: Event = { type: "session.updated", properties: { info: session("project-B") } } - expect(isEventFromForeignProject(event, "project-A")).toBe(true) + it("drops session.updated.1 from a different project", () => { + expect(isEventFromForeignProject(updated("project-B"), "project-A")).toBe(true) }) - it("keeps session.created from the same project", () => { - const event: Event = { type: "session.created", properties: { info: session("project-A") } } - expect(isEventFromForeignProject(event, "project-A")).toBe(false) + it("keeps session.created.1 from the same project", () => { + expect(isEventFromForeignProject(created("project-A"), "project-A")).toBe(false) }) it("keeps all events when expectedProjectID is undefined", () => { - const event: Event = { type: "session.created", properties: { info: session("project-B") } } - expect(isEventFromForeignProject(event, undefined)).toBe(false) + expect(isEventFromForeignProject(created("project-B"), undefined)).toBe(false) }) it("keeps non-session events regardless of project", () => { - const event = { type: "server.heartbeat", properties: {} } as unknown as Event + const event: EventServerConnected = { type: "server.connected", properties: {} } expect(isEventFromForeignProject(event, "project-A")).toBe(false) }) }) diff --git a/packages/kilo-vscode/webview-ui/src/context/session.tsx b/packages/kilo-vscode/webview-ui/src/context/session.tsx index f608ea47d63..77d6ff321f3 100644 --- a/packages/kilo-vscode/webview-ui/src/context/session.tsx +++ b/packages/kilo-vscode/webview-ui/src/context/session.tsx @@ -25,6 +25,7 @@ import { useLanguage } from "./language" import { showToast } from "@kilocode/kilo-ui/toast" import type { SessionInfo, + SessionUpdate, Message, Part, PartDelta, @@ -1636,11 +1637,12 @@ export const SessionProvider: ParentComponent = (props) => { setStore("todos", sessionID, items) } - function handleSessionUpdated(session: SessionInfo) { + function handleSessionUpdated(session: SessionUpdate) { + const changed = session.revert !== undefined const prev = store.sessions[session.id]?.revert const next = session.revert ?? undefined setStore("sessions", session.id, session) - if (prev?.messageID === next?.messageID && prev?.partID === next?.partID) return + if (!changed || (prev?.messageID === next?.messageID && prev?.partID === next?.partID)) return clearClose(session.id) resetTodos(session.id, next) } diff --git a/packages/kilo-vscode/webview-ui/src/types/messages/extension-messages.ts b/packages/kilo-vscode/webview-ui/src/types/messages/extension-messages.ts index 6d4b5d9a0b4..4524f278dfa 100644 --- a/packages/kilo-vscode/webview-ui/src/types/messages/extension-messages.ts +++ b/packages/kilo-vscode/webview-ui/src/types/messages/extension-messages.ts @@ -5,7 +5,14 @@ import type { SessionMode } from "../../context/worktree-mode" import type { MarketplaceItem, MarketplaceInstalledMetadata } from "../marketplace" import type { ConnectionState, ServerInfo, SessionStatus } from "./connection" import type { FileAttachment, Part } from "./parts" -import type { CloudSessionInfo, Message, MessageLoadMode, SessionCloseReason, SessionInfo } from "./sessions" +import type { + CloudSessionInfo, + Message, + MessageLoadMode, + SessionCloseReason, + SessionInfo, + SessionUpdate, +} from "./sessions" import type { PermissionRequest } from "./permissions" import type { QuestionRequest, SuggestionRequest, TodoItem } from "./questions" import type { ModelSelection, Provider, ProviderAuthState } from "./providers" @@ -159,7 +166,7 @@ export interface SessionForkedMessage { export interface SessionUpdatedMessage { type: "sessionUpdated" - session: SessionInfo + session: SessionUpdate } export interface SessionDeletedMessage { diff --git a/packages/kilo-vscode/webview-ui/src/types/messages/sessions.ts b/packages/kilo-vscode/webview-ui/src/types/messages/sessions.ts index 99df5134296..23f9640def0 100644 --- a/packages/kilo-vscode/webview-ui/src/types/messages/sessions.ts +++ b/packages/kilo-vscode/webview-ui/src/types/messages/sessions.ts @@ -56,6 +56,8 @@ export interface SessionInfo { } | null } +export type SessionUpdate = Partial & Pick + // Cloud session info (from Kilo cloud API) export interface CloudSessionInfo { session_id: string diff --git a/packages/llm/AGENTS.md b/packages/llm/AGENTS.md index b20847da3bf..16a58fd8662 100644 --- a/packages/llm/AGENTS.md +++ b/packages/llm/AGENTS.md @@ -8,6 +8,10 @@ - In `Effect.gen`, yield yieldable errors directly (`return yield* new MyError(...)`) instead of `Effect.fail(new MyError(...))`. - Use `Effect.void` instead of `Effect.succeed(undefined)` when the successful value is intentionally void. +## Conventions + +Per-type constructors live on the type's namespace, not as top-level re-exports. Use `Message.user(...)`, `Message.assistant(...)`, `Message.tool(...)`, `ToolDefinition.make(...)`, `ToolCallPart.make(...)`, `ToolResultPart.make(...)`, `ToolChoice.make(...)`, `ToolChoice.named(...)`, `SystemPart.make(...)`, and `GenerationOptions.make(...)` directly. The top-level `LLM` namespace is reserved for the request-shaped call API: `LLM.request`, `LLM.generate`, `LLM.stream`, `LLM.model`, `LLM.updateRequest`, `LLM.generateObject`. Two ways to construct the same thing is one too many. + ## Tests - Use `testEffect(...)` from `test/lib/effect.ts` for tests requiring Effect layers. @@ -166,12 +170,12 @@ If you find yourself copying a 3-to-5-line snippet between two protocols, lift i Tool loops are represented in common messages and events: ```ts -const call = LLM.toolCall({ id: "call_1", name: "lookup", input: { query: "weather" } }) -const result = LLM.toolMessage({ id: "call_1", name: "lookup", result: { forecast: "sunny" } }) +const call = ToolCallPart.make({ id: "call_1", name: "lookup", input: { query: "weather" } }) +const result = Message.tool({ id: "call_1", name: "lookup", result: { forecast: "sunny" } }) const followUp = LLM.request({ model, - messages: [LLM.user("Weather?"), LLM.assistant([call]), result], + messages: [Message.user("Weather?"), Message.assistant([call]), result], }) ``` @@ -289,6 +293,6 @@ Filters apply in replay and record mode. Combine them with `RECORD=true` when re **Binary response bodies.** Most providers stream text (SSE, JSON). AWS Bedrock streams binary AWS event-stream frames whose CRC32 fields would be mangled by a UTF-8 round-trip — those bodies are stored as base64 with `bodyEncoding: "base64"` on the response snapshot. Detection is by `Content-Type` in `@opencode-ai/http-recorder` (currently `application/vnd.amazon.eventstream` and `application/octet-stream`); cassettes for SSE/JSON routes omit the field and decode as text. -**Matching strategies.** Replay defaults to structural matching, which finds an interaction by comparing method, URL, allow-listed headers, and the canonical JSON body. This is the right choice for tool loops because each round's request differs (the message history grows). For scenarios where successive requests are byte-identical and expect different responses (retries, polling), pass `dispatch: "sequential"` in `RecordReplayOptions` — replay then walks the cassette in record order via an internal cursor. `scriptedResponses` (in `test/lib/http.ts`) is the deterministic counterpart for tests that don't need a live provider; it scripts response bodies in order without reading from disk. +**Matching strategy.** Replay walks the cassette in record order via an internal cursor: the Nth runtime request is served by the Nth recorded interaction, and each one is validated by comparing method, URL, allow-listed headers, and the canonical JSON body. This handles tool loops (each round's request differs as history grows) and retry/polling scenarios (successive byte-identical requests with different responses) uniformly. If a test reorders its requests, re-record the cassette. `scriptedResponses` (in `test/lib/http.ts`) is the deterministic counterpart for tests that don't need a live provider; it scripts response bodies in order without reading from disk. Do not blanket re-record an entire test file when adding one cassette. `RECORD=true` rewrites every recorded case that runs, and provider streams contain volatile IDs, timestamps, fingerprints, and obfuscation fields. Prefer deleting the one cassette you intend to refresh, or run a focused test pattern that only registers the scenario you want to record. Keep stable existing cassettes unchanged unless their request shape or expected behavior changed. diff --git a/packages/llm/README.md b/packages/llm/README.md new file mode 100644 index 00000000000..321bf715bb1 --- /dev/null +++ b/packages/llm/README.md @@ -0,0 +1,129 @@ +# @opencode-ai/llm + +Schema-first LLM core for opencode. One typed request, response, event, and tool language; provider quirks live in adapters, not in calling code. + +```ts +import { Effect } from "effect" +import { LLM, LLMClient } from "@opencode-ai/llm" +import { OpenAI } from "@opencode-ai/llm/providers" + +const model = OpenAI.model("gpt-4o-mini", { apiKey: process.env.OPENAI_API_KEY }) + +const request = LLM.request({ + model, + system: "You are concise.", + prompt: "Say hello in one short sentence.", + generation: { maxTokens: 40 }, +}) + +const program = Effect.gen(function* () { + const response = yield* LLMClient.generate(request) + console.log(response.text) +}) +``` + +Run `LLMClient.stream(request)` instead of `generate` when you want incremental `LLMEvent`s. The event stream is provider-neutral — same shape across OpenAI Chat, OpenAI Responses, Anthropic Messages, Gemini, Bedrock Converse, and any OpenAI-compatible deployment. + +## Public API + +- **`LLM.request({...})`** — build a provider-neutral `LLMRequest`. Accepts ergonomic inputs (`system: string`, `prompt: string`) that normalize into the canonical Schema classes. +- **`LLM.generate` / `LLM.stream`** — re-exported from `LLMClient` for one-import use. +- **`LLM.user(...)` / `LLM.assistant(...)` / `LLM.toolMessage(...)`** — message constructors. +- **`LLM.toolCall(...)` / `LLM.toolResult(...)` / `LLM.toolDefinition(...)`** — tool-related parts. +- **`LLMClient.prepare(request)`** — compile a request through protocol body construction, validation, and HTTP preparation without sending. Useful for inspection and testing. +- **`LLMEvent.is.*`** — typed guards (`is.text`, `is.toolCall`, `is.requestFinish`, …) for filtering streams. + +## Caching + +Prompt caching is **on by default**. Every `LLMRequest` resolves to `cache: "auto"` unless the caller opts out with `cache: "none"`. Each protocol translates `CacheHint`s to its wire format (`cache_control` on Anthropic, `cachePoint` on Bedrock; OpenAI and Gemini do implicit caching server-side and don't need inline markers — auto is a no-op there). + +### Auto placement + +`"auto"` places three breakpoints — last tool definition, last system part, latest user message. The last-user-message boundary is the load-bearing detail: in a tool-use loop, a single user turn expands into many assistant/tool round-trips, all sharing that prefix. Caching at that boundary lets every intra-turn API call hit. + +The math justifies the default: Anthropic's 5-minute cache write is 1.25× base, read is 0.1×, so a single reuse within 5 minutes already wins. One-shot completions below the per-model minimum-cacheable-token threshold silently no-op on the wire, so the worst case is harmless. + +### Opting out + +```ts +LLM.request({ + model, + system, + prompt: "one-off question", + cache: "none", +}) +``` + +### Granular policy + +```ts +cache: { + tools?: boolean, + system?: boolean, + messages?: "latest-user-message" | "latest-assistant" | { tail: number }, + ttlSeconds?: number, // ≥ 3600 → 1h on Anthropic/Bedrock; else 5m +} +``` + +### Manual hints + +Inline `CacheHint` on any text / system / tool / tool-result part overrides automatic placement. The auto policy preserves manual hints; it only fills gaps. + +```ts +LLM.request({ + model, + system: [ + { type: "text", text: "stable system prompt", cache: { type: "ephemeral" } }, + ], + ... +}) +``` + +### Provider behavior table + +| Protocol | `cache: "auto"` | +| ----------------------- | ------------------------------------------------------------------------- | +| Anthropic Messages | emits up to 3 `cache_control` markers (4-breakpoint cap enforced) | +| Bedrock Converse | emits up to 3 `cachePoint` blocks (4-breakpoint cap enforced) | +| OpenAI Chat / Responses | no-op (implicit caching above 1024 tokens) | +| Gemini | no-op (implicit caching on 2.5+; explicit `CachedContent` is out-of-band) | + +Normalized cache usage is read back into `response.usage.cacheReadInputTokens` and `cacheWriteInputTokens` across every provider. + +## Providers + +Each provider exports a `model(...)` helper that records identity, protocol, capabilities, auth, and defaults. + +```ts +import { Anthropic } from "@opencode-ai/llm/providers" + +const model = Anthropic.model("claude-sonnet-4-6", { + apiKey: process.env.ANTHROPIC_API_KEY, +}) +``` + +Included providers: OpenAI, Anthropic, Google (Gemini), Amazon Bedrock, Azure OpenAI, Cloudflare, GitHub Copilot, OpenRouter, xAI, plus generic OpenAI-compatible helpers for DeepSeek, Cerebras, Groq, Fireworks, Together, etc. + +## Provider options & HTTP overlays + +Three escape hatches in order of stability: + +1. **`generation`** — portable knobs (`maxTokens`, `temperature`, `topP`, `topK`, penalties, seed, stop). +2. **`providerOptions: { : {...} }`** — typed-at-the-facade provider-specific knobs (OpenAI `promptCacheKey`, Anthropic `thinking`, Gemini `thinkingConfig`, OpenRouter routing). +3. **`http: { body, headers, query }`** — last-resort serializable overlays merged into the final HTTP request. Reach for this only when a stable typed path doesn't yet exist. + +Model-level defaults are overridden by request-level values for each axis. + +## Routes + +Adding a new model or deployment is usually 5–15 lines using `Route.make({ protocol, transport, ... })`. The four orthogonal pieces are protocol (body construction + stream parsing), transport (endpoint + auth + framing + encoding), defaults, and capabilities. See `AGENTS.md` for the architectural detail. + +## Effect + +This package is built on Effect. Public methods return `Effect` or `Stream`; provide `LLMClient.layer` (the default registers every shipped route) for runtime dispatch. The example at `example/tutorial.ts` is a runnable walkthrough. + +## See also + +- `AGENTS.md` — architecture, route construction, contributor guide +- `example/tutorial.ts` — runnable end-to-end walkthrough +- `test/provider/*.test.ts` — fixture-first protocol tests; `*.recorded.test.ts` files cover live cassettes diff --git a/packages/llm/example/tutorial.ts b/packages/llm/example/tutorial.ts index 5b84b152a4f..37d747dd8d3 100644 --- a/packages/llm/example/tutorial.ts +++ b/packages/llm/example/tutorial.ts @@ -78,7 +78,7 @@ const streamText = LLM.stream(request).pipe( Stream.tap((event) => Effect.sync(() => { if (event.type === "text-delta") process.stdout.write(`\ntext: ${event.text}`) - if (event.type === "request-finish") process.stdout.write(`\nfinish: ${event.reason}\n`) + if (event.type === "finish") process.stdout.write(`\nfinish: ${event.reason}\n`) }), ), Stream.runDrain, @@ -185,7 +185,7 @@ const FakeProtocol = Protocol.make({ event: Schema.String, initial: () => undefined, step: (_, frame) => Effect.succeed([undefined, [{ type: "text-delta", id: "text-0", text: frame }]] as const), - onHalt: () => [{ type: "request-finish", reason: "stop" }], + onHalt: () => [{ type: "finish", reason: "stop" }], }, }) diff --git a/packages/llm/src/cache-policy.ts b/packages/llm/src/cache-policy.ts new file mode 100644 index 00000000000..6ab7a049fe5 --- /dev/null +++ b/packages/llm/src/cache-policy.ts @@ -0,0 +1,111 @@ +// Apply an `LLMRequest.cache` policy by injecting `CacheHint`s onto the parts +// the policy designates. Runs once at compile time, before the per-protocol +// body builder, so the existing inline-hint lowering path handles the rest. +// +// The default `"auto"` shape places one breakpoint at the last tool definition, +// one at the last system part, and one at the latest user message. This +// matches what production agent harnesses (LangChain's caching middleware, +// kern-ai's 10x cost-reduction playbook) converge on for tool-use loops: the +// latest user message stays put while a single turn explodes into many +// assistant/tool round-trips, so caching at that boundary lets every +// intra-turn API call hit the prefix. +// +// Manual `cache: CacheHint` placements on individual parts are preserved — +// this function only fills gaps the caller left empty. +import { CacheHint, type CachePolicy, type CachePolicyObject } from "./schema/options" +import { LLMRequest, Message, ToolDefinition, type ContentPart } from "./schema/messages" + +const AUTO: CachePolicyObject = { + tools: true, + system: true, + messages: "latest-user-message", +} + +const NONE: CachePolicyObject = {} + +// Resolution rules: +// - undefined → "auto" — caching is on by default. The math favors it: +// Anthropic 5m-cache write is 1.25x base, read is 0.1x, +// so a single reuse within 5 minutes already wins. +// - "auto" → tools + system + latest user msg. +// - "none" → no auto placement; manual `CacheHint`s still flow. +// - object form → exactly what the caller asked for. +const resolve = (policy: CachePolicy | undefined): CachePolicyObject => { + if (policy === undefined || policy === "auto") return AUTO + if (policy === "none") return NONE + return policy +} + +// Protocols whose wire format ignores inline cache markers (OpenAI's implicit +// prefix caching, Gemini's implicit + out-of-band CachedContent). Skip the +// whole policy pass for these — emitting hints would be harmless but pointless. +const RESPECTS_INLINE_HINTS = new Set(["anthropic-messages", "bedrock-converse"]) + +const makeHint = (ttlSeconds: number | undefined): CacheHint => + ttlSeconds !== undefined ? new CacheHint({ type: "ephemeral", ttlSeconds }) : new CacheHint({ type: "ephemeral" }) + +const markLastTool = (tools: ReadonlyArray, hint: CacheHint): ReadonlyArray => { + if (tools.length === 0) return tools + const last = tools.length - 1 + if (tools[last]!.cache) return tools + return tools.map((tool, i) => (i === last ? new ToolDefinition({ ...tool, cache: hint }) : tool)) +} + +const markLastSystem = (system: LLMRequest["system"], hint: CacheHint): LLMRequest["system"] => { + if (system.length === 0) return system + const last = system.length - 1 + if (system[last]!.cache) return system + return system.map((part, i) => (i === last ? { ...part, cache: hint } : part)) +} + +const lastIndexOfRole = (messages: ReadonlyArray, role: Message["role"]): number => + messages.findLastIndex((m) => m.role === role) + +// Mark the last text part of `messages[index]`. If no text part exists, mark +// the last content part regardless of type — that's the breakpoint position +// in tool-result-only messages too. +const markMessageAt = (messages: ReadonlyArray, index: number, hint: CacheHint): ReadonlyArray => { + if (index < 0 || index >= messages.length) return messages + const target = messages[index]! + if (target.content.length === 0) return messages + const lastTextIndex = target.content.findLastIndex((part) => part.type === "text") + const markAt = lastTextIndex >= 0 ? lastTextIndex : target.content.length - 1 + const existing = target.content[markAt]! + if ("cache" in existing && existing.cache) return messages + const nextContent = target.content.map((part, i) => (i === markAt ? ({ ...part, cache: hint } as ContentPart) : part)) + const next = new Message({ ...target, content: nextContent }) + // Single pass over `messages`, substituting the one updated entry. Long + // conversations call this on every request, so avoid `.map()` here — its + // closure dispatch and identity copies show up in profiling. + const result = messages.slice() + result[index] = next + return result +} + +const markMessages = ( + messages: ReadonlyArray, + strategy: NonNullable, + hint: CacheHint, +): ReadonlyArray => { + if (messages.length === 0) return messages + if (strategy === "latest-user-message") return markMessageAt(messages, lastIndexOfRole(messages, "user"), hint) + if (strategy === "latest-assistant") return markMessageAt(messages, lastIndexOfRole(messages, "assistant"), hint) + const start = Math.max(0, messages.length - strategy.tail) + let next = messages + for (let i = start; i < messages.length; i++) next = markMessageAt(next, i, hint) + return next +} + +export const applyCachePolicy = (request: LLMRequest): LLMRequest => { + if (!RESPECTS_INLINE_HINTS.has(request.model.route)) return request + const policy = resolve(request.cache) + if (!policy.tools && !policy.system && !policy.messages) return request + + const hint = makeHint(policy.ttlSeconds) + const tools = policy.tools ? markLastTool(request.tools, hint) : request.tools + const system = policy.system ? markLastSystem(request.system, hint) : request.system + const messages = policy.messages ? markMessages(request.messages, policy.messages, hint) : request.messages + + if (tools === request.tools && system === request.system && messages === request.messages) return request + return LLMRequest.update(request, { tools, system, messages }) +} diff --git a/packages/llm/src/index.ts b/packages/llm/src/index.ts index f4adf4859a9..acf73b360e1 100644 --- a/packages/llm/src/index.ts +++ b/packages/llm/src/index.ts @@ -17,6 +17,7 @@ export type { ExecutableTools, Tool as ToolShape, ToolExecute, + ToolExecuteContext, Tools, ToolSchema, } from "./tool" diff --git a/packages/llm/src/llm.ts b/packages/llm/src/llm.ts index bca78c888aa..6f6728216ba 100644 --- a/packages/llm/src/llm.ts +++ b/packages/llm/src/llm.ts @@ -44,32 +44,8 @@ export type RequestInput = Omit< export const limits = modelLimits -export const text = Message.text - -export const system = SystemPart.make - -export const message = Message.make - -export const user = Message.user - -export const assistant = Message.assistant - export const model = modelRef -export const toolDefinition = ToolDefinition.make - -export const toolCall = ToolCallPart.make - -export const toolResult = ToolResultPart.make - -export const toolMessage = Message.tool - -export const toolChoiceName = ToolChoice.named - -export const toolChoice = ToolChoice.make - -export const generation = GenerationOptions.make - export const generate = LLMClient.generate export const stream = LLMClient.stream @@ -95,10 +71,10 @@ export const request = (input: RequestInput) => { return new LLMRequest({ ...rest, system: SystemPart.content(requestSystem), - messages: [...(messages?.map(message) ?? []), ...(prompt === undefined ? [] : [user(prompt)])], - tools: tools?.map(toolDefinition) ?? [], - toolChoice: requestToolChoice ? toolChoice(requestToolChoice) : undefined, - generation: requestGeneration === undefined ? undefined : generation(requestGeneration), + messages: [...(messages?.map(Message.make) ?? []), ...(prompt === undefined ? [] : [Message.user(prompt)])], + tools: tools?.map(ToolDefinition.make) ?? [], + toolChoice: requestToolChoice ? ToolChoice.make(requestToolChoice) : undefined, + generation: requestGeneration === undefined ? undefined : GenerationOptions.make(requestGeneration), providerOptions: requestProviderOptions, http: requestHttp === undefined ? undefined : HttpOptions.make(requestHttp), }) diff --git a/packages/llm/src/protocols/anthropic-messages.ts b/packages/llm/src/protocols/anthropic-messages.ts index a426807c028..e27af18426e 100644 --- a/packages/llm/src/protocols/anthropic-messages.ts +++ b/packages/llm/src/protocols/anthropic-messages.ts @@ -17,6 +17,7 @@ import { } from "../schema" import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared" import * as Cache from "./utils/cache" +import { Lifecycle } from "./utils/lifecycle" import { ToolStream } from "./utils/tool-stream" const ADAPTER = "anthropic-messages" @@ -190,6 +191,7 @@ type AnthropicEvent = Schema.Schema.Type interface ParserState { readonly tools: ToolStream.State readonly usage?: Usage + readonly lifecycle: Lifecycle.State } const invalid = ProviderShared.invalidRequest @@ -404,34 +406,56 @@ const mapFinishReason = (reason: string | null | undefined): FinishReason => { return "unknown" } +// Anthropic reports the non-overlapping breakdown natively — its +// `input_tokens` is the *non-cached* count per the Messages API docs, with +// cache reads and writes as separate fields. We sum them to derive the +// inclusive `inputTokens` the rest of the contract expects. Extended +// thinking tokens are *not* broken out by Anthropic — they're billed as +// part of `output_tokens`, so `reasoningTokens` stays `undefined` and +// `outputTokens` carries the combined total. const mapUsage = (usage: AnthropicUsage | undefined): Usage | undefined => { if (!usage) return undefined + const nonCached = usage.input_tokens + const cacheRead = usage.cache_read_input_tokens ?? undefined + const cacheWrite = usage.cache_creation_input_tokens ?? undefined + const inputTokens = ProviderShared.sumTokens(nonCached, cacheRead, cacheWrite) return new Usage({ - inputTokens: usage.input_tokens, + inputTokens, outputTokens: usage.output_tokens, - cacheReadInputTokens: usage.cache_read_input_tokens ?? undefined, - cacheWriteInputTokens: usage.cache_creation_input_tokens ?? undefined, - totalTokens: ProviderShared.totalTokens(usage.input_tokens, usage.output_tokens, undefined), - native: usage, + nonCachedInputTokens: nonCached, + cacheReadInputTokens: cacheRead, + cacheWriteInputTokens: cacheWrite, + totalTokens: ProviderShared.totalTokens(inputTokens, usage.output_tokens, undefined), + providerMetadata: { anthropic: usage }, }) } // Anthropic emits usage on `message_start` and again on `message_delta` — the // final delta carries the authoritative totals. Right-biased merge: each -// field prefers `right` when defined, falls back to `left`. `totalTokens` is -// recomputed from the merged input/output to stay consistent. +// field prefers `right` when defined, falls back to `left`. `inputTokens` is +// recomputed from the merged breakdown so the inclusive total stays +// consistent with `nonCached + cacheRead + cacheWrite`. const mergeUsage = (left: Usage | undefined, right: Usage | undefined) => { if (!left) return right if (!right) return left - const inputTokens = right.inputTokens ?? left.inputTokens + const nonCachedInputTokens = right.nonCachedInputTokens ?? left.nonCachedInputTokens + const cacheReadInputTokens = right.cacheReadInputTokens ?? left.cacheReadInputTokens + const cacheWriteInputTokens = right.cacheWriteInputTokens ?? left.cacheWriteInputTokens + const inputTokens = ProviderShared.sumTokens(nonCachedInputTokens, cacheReadInputTokens, cacheWriteInputTokens) const outputTokens = right.outputTokens ?? left.outputTokens return new Usage({ inputTokens, outputTokens, - cacheReadInputTokens: right.cacheReadInputTokens ?? left.cacheReadInputTokens, - cacheWriteInputTokens: right.cacheWriteInputTokens ?? left.cacheWriteInputTokens, + nonCachedInputTokens, + cacheReadInputTokens, + cacheWriteInputTokens, totalTokens: ProviderShared.totalTokens(inputTokens, outputTokens, undefined), - native: { ...left.native, ...right.native }, + providerMetadata: { + anthropic: { + ...(left.providerMetadata?.["anthropic"] ?? {}), + ...(right.providerMetadata?.["anthropic"] ?? {}), + }, + }, }) } @@ -478,37 +502,45 @@ const onContentBlockStart = (state: ParserState, event: AnthropicEvent): StepRes if (!block) return [state, NO_EVENTS] if ((block.type === "tool_use" || block.type === "server_tool_use") && event.index !== undefined) { + const events: LLMEvent[] = [] + const lifecycle = Lifecycle.stepStart(state.lifecycle, events) return [ { ...state, + lifecycle, tools: ToolStream.start(state.tools, event.index, { id: block.id ?? String(event.index), name: block.name ?? "", providerExecuted: block.type === "server_tool_use", }), }, - NO_EVENTS, + [...events, LLMEvent.toolInputStart({ id: block.id ?? String(event.index), name: block.name ?? "" })], ] } if (block.type === "text" && block.text) { - return [state, [LLMEvent.textDelta({ id: `text-${event.index ?? 0}`, text: block.text })]] + const events: LLMEvent[] = [] + return [ + { ...state, lifecycle: Lifecycle.textDelta(state.lifecycle, events, `text-${event.index ?? 0}`, block.text) }, + events, + ] } if (block.type === "thinking" && block.thinking) { + const events: LLMEvent[] = [] return [ - state, - [ - LLMEvent.reasoningDelta({ - id: `reasoning-${event.index ?? 0}`, - text: block.thinking, - }), - ], + { + ...state, + lifecycle: Lifecycle.reasoningDelta(state.lifecycle, events, `reasoning-${event.index ?? 0}`, block.thinking), + }, + events, ] } const result = serverToolResultEvent(block) - return [state, result ? [result] : NO_EVENTS] + if (!result) return [state, NO_EVENTS] + const events: LLMEvent[] = [] + return [{ ...state, lifecycle: Lifecycle.stepStart(state.lifecycle, events) }, [...events, result]] } const onContentBlockDelta = Effect.fn("AnthropicMessages.onContentBlockDelta")(function* ( @@ -518,25 +550,37 @@ const onContentBlockDelta = Effect.fn("AnthropicMessages.onContentBlockDelta")(f const delta = event.delta if (delta?.type === "text_delta" && delta.text) { - return [state, [LLMEvent.textDelta({ id: `text-${event.index ?? 0}`, text: delta.text })]] satisfies StepResult + const events: LLMEvent[] = [] + return [ + { ...state, lifecycle: Lifecycle.textDelta(state.lifecycle, events, `text-${event.index ?? 0}`, delta.text) }, + events, + ] satisfies StepResult } if (delta?.type === "thinking_delta" && delta.thinking) { + const events: LLMEvent[] = [] return [ - state, - [LLMEvent.reasoningDelta({ id: `reasoning-${event.index ?? 0}`, text: delta.thinking })], + { + ...state, + lifecycle: Lifecycle.reasoningDelta(state.lifecycle, events, `reasoning-${event.index ?? 0}`, delta.thinking), + }, + events, ] satisfies StepResult } if (delta?.type === "signature_delta" && delta.signature) { + const events: LLMEvent[] = [] return [ - state, - [ - LLMEvent.reasoningEnd({ - id: `reasoning-${event.index ?? 0}`, - providerMetadata: anthropicMetadata({ signature: delta.signature }), - }), - ], + { + ...state, + lifecycle: Lifecycle.reasoningEnd( + state.lifecycle, + events, + `reasoning-${event.index ?? 0}`, + anthropicMetadata({ signature: delta.signature }), + ), + }, + events, ] satisfies StepResult } @@ -550,7 +594,10 @@ const onContentBlockDelta = Effect.fn("AnthropicMessages.onContentBlockDelta")(f "Anthropic Messages tool argument delta is missing its tool call", ) if (ToolStream.isError(result)) return yield* result - return [{ ...state, tools: result.tools }, result.event ? [result.event] : NO_EVENTS] satisfies StepResult + const events: LLMEvent[] = [] + const lifecycle = result.events.length ? Lifecycle.stepStart(state.lifecycle, events) : state.lifecycle + events.push(...result.events) + return [{ ...state, lifecycle, tools: result.tools }, events] satisfies StepResult } return [state, NO_EVENTS] satisfies StepResult @@ -562,23 +609,30 @@ const onContentBlockStop = Effect.fn("AnthropicMessages.onContentBlockStop")(fun ) { if (event.index === undefined) return [state, NO_EVENTS] satisfies StepResult const result = yield* ToolStream.finish(ADAPTER, state.tools, event.index) - return [{ ...state, tools: result.tools }, result.event ? [result.event] : NO_EVENTS] satisfies StepResult + const events: LLMEvent[] = [] + const resultEvents = result.events ?? [] + const lifecycle = resultEvents.length + ? Lifecycle.stepStart(state.lifecycle, events) + : Lifecycle.reasoningEnd( + Lifecycle.textEnd(state.lifecycle, events, `text-${event.index}`), + events, + `reasoning-${event.index}`, + ) + events.push(...resultEvents) + return [{ ...state, lifecycle, tools: result.tools }, events] satisfies StepResult }) const onMessageDelta = (state: ParserState, event: AnthropicEvent): StepResult => { const usage = mergeUsage(state.usage, mapUsage(event.usage)) - return [ - { ...state, usage }, - [ - LLMEvent.requestFinish({ - reason: mapFinishReason(event.delta?.stop_reason), - usage, - providerMetadata: event.delta?.stop_sequence - ? anthropicMetadata({ stopSequence: event.delta.stop_sequence }) - : undefined, - }), - ], - ] + const events: LLMEvent[] = [] + const lifecycle = Lifecycle.finish(state.lifecycle, events, { + reason: mapFinishReason(event.delta?.stop_reason), + usage, + providerMetadata: event.delta?.stop_sequence + ? anthropicMetadata({ stopSequence: event.delta.stop_sequence }) + : undefined, + }) + return [{ ...state, lifecycle, usage }, events] } const onError = (state: ParserState, event: AnthropicEvent): StepResult => [ @@ -612,7 +666,7 @@ export const protocol = Protocol.make({ }, stream: { event: Protocol.jsonEvent(AnthropicEvent), - initial: () => ({ tools: ToolStream.empty() }), + initial: () => ({ tools: ToolStream.empty(), lifecycle: Lifecycle.initial() }), step, }, }) diff --git a/packages/llm/src/protocols/bedrock-converse.ts b/packages/llm/src/protocols/bedrock-converse.ts index e2ba1ff3bea..7f5647c4a7b 100644 --- a/packages/llm/src/protocols/bedrock-converse.ts +++ b/packages/llm/src/protocols/bedrock-converse.ts @@ -17,6 +17,7 @@ import { JsonObject, optionalArray, ProviderShared } from "./shared" import { BedrockAuth, type Credentials as BedrockCredentials } from "./utils/bedrock-auth" import { BedrockCache } from "./utils/bedrock-cache" import { BedrockMedia } from "./utils/bedrock-media" +import { Lifecycle } from "./utils/lifecycle" import { ToolStream } from "./utils/tool-stream" const ADAPTER = "bedrock-converse" @@ -395,15 +396,22 @@ const mapFinishReason = (reason: string): FinishReason => { return "unknown" } +// AWS Bedrock Converse reports `inputTokens` (inclusive total) with +// `cacheReadInputTokens` and `cacheWriteInputTokens` as subsets. Pass +// the total through and derive the non-cached breakdown. Bedrock does +// not break reasoning out of `outputTokens` for any current model. const mapUsage = (usage: BedrockUsageSchema | undefined): Usage | undefined => { if (!usage) return undefined + const cacheTotal = (usage.cacheReadInputTokens ?? 0) + (usage.cacheWriteInputTokens ?? 0) + const nonCached = ProviderShared.subtractTokens(usage.inputTokens, cacheTotal) return new Usage({ inputTokens: usage.inputTokens, outputTokens: usage.outputTokens, - totalTokens: ProviderShared.totalTokens(usage.inputTokens, usage.outputTokens, usage.totalTokens), + nonCachedInputTokens: nonCached, cacheReadInputTokens: usage.cacheReadInputTokens, cacheWriteInputTokens: usage.cacheWriteInputTokens, - native: usage, + totalTokens: ProviderShared.totalTokens(usage.inputTokens, usage.outputTokens, usage.totalTokens), + providerMetadata: { bedrock: usage }, }) } @@ -413,45 +421,64 @@ interface ParserState { // `metadata` (carries usage). Hold the terminal event in state so `onHalt` // can emit exactly one finish after both chunks have had a chance to arrive. readonly pendingFinish: { readonly reason: FinishReason; readonly usage?: Usage } | undefined + readonly hasToolCalls: boolean + readonly lifecycle: Lifecycle.State } const step = (state: ParserState, event: BedrockEvent) => Effect.gen(function* () { if (event.contentBlockStart?.start?.toolUse) { const index = event.contentBlockStart.contentBlockIndex + const events: LLMEvent[] = [] + const lifecycle = Lifecycle.stepStart(state.lifecycle, events) return [ { ...state, + lifecycle, tools: ToolStream.start(state.tools, index, { id: event.contentBlockStart.start.toolUse.toolUseId, name: event.contentBlockStart.start.toolUse.name, }), }, - [], + [ + ...events, + LLMEvent.toolInputStart({ + id: event.contentBlockStart.start.toolUse.toolUseId, + name: event.contentBlockStart.start.toolUse.name, + }), + ], ] as const } if (event.contentBlockDelta?.delta?.text) { + const events: LLMEvent[] = [] return [ - state, - [ - LLMEvent.textDelta({ - id: `text-${event.contentBlockDelta.contentBlockIndex}`, - text: event.contentBlockDelta.delta.text, - }), - ], + { + ...state, + lifecycle: Lifecycle.textDelta( + state.lifecycle, + events, + `text-${event.contentBlockDelta.contentBlockIndex}`, + event.contentBlockDelta.delta.text, + ), + }, + events, ] as const } if (event.contentBlockDelta?.delta?.reasoningContent?.text) { + const events: LLMEvent[] = [] return [ - state, - [ - LLMEvent.reasoningDelta({ - id: `reasoning-${event.contentBlockDelta.contentBlockIndex}`, - text: event.contentBlockDelta.delta.reasoningContent.text, - }), - ], + { + ...state, + lifecycle: Lifecycle.reasoningDelta( + state.lifecycle, + events, + `reasoning-${event.contentBlockDelta.contentBlockIndex}`, + event.contentBlockDelta.delta.reasoningContent.text, + ), + }, + events, ] as const } @@ -465,12 +492,33 @@ const step = (state: ParserState, event: BedrockEvent) => "Bedrock Converse tool delta is missing its tool call", ) if (ToolStream.isError(result)) return yield* result - return [{ ...state, tools: result.tools }, result.event ? [result.event] : []] as const + const events: LLMEvent[] = [] + const lifecycle = result.events.length ? Lifecycle.stepStart(state.lifecycle, events) : state.lifecycle + events.push(...result.events) + return [{ ...state, lifecycle, tools: result.tools }, events] as const } if (event.contentBlockStop) { const result = yield* ToolStream.finish(ADAPTER, state.tools, event.contentBlockStop.contentBlockIndex) - return [{ ...state, tools: result.tools }, result.event ? [result.event] : []] as const + const events: LLMEvent[] = [] + const resultEvents = result.events ?? [] + const lifecycle = resultEvents.length + ? Lifecycle.stepStart(state.lifecycle, events) + : Lifecycle.reasoningEnd( + Lifecycle.textEnd(state.lifecycle, events, `text-${event.contentBlockStop.contentBlockIndex}`), + events, + `reasoning-${event.contentBlockStop.contentBlockIndex}`, + ) + events.push(...resultEvents) + return [ + { + ...state, + hasToolCalls: resultEvents.some(LLMEvent.is.toolCall) ? true : state.hasToolCalls, + lifecycle, + tools: result.tools, + }, + events, + ] as const } if (event.messageStop) { @@ -510,7 +558,15 @@ const framing = BedrockEventStream.framing(ADAPTER) const onHalt = (state: ParserState): ReadonlyArray => state.pendingFinish - ? [LLMEvent.requestFinish({ reason: state.pendingFinish.reason, usage: state.pendingFinish.usage })] + ? (() => { + const events: LLMEvent[] = [] + Lifecycle.finish(state.lifecycle, events, { + reason: + state.pendingFinish.reason === "stop" && state.hasToolCalls ? "tool-calls" : state.pendingFinish.reason, + usage: state.pendingFinish.usage, + }) + return events + })() : [] // ============================================================================= @@ -528,7 +584,12 @@ export const protocol = Protocol.make({ }, stream: { event: BedrockEvent, - initial: () => ({ tools: ToolStream.empty(), pendingFinish: undefined }), + initial: () => ({ + tools: ToolStream.empty(), + pendingFinish: undefined, + hasToolCalls: false, + lifecycle: Lifecycle.initial(), + }), step, onHalt, }, diff --git a/packages/llm/src/protocols/gemini.ts b/packages/llm/src/protocols/gemini.ts index 140da521a5a..6e0b82abba9 100644 --- a/packages/llm/src/protocols/gemini.ts +++ b/packages/llm/src/protocols/gemini.ts @@ -16,6 +16,7 @@ import { } from "../schema" import { JsonObject, optionalArray, ProviderShared } from "./shared" import { GeminiToolSchema } from "./utils/gemini-tool-schema" +import { Lifecycle } from "./utils/lifecycle" const ADAPTER = "gemini" export const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta" @@ -134,10 +135,9 @@ interface ParserState { readonly hasToolCalls: boolean readonly nextToolCallId: number readonly usage?: Usage + readonly lifecycle: Lifecycle.State } -const invalid = ProviderShared.invalidRequest - const mediaData = ProviderShared.mediaBytes // ============================================================================= @@ -281,15 +281,28 @@ const fromRequest = Effect.fn("Gemini.fromRequest")(function* (request: LLMReque // ============================================================================= // Stream Parsing // ============================================================================= +// Gemini reports `promptTokenCount` (inclusive total) with a +// `cachedContentTokenCount` subset. `candidatesTokenCount` is *exclusive* +// of `thoughtsTokenCount` — visible-only, not a total — so we sum the two +// to produce the inclusive `outputTokens` the rest of the contract expects. const mapUsage = (usage: GeminiUsage | undefined) => { if (!usage) return undefined + const cached = usage.cachedContentTokenCount + const nonCached = ProviderShared.subtractTokens(usage.promptTokenCount, cached) + // `candidatesTokenCount` is visible-only; sum with thoughts to produce the + // inclusive `outputTokens` the contract expects. Only compute the total + // when the visible component is reported — otherwise we'd fabricate an + // inclusive number from a partial breakdown. + const outputTokens = + usage.candidatesTokenCount !== undefined ? usage.candidatesTokenCount + (usage.thoughtsTokenCount ?? 0) : undefined return new Usage({ inputTokens: usage.promptTokenCount, - outputTokens: usage.candidatesTokenCount, + outputTokens, + nonCachedInputTokens: nonCached, + cacheReadInputTokens: cached, reasoningTokens: usage.thoughtsTokenCount, - cacheReadInputTokens: usage.cachedContentTokenCount, - totalTokens: ProviderShared.totalTokens(usage.promptTokenCount, usage.candidatesTokenCount, usage.totalTokenCount), - native: usage, + totalTokens: ProviderShared.totalTokens(usage.promptTokenCount, outputTokens, usage.totalTokenCount), + providerMetadata: { google: usage }, }) } @@ -311,7 +324,14 @@ const mapFinishReason = (finishReason: string | undefined, hasToolCalls: boolean const finish = (state: ParserState): ReadonlyArray => state.finishReason || state.usage - ? [LLMEvent.requestFinish({ reason: mapFinishReason(state.finishReason, state.hasToolCalls), usage: state.usage })] + ? (() => { + const events: LLMEvent[] = [] + Lifecycle.finish(state.lifecycle, events, { + reason: mapFinishReason(state.finishReason, state.hasToolCalls), + usage: state.usage, + }) + return events + })() : [] const step = (state: ParserState, event: GeminiEvent) => { @@ -328,21 +348,21 @@ const step = (state: ParserState, event: GeminiEvent) => { const events: LLMEvent[] = [] let hasToolCalls = nextState.hasToolCalls + let lifecycle = nextState.lifecycle let nextToolCallId = nextState.nextToolCallId for (const part of candidate.content.parts) { if ("text" in part && part.text.length > 0) { - events.push( - part.thought - ? LLMEvent.reasoningDelta({ id: "reasoning-0", text: part.text }) - : LLMEvent.textDelta({ id: "text-0", text: part.text }), - ) + lifecycle = part.thought + ? Lifecycle.reasoningDelta(lifecycle, events, "reasoning-0", part.text) + : Lifecycle.textDelta(lifecycle, events, "text-0", part.text) continue } if ("functionCall" in part) { const input = part.functionCall.args const id = `tool_${nextToolCallId++}` + lifecycle = Lifecycle.stepStart(lifecycle, events) events.push(LLMEvent.toolCall({ id, name: part.functionCall.name, input })) hasToolCalls = true } @@ -352,6 +372,7 @@ const step = (state: ParserState, event: GeminiEvent) => { { ...nextState, hasToolCalls, + lifecycle, nextToolCallId, finishReason: candidate.finishReason ?? nextState.finishReason, }, @@ -375,7 +396,7 @@ export const protocol = Protocol.make({ }, stream: { event: Protocol.jsonEvent(GeminiEvent), - initial: () => ({ hasToolCalls: false, nextToolCallId: 0 }), + initial: () => ({ hasToolCalls: false, nextToolCallId: 0, lifecycle: Lifecycle.initial() }), step, onHalt: finish, }, diff --git a/packages/llm/src/protocols/openai-chat.ts b/packages/llm/src/protocols/openai-chat.ts index 5d42c0a4e92..470a1473c40 100644 --- a/packages/llm/src/protocols/openai-chat.ts +++ b/packages/llm/src/protocols/openai-chat.ts @@ -16,6 +16,7 @@ import { } from "../schema" import { isRecord, JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared" import { OpenAIOptions } from "./utils/openai-options" +import { Lifecycle } from "./utils/lifecycle" import { ToolStream } from "./utils/tool-stream" const ADAPTER = "openai-chat" @@ -147,6 +148,7 @@ interface ParserState { readonly toolCallEvents: ReadonlyArray readonly usage?: Usage readonly finishReason?: FinishReason + readonly lifecycle: Lifecycle.State } const invalid = ProviderShared.invalidRequest @@ -290,15 +292,24 @@ const mapFinishReason = (reason: string | null | undefined): FinishReason => { return "unknown" } +// OpenAI Chat reports `prompt_tokens` (inclusive total) with a +// `cached_tokens` subset, and `completion_tokens` (inclusive total) with +// a `reasoning_tokens` subset. We pass the inclusive totals through and +// derive the non-cached breakdown so the `LLM.Usage` contract is +// satisfied on both sides. const mapUsage = (usage: OpenAIChatEvent["usage"]): Usage | undefined => { if (!usage) return undefined + const cached = usage.prompt_tokens_details?.cached_tokens + const reasoning = usage.completion_tokens_details?.reasoning_tokens + const nonCached = ProviderShared.subtractTokens(usage.prompt_tokens, cached) return new Usage({ inputTokens: usage.prompt_tokens, outputTokens: usage.completion_tokens, - reasoningTokens: usage.completion_tokens_details?.reasoning_tokens, - cacheReadInputTokens: usage.prompt_tokens_details?.cached_tokens, + nonCachedInputTokens: nonCached, + cacheReadInputTokens: cached, + reasoningTokens: reasoning, totalTokens: ProviderShared.totalTokens(usage.prompt_tokens, usage.completion_tokens, usage.total_tokens), - native: usage, + providerMetadata: { openai: usage }, }) } @@ -312,7 +323,9 @@ const step = (state: ParserState, event: OpenAIChatEvent) => const toolDeltas = delta?.tool_calls ?? [] let tools = state.tools - if (delta?.content) events.push(LLMEvent.textDelta({ id: "text-0", text: delta.content })) + let lifecycle = state.lifecycle + + if (delta?.content) lifecycle = Lifecycle.textDelta(lifecycle, events, "text-0", delta.content) for (const tool of toolDeltas) { const result = ToolStream.appendOrStart( @@ -324,7 +337,8 @@ const step = (state: ParserState, event: OpenAIChatEvent) => ) if (ToolStream.isError(result)) return yield* result tools = result.tools - if (result.event) events.push(result.event) + if (result.events.length) lifecycle = Lifecycle.stepStart(lifecycle, events) + events.push(...result.events) } // Finalize accumulated tool inputs eagerly when finish_reason arrives so @@ -340,15 +354,20 @@ const step = (state: ParserState, event: OpenAIChatEvent) => toolCallEvents: finished?.events ?? state.toolCallEvents, usage, finishReason, + lifecycle, }, events, ] as const }) const finishEvents = (state: ParserState): ReadonlyArray => { + const events: LLMEvent[] = [] const hasToolCalls = state.toolCallEvents.length > 0 const reason = state.finishReason === "stop" && hasToolCalls ? "tool-calls" : state.finishReason - return [...state.toolCallEvents, ...(reason ? [LLMEvent.requestFinish({ reason, usage: state.usage })] : [])] + const lifecycle = state.toolCallEvents.length ? Lifecycle.stepStart(state.lifecycle, events) : state.lifecycle + events.push(...state.toolCallEvents) + if (reason) Lifecycle.finish(lifecycle, events, { reason, usage: state.usage }) + return events } // ============================================================================= @@ -368,7 +387,7 @@ export const protocol = Protocol.make({ }, stream: { event: Protocol.jsonEvent(OpenAIChatEvent), - initial: () => ({ tools: ToolStream.empty(), toolCallEvents: [] }), + initial: () => ({ tools: ToolStream.empty(), toolCallEvents: [], lifecycle: Lifecycle.initial() }), step, onHalt: finishEvents, }, diff --git a/packages/llm/src/protocols/openai-responses.ts b/packages/llm/src/protocols/openai-responses.ts index 14dc32130c9..7cf734f0278 100644 --- a/packages/llm/src/protocols/openai-responses.ts +++ b/packages/llm/src/protocols/openai-responses.ts @@ -17,6 +17,7 @@ import { } from "../schema" import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared" import { OpenAIOptions } from "./utils/openai-options" +import { Lifecycle } from "./utils/lifecycle" import { ToolStream } from "./utils/tool-stream" const ADAPTER = "openai-responses" @@ -165,6 +166,7 @@ type OpenAIResponsesEvent = Schema.Schema.Type interface ParserState { readonly tools: ToolStream.State readonly hasFunctionCall: boolean + readonly lifecycle: Lifecycle.State } const invalid = ProviderShared.invalidRequest @@ -276,15 +278,23 @@ const fromRequest = Effect.fn("OpenAIResponses.fromRequest")(function* (request: // ============================================================================= // Stream Parsing // ============================================================================= +// OpenAI Responses reports `input_tokens` (inclusive total) with a +// `cached_tokens` subset, and `output_tokens` (inclusive total) with a +// `reasoning_tokens` subset. Pass the totals through and derive the +// non-cached breakdown. const mapUsage = (usage: OpenAIResponsesUsage | null | undefined) => { if (!usage) return undefined + const cached = usage.input_tokens_details?.cached_tokens + const reasoning = usage.output_tokens_details?.reasoning_tokens + const nonCached = ProviderShared.subtractTokens(usage.input_tokens, cached) return new Usage({ inputTokens: usage.input_tokens, outputTokens: usage.output_tokens, - reasoningTokens: usage.output_tokens_details?.reasoning_tokens, - cacheReadInputTokens: usage.input_tokens_details?.cached_tokens, + nonCachedInputTokens: nonCached, + cacheReadInputTokens: cached, + reasoningTokens: reasoning, totalTokens: ProviderShared.totalTokens(usage.input_tokens, usage.output_tokens, usage.total_tokens), - native: usage, + providerMetadata: { openai: usage }, }) } @@ -370,30 +380,39 @@ type StepResult = readonly [ParserState, ReadonlyArray] const NO_EVENTS: StepResult["1"] = [] // `response.completed` / `response.incomplete` are clean finishes that emit a -// `request-finish` event; `response.failed` is a hard failure that emits a +// `finish` event; `response.failed` is a hard failure that emits a // `provider-error`. All three end the stream — kept in one set so `step` and // the protocol's `terminal` predicate stay in sync. const TERMINAL_TYPES = new Set(["response.completed", "response.incomplete", "response.failed"]) const onOutputTextDelta = (state: ParserState, event: OpenAIResponsesEvent): StepResult => { if (!event.delta) return [state, NO_EVENTS] - return [state, [LLMEvent.textDelta({ id: event.item_id ?? "text-0", text: event.delta })]] + const events: LLMEvent[] = [] + return [ + { ...state, lifecycle: Lifecycle.textDelta(state.lifecycle, events, event.item_id ?? "text-0", event.delta) }, + events, + ] } const onOutputItemAdded = (state: ParserState, event: OpenAIResponsesEvent): StepResult => { const item = event.item if (item?.type !== "function_call" || !item.id) return [state, NO_EVENTS] + const providerMetadata = openaiMetadata({ itemId: item.id }) + const events: LLMEvent[] = [] + const lifecycle = Lifecycle.stepStart(state.lifecycle, events) return [ { + ...state, + lifecycle, hasFunctionCall: state.hasFunctionCall, tools: ToolStream.start(state.tools, item.id, { id: item.call_id ?? item.id, name: item.name ?? "", input: item.arguments ?? "", - providerMetadata: openaiMetadata({ itemId: item.id }), + providerMetadata, }), }, - NO_EVENTS, + [...events, LLMEvent.toolInputStart({ id: item.call_id ?? item.id, name: item.name ?? "", providerMetadata })], ] } @@ -410,10 +429,10 @@ const onFunctionCallArgumentsDelta = Effect.fn("OpenAIResponses.onFunctionCallAr "OpenAI Responses tool argument delta is missing its tool call", ) if (ToolStream.isError(result)) return yield* result - return [ - { hasFunctionCall: state.hasFunctionCall, tools: result.tools }, - result.event ? [result.event] : NO_EVENTS, - ] satisfies StepResult + const events: LLMEvent[] = [] + const lifecycle = result.events.length ? Lifecycle.stepStart(state.lifecycle, events) : state.lifecycle + events.push(...result.events) + return [{ ...state, lifecycle, tools: result.tools }, events] satisfies StepResult }) const onOutputItemDone = Effect.fn("OpenAIResponses.onOutputItemDone")(function* ( @@ -432,33 +451,46 @@ const onOutputItemDone = Effect.fn("OpenAIResponses.onOutputItemDone")(function* item.arguments === undefined ? yield* ToolStream.finish(ADAPTER, tools, item.id) : yield* ToolStream.finishWithInput(ADAPTER, tools, item.id, item.arguments) + const events: LLMEvent[] = [] + const resultEvents = result.events ?? [] + const lifecycle = resultEvents.length ? Lifecycle.stepStart(state.lifecycle, events) : state.lifecycle + events.push(...resultEvents) return [ - { hasFunctionCall: result.event ? true : state.hasFunctionCall, tools: result.tools }, - result.event ? [result.event] : NO_EVENTS, + { + ...state, + lifecycle, + hasFunctionCall: resultEvents.some(LLMEvent.is.toolCall) ? true : state.hasFunctionCall, + tools: result.tools, + }, + events, ] satisfies StepResult } - if (isHostedToolItem(item)) return [state, hostedToolEvents(item)] satisfies StepResult + if (isHostedToolItem(item)) { + const events: LLMEvent[] = [] + const lifecycle = Lifecycle.stepStart(state.lifecycle, events) + events.push(...hostedToolEvents(item)) + return [{ ...state, lifecycle }, events] satisfies StepResult + } return [state, NO_EVENTS] satisfies StepResult }) -const onResponseFinish = (state: ParserState, event: OpenAIResponsesEvent): StepResult => [ - state, - [ - LLMEvent.requestFinish({ - reason: mapFinishReason(event, state.hasFunctionCall), - usage: mapUsage(event.response?.usage), - providerMetadata: - event.response?.id || event.response?.service_tier - ? openaiMetadata({ - responseId: event.response.id, - serviceTier: event.response.service_tier, - }) - : undefined, - }), - ], -] +const onResponseFinish = (state: ParserState, event: OpenAIResponsesEvent): StepResult => { + const events: LLMEvent[] = [] + const lifecycle = Lifecycle.finish(state.lifecycle, events, { + reason: mapFinishReason(event, state.hasFunctionCall), + usage: mapUsage(event.response?.usage), + providerMetadata: + event.response?.id || event.response?.service_tier + ? openaiMetadata({ + responseId: event.response.id, + serviceTier: event.response.service_tier, + }) + : undefined, + }) + return [{ ...state, lifecycle }, events] +} const onResponseFailed = (state: ParserState, event: OpenAIResponsesEvent): StepResult => [ state, @@ -498,7 +530,7 @@ export const protocol = Protocol.make({ }, stream: { event: Protocol.jsonEvent(OpenAIResponsesEvent), - initial: () => ({ hasFunctionCall: false, tools: ToolStream.empty() }), + initial: () => ({ hasFunctionCall: false, tools: ToolStream.empty(), lifecycle: Lifecycle.initial() }), step, terminal: (event) => TERMINAL_TYPES.has(event.type), }, diff --git a/packages/llm/src/protocols/shared.ts b/packages/llm/src/protocols/shared.ts index c931353998e..b8067bbe905 100644 --- a/packages/llm/src/protocols/shared.ts +++ b/packages/llm/src/protocols/shared.ts @@ -42,6 +42,13 @@ export interface ToolAccumulator { * supplied total; otherwise falls back to `inputTokens + outputTokens` only * when at least one is defined. Returns `undefined` when neither input nor * output is known so routes don't publish a misleading `0`. + * + * Under the additive `LLM.Usage` contract, `inputTokens` and `outputTokens` + * are the non-cached input and visible output only. The provider-supplied + * `total` is the source of truth when present; the computed fallback + * under-counts cache and reasoning by design and exists mainly so + * Anthropic-style providers (which don't surface a total) still get a + * sensible aggregate on the input + output axes. */ export const totalTokens = ( inputTokens: number | undefined, @@ -53,6 +60,35 @@ export const totalTokens = ( return (inputTokens ?? 0) + (outputTokens ?? 0) } +/** + * Subtract `subtrahend` from `total`, clamping to zero if the provider + * reports a non-sensical breakdown (e.g. `cached_tokens > prompt_tokens`). + * Used by protocol mappers when deriving a non-overlapping breakdown field + * from a provider's inclusive total — `nonCachedInputTokens` from + * `inputTokens - cacheReadInputTokens - cacheWriteInputTokens`. + * + * If `total` is `undefined`, returns `undefined` (we don't fabricate + * counts). If `subtrahend` is `undefined`, returns `total` unchanged. The + * provider-native breakdown stays available on `Usage.native` for debugging. + */ +export const subtractTokens = (total: number | undefined, subtrahend: number | undefined): number | undefined => { + if (total === undefined) return undefined + if (subtrahend === undefined) return total + return Math.max(0, total - subtrahend) +} + +/** + * Sum a list of optional token counts, returning `undefined` only when + * every value is `undefined` (so we don't fabricate a `0`). Used by + * protocol mappers to derive the inclusive `inputTokens` total from a + * provider that natively reports a non-overlapping breakdown + * (e.g. Anthropic, whose `input_tokens` is already non-cached only). + */ +export const sumTokens = (...values: ReadonlyArray): number | undefined => { + if (values.every((value) => value === undefined)) return undefined + return values.reduce((acc, value) => acc + (value ?? 0), 0) +} + export const eventError = (route: string, message: string, raw?: string) => new LLMError({ module: "ProviderShared", diff --git a/packages/llm/src/protocols/utils/lifecycle.ts b/packages/llm/src/protocols/utils/lifecycle.ts new file mode 100644 index 00000000000..c249d75cee8 --- /dev/null +++ b/packages/llm/src/protocols/utils/lifecycle.ts @@ -0,0 +1,88 @@ +import { LLMEvent, type FinishReason, type ProviderMetadata, type Usage } from "../../schema" + +export interface State { + readonly stepStarted: boolean + readonly text: ReadonlySet + readonly reasoning: ReadonlySet +} + +export const initial = (): State => ({ stepStarted: false, text: new Set(), reasoning: new Set() }) + +export const stepStart = (state: State, events: LLMEvent[]): State => { + if (state.stepStarted) return state + events.push(LLMEvent.stepStart({ index: 0 })) + return { ...state, stepStarted: true } +} + +export const textDelta = (state: State, events: LLMEvent[], id: string, text: string): State => { + const stepped = stepStart(state, events) + if (stepped.text.has(id)) { + events.push(LLMEvent.textDelta({ id, text })) + return stepped + } + events.push(LLMEvent.textStart({ id }), LLMEvent.textDelta({ id, text })) + return { ...stepped, text: new Set([...stepped.text, id]) } +} + +export const reasoningDelta = (state: State, events: LLMEvent[], id: string, text: string): State => { + const stepped = stepStart(state, events) + if (stepped.reasoning.has(id)) { + events.push(LLMEvent.reasoningDelta({ id, text })) + return stepped + } + events.push(LLMEvent.reasoningStart({ id }), LLMEvent.reasoningDelta({ id, text })) + return { ...stepped, reasoning: new Set([...stepped.reasoning, id]) } +} + +export const reasoningEnd = ( + state: State, + events: LLMEvent[], + id: string, + providerMetadata?: ProviderMetadata, +): State => { + if (!state.reasoning.has(id)) return state + const stepped = stepStart(state, events) + events.push(LLMEvent.reasoningEnd({ id, providerMetadata })) + const reasoning = new Set(stepped.reasoning) + reasoning.delete(id) + return { ...stepped, reasoning } +} + +export const textEnd = (state: State, events: LLMEvent[], id: string, providerMetadata?: ProviderMetadata): State => { + if (!state.text.has(id)) return state + const stepped = stepStart(state, events) + events.push(LLMEvent.textEnd({ id, providerMetadata })) + const text = new Set(stepped.text) + text.delete(id) + return { ...stepped, text } +} + +const closeOpenBlocks = (state: State, events: LLMEvent[]): State => { + for (const id of state.reasoning) events.push(LLMEvent.reasoningEnd({ id })) + for (const id of state.text) events.push(LLMEvent.textEnd({ id })) + return { ...state, text: new Set(), reasoning: new Set() } +} + +export const finish = ( + state: State, + events: LLMEvent[], + input: { + readonly reason: FinishReason + readonly usage?: Usage + readonly providerMetadata?: ProviderMetadata + }, +): State => { + const stepped = closeOpenBlocks(stepStart(state, events), events) + events.push( + LLMEvent.stepFinish({ + index: 0, + reason: input.reason, + usage: input.usage, + providerMetadata: input.providerMetadata, + }), + LLMEvent.finish(input), + ) + return { ...stepped, stepStarted: false } +} + +export * as Lifecycle from "./lifecycle" diff --git a/packages/llm/src/protocols/utils/tool-stream.ts b/packages/llm/src/protocols/utils/tool-stream.ts index aa9c70f017b..8e07a64bfed 100644 --- a/packages/llm/src/protocols/utils/tool-stream.ts +++ b/packages/llm/src/protocols/utils/tool-stream.ts @@ -1,5 +1,5 @@ import { Effect } from "effect" -import { LLMError, LLMEvent, type ProviderMetadata, type ToolCall, type ToolInputDelta } from "../../schema" +import { LLMError, LLMEvent, type ProviderMetadata, type ToolCall } from "../../schema" import { eventError, parseToolInput, type ToolAccumulator } from "../shared" type StreamKey = string | number @@ -27,13 +27,13 @@ export type State = Partial> /** * Result of adding argument text to one pending tool call. It returns both the * next `tools` state and the updated `tool` because parsers often need the - * current id/name immediately. `event` is present only when new text arrived; - * metadata-only deltas update identity without emitting `tool-input-delta`. + * current id/name immediately. `events` contains lifecycle and delta events + * produced by the append; metadata-only deltas update identity without output. */ export interface AppendOutcome { readonly tools: State readonly tool: PendingTool - readonly event?: ToolInputDelta + readonly events: ReadonlyArray } /** Create empty accumulator state for one provider stream. */ @@ -49,7 +49,14 @@ const withoutTool = (tools: State, key: K): State => return next } -const inputDelta = (tool: PendingTool, text: string): ToolInputDelta => +const inputStart = (tool: PendingTool) => + LLMEvent.toolInputStart({ + id: tool.id, + name: tool.name, + providerMetadata: tool.providerMetadata, + }) + +const inputDelta = (tool: PendingTool, text: string) => LLMEvent.toolInputDelta({ id: tool.id, name: tool.name, @@ -76,11 +83,16 @@ const appendTool = ( key: K, tool: PendingTool, text: string, -): AppendOutcome => ({ - tools: withTool(tools, key, tool), - tool, - event: text.length === 0 ? undefined : inputDelta(tool, text), -}) +): AppendOutcome => { + const events: LLMEvent[] = [] + if (!tools[key]) events.push(inputStart(tool)) + if (text.length > 0) events.push(inputDelta(tool, text)) + return { + tools: withTool(tools, key, tool), + tool, + events, + } +} export const isError = (result: AppendOutcome | LLMError): result is LLMError => result instanceof LLMError @@ -121,7 +133,8 @@ export const appendOrStart = ( providerExecuted: current?.providerExecuted, providerMetadata: current?.providerMetadata, } - if (current && delta.text.length === 0 && current.id === id && current.name === name) return { tools, tool: current } + if (current && delta.text.length === 0 && current.id === id && current.name === name) + return { tools, tool: current, events: [] } return appendTool(tools, key, tool, delta.text) } @@ -139,7 +152,7 @@ export const appendExisting = ( ): AppendOutcome | LLMError => { const current = tools[key] if (!current) return eventError(route, missingToolMessage) - if (text.length === 0) return { tools, tool: current } + if (text.length === 0) return { tools, tool: current, events: [] } return appendTool(tools, key, { ...current, input: `${current.input}${text}` }, text) } @@ -152,7 +165,13 @@ export const finish = (route: string, tools: State, key: Effect.gen(function* () { const tool = tools[key] if (!tool) return { tools } - return { tools: withoutTool(tools, key), event: yield* toolCall(route, tool) } + return { + tools: withoutTool(tools, key), + events: [ + LLMEvent.toolInputEnd({ id: tool.id, name: tool.name, providerMetadata: tool.providerMetadata }), + yield* toolCall(route, tool), + ], + } }) /** @@ -164,7 +183,13 @@ export const finishWithInput = (route: string, tools: State Effect.gen(function* () { const tool = tools[key] if (!tool) return { tools } - return { tools: withoutTool(tools, key), event: yield* toolCall(route, tool, input) } + return { + tools: withoutTool(tools, key), + events: [ + LLMEvent.toolInputEnd({ id: tool.id, name: tool.name, providerMetadata: tool.providerMetadata }), + yield* toolCall(route, tool, input), + ], + } }) /** @@ -179,7 +204,14 @@ export const finishAll = (route: string, tools: State) = ) return { tools: empty(), - events: yield* Effect.forEach(pending, (tool) => toolCall(route, tool)), + events: yield* Effect.forEach(pending, (tool) => + toolCall(route, tool).pipe( + Effect.map((call) => [ + LLMEvent.toolInputEnd({ id: tool.id, name: tool.name, providerMetadata: tool.providerMetadata }), + call, + ]), + ), + ).pipe(Effect.map((events) => events.flat())), } }) diff --git a/packages/llm/src/route/client.ts b/packages/llm/src/route/client.ts index 734eedff213..2d9de2fd39d 100644 --- a/packages/llm/src/route/client.ts +++ b/packages/llm/src/route/client.ts @@ -8,6 +8,7 @@ import type { Transport, TransportRuntime } from "./transport" import { WebSocketExecutor } from "./transport" import type { Service as WebSocketExecutorService } from "./transport/websocket" import type { Protocol } from "./protocol" +import { applyCachePolicy } from "../cache-policy" import * as ProviderShared from "../protocols/shared" import * as ToolRuntime from "../tool-runtime" import type { Tools } from "../tool" @@ -400,7 +401,7 @@ export function make( // validated provider body plus transport-private prepared data, but does not // execute transport. const compile = Effect.fn("LLM.compile")(function* (request: LLMRequest) { - const resolved = resolveRequestOptions(request) + const resolved = applyCachePolicy(resolveRequestOptions(request)) const route = registeredRoute(resolved.model.route) if (!route) return yield* noRoute(resolved.model) diff --git a/packages/llm/src/schema/errors.ts b/packages/llm/src/schema/errors.ts index 9bcc8e16941..39bf5b6252d 100644 --- a/packages/llm/src/schema/errors.ts +++ b/packages/llm/src/schema/errors.ts @@ -198,5 +198,6 @@ export class LLMError extends Schema.TaggedErrorClass()("LLM.Error", { */ export class ToolFailure extends Schema.TaggedErrorClass()("LLM.ToolFailure", { message: Schema.String, + error: Schema.optional(Schema.Defect), metadata: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)), }) {} diff --git a/packages/llm/src/schema/events.ts b/packages/llm/src/schema/events.ts index d0befe246eb..63c9b7b7df5 100644 --- a/packages/llm/src/schema/events.ts +++ b/packages/llm/src/schema/events.ts @@ -1,24 +1,78 @@ import { Schema } from "effect" -import { ContentBlockID, FinishReason, ProtocolID, ProviderMetadata, ResponseID, RouteID, ToolCallID } from "./ids" +import { ContentBlockID, FinishReason, ProtocolID, ProviderMetadata, RouteID, ToolCallID } from "./ids" import { ModelRef } from "./options" import { ToolResultValue } from "./messages" +/** + * Token usage reported by an LLM provider. + * + * **Inclusive totals** (match AI SDK / OpenAI / LangChain convention — a + * reader from any of those ecosystems sees the number they expect): + * + * - `inputTokens` — total prompt tokens, *including* cached reads/writes. + * - `outputTokens` — total output tokens, *including* reasoning. + * - `totalTokens` — provider-supplied total, or `inputTokens + outputTokens`. + * + * **Non-overlapping breakdown** (every field is independently meaningful; + * consumers never have to subtract): + * + * - `nonCachedInputTokens` — the "fresh" portion of the prompt. + * - `cacheReadInputTokens` — input tokens served from cache. + * - `cacheWriteInputTokens` — input tokens written to cache. + * - `reasoningTokens` — subset of `outputTokens` spent on hidden reasoning. + * + * **Invariant**: `nonCachedInputTokens + cacheReadInputTokens + + * cacheWriteInputTokens = inputTokens`, and `reasoningTokens ≤ outputTokens`. + * Each protocol mapper computes whichever side it doesn't get natively, + * with `Math.max(0, …)` clamping for defense against provider bugs. Because + * every breakdown field is stored independently, downstream consumers can + * read whatever they need (cost-by-category, context-pressure, AI-SDK-style + * inclusive total) without ever subtracting — eliminating the underflow + * class of bug where a clamped difference would silently store the wrong + * value. + * + * **Semantics by provider**: + * + * - OpenAI Chat / Responses / Gemini / Bedrock: provider reports inclusive + * `inputTokens` and an inclusive `outputTokens`; mapper subtracts to + * derive the breakdown. + * - Anthropic: provider reports the breakdown natively (`input_tokens` is + * non-cached only); mapper sums to derive the inclusive `inputTokens`. + * Anthropic does *not* break extended-thinking out of `output_tokens`, so + * `reasoningTokens` is `undefined` and `outputTokens` carries the + * combined total — a documented limitation of the Anthropic API. + * + * `providerMetadata` always carries the provider's raw usage payload — + * keyed by provider name (`{ openai: ... }`, `{ anthropic: ... }`, etc.) + * — for fields we don't normalize and for billing-level audit trails. + * Matches the same escape-hatch field on `LLMEvent`. + */ export class Usage extends Schema.Class("LLM.Usage")({ inputTokens: Schema.optional(Schema.Number), outputTokens: Schema.optional(Schema.Number), - reasoningTokens: Schema.optional(Schema.Number), + nonCachedInputTokens: Schema.optional(Schema.Number), cacheReadInputTokens: Schema.optional(Schema.Number), cacheWriteInputTokens: Schema.optional(Schema.Number), + reasoningTokens: Schema.optional(Schema.Number), totalTokens: Schema.optional(Schema.Number), - native: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)), -}) {} + providerMetadata: Schema.optional(ProviderMetadata), +}) { + /** + * Visible output tokens — `outputTokens` minus `reasoningTokens`, clamped + * to zero. The one place subtraction happens in this contract; the clamp + * means a provider reporting `reasoningTokens > outputTokens` produces a + * harmless zero rather than a negative that crashes downstream schemas. + */ + get visibleOutputTokens() { + return Math.max(0, (this.outputTokens ?? 0) - (this.reasoningTokens ?? 0)) + } -export const RequestStart = Schema.Struct({ - type: Schema.tag("request-start"), - id: ResponseID, - model: ModelRef, -}).annotate({ identifier: "LLM.Event.RequestStart" }) -export type RequestStart = Schema.Schema.Type + static from(input: UsageInput) { + return input instanceof Usage ? input : new Usage(input) + } +} + +export type UsageInput = Usage | ConstructorParameters[0] export const StepStart = Schema.Struct({ type: Schema.tag("step-start"), @@ -117,6 +171,7 @@ export const ToolError = Schema.Struct({ id: ToolCallID, name: Schema.String, message: Schema.String, + error: Schema.optional(Schema.Defect), providerMetadata: Schema.optional(ProviderMetadata), }).annotate({ identifier: "LLM.Event.ToolError" }) export type ToolError = Schema.Schema.Type @@ -130,13 +185,13 @@ export const StepFinish = Schema.Struct({ }).annotate({ identifier: "LLM.Event.StepFinish" }) export type StepFinish = Schema.Schema.Type -export const RequestFinish = Schema.Struct({ - type: Schema.tag("request-finish"), +export const Finish = Schema.Struct({ + type: Schema.tag("finish"), reason: FinishReason, usage: Schema.optional(Usage), providerMetadata: Schema.optional(ProviderMetadata), -}).annotate({ identifier: "LLM.Event.RequestFinish" }) -export type RequestFinish = Schema.Schema.Type +}).annotate({ identifier: "LLM.Event.Finish" }) +export type Finish = Schema.Schema.Type export const ProviderErrorEvent = Schema.Struct({ type: Schema.tag("provider-error"), @@ -147,7 +202,6 @@ export const ProviderErrorEvent = Schema.Struct({ export type ProviderErrorEvent = Schema.Schema.Type const llmEventTagged = Schema.Union([ - RequestStart, StepStart, TextStart, TextDelta, @@ -162,13 +216,15 @@ const llmEventTagged = Schema.Union([ ToolResult, ToolError, StepFinish, - RequestFinish, + Finish, ProviderErrorEvent, ]).pipe(Schema.toTaggedUnion("type")) type WithID = Omit & { readonly id: ID | string } +type WithUsage = Omit & { + readonly usage?: UsageInput +} -const responseID = (value: ResponseID | string) => ResponseID.make(value) const contentBlockID = (value: ContentBlockID | string) => ContentBlockID.make(value) const toolCallID = (value: ToolCallID | string) => ToolCallID.make(value) @@ -178,7 +234,6 @@ const toolCallID = (value: ToolCallID | string) => ToolCallID.make(value) * `events.filter(LLMEvent.guards["tool-call"])`. */ export const LLMEvent = Object.assign(llmEventTagged, { - requestStart: (input: WithID) => RequestStart.make({ ...input, id: responseID(input.id) }), stepStart: StepStart.make, textStart: (input: WithID) => TextStart.make({ ...input, id: contentBlockID(input.id) }), textDelta: (input: WithID) => TextDelta.make({ ...input, id: contentBlockID(input.id) }), @@ -197,11 +252,18 @@ export const LLMEvent = Object.assign(llmEventTagged, { toolCall: (input: WithID) => ToolCall.make({ ...input, id: toolCallID(input.id) }), toolResult: (input: WithID) => ToolResult.make({ ...input, id: toolCallID(input.id) }), toolError: (input: WithID) => ToolError.make({ ...input, id: toolCallID(input.id) }), - stepFinish: StepFinish.make, - requestFinish: RequestFinish.make, + stepFinish: (input: WithUsage) => + StepFinish.make({ + ...input, + usage: input.usage === undefined ? undefined : Usage.from(input.usage), + }), + finish: (input: WithUsage) => + Finish.make({ + ...input, + usage: input.usage === undefined ? undefined : Usage.from(input.usage), + }), providerError: ProviderErrorEvent.make, is: { - requestStart: llmEventTagged.guards["request-start"], stepStart: llmEventTagged.guards["step-start"], textStart: llmEventTagged.guards["text-start"], textDelta: llmEventTagged.guards["text-delta"], @@ -216,7 +278,7 @@ export const LLMEvent = Object.assign(llmEventTagged, { toolResult: llmEventTagged.guards["tool-result"], toolError: llmEventTagged.guards["tool-error"], stepFinish: llmEventTagged.guards["step-finish"], - requestFinish: llmEventTagged.guards["request-finish"], + finish: llmEventTagged.guards.finish, providerError: llmEventTagged.guards["provider-error"], }, }) diff --git a/packages/llm/src/schema/messages.ts b/packages/llm/src/schema/messages.ts index cc6b89a2c73..c38a66d33d8 100644 --- a/packages/llm/src/schema/messages.ts +++ b/packages/llm/src/schema/messages.ts @@ -1,6 +1,6 @@ import { Schema } from "effect" import { JsonSchema, MessageRole, ProviderMetadata } from "./ids" -import { CacheHint, GenerationOptions, HttpOptions, ModelRef, ProviderOptions } from "./options" +import { CacheHint, CachePolicy, GenerationOptions, HttpOptions, ModelRef, ProviderOptions } from "./options" const isRecord = (value: unknown): value is Record => typeof value === "object" && value !== null && !Array.isArray(value) @@ -206,6 +206,7 @@ export class LLMRequest extends Schema.Class("LLM.Request")({ providerOptions: Schema.optional(ProviderOptions), http: Schema.optional(HttpOptions), responseFormat: Schema.optional(ResponseFormat), + cache: Schema.optional(CachePolicy), metadata: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)), }) {} @@ -223,6 +224,7 @@ export namespace LLMRequest { providerOptions: request.providerOptions, http: request.http, responseFormat: request.responseFormat, + cache: request.cache, metadata: request.metadata, }) diff --git a/packages/llm/src/schema/options.ts b/packages/llm/src/schema/options.ts index 9a618aa8ae8..0f40196f7d4 100644 --- a/packages/llm/src/schema/options.ts +++ b/packages/llm/src/schema/options.ts @@ -200,3 +200,31 @@ export class CacheHint extends Schema.Class("LLM.CacheHint")({ type: Schema.Literals(["ephemeral", "persistent"]), ttlSeconds: Schema.optional(Schema.Number), }) {} + +// Auto-placement policy for prompt caching. The protocol-neutral lowering step +// reads this and injects `CacheHint`s at the configured boundaries; the +// per-protocol body builders then translate those hints into wire markers as +// usual. `"auto"` is the recommended default for agent loops — it places one +// breakpoint at the last tool definition, one at the last system part, and one +// at the latest user message. The combination of provider invalidation +// hierarchy (tools → system → messages) and Anthropic/Bedrock's 20-block +// lookback means three trailing breakpoints reliably cover the static prefix. +// +// Pass `"none"` to opt out entirely (the legacy behavior). Pass the granular +// object form to override individual choices. +export const CachePolicyObject = Schema.Struct({ + tools: Schema.optional(Schema.Boolean), + system: Schema.optional(Schema.Boolean), + messages: Schema.optional( + Schema.Union([ + Schema.Literal("latest-user-message"), + Schema.Literal("latest-assistant"), + Schema.Struct({ tail: Schema.Number }), + ]), + ), + ttlSeconds: Schema.optional(Schema.Number), +}) +export type CachePolicyObject = Schema.Schema.Type + +export const CachePolicy = Schema.Union([Schema.Literal("auto"), Schema.Literal("none"), CachePolicyObject]) +export type CachePolicy = Schema.Schema.Type diff --git a/packages/llm/src/tool-runtime.ts b/packages/llm/src/tool-runtime.ts index c6e716d45ee..ef527faa21b 100644 --- a/packages/llm/src/tool-runtime.ts +++ b/packages/llm/src/tool-runtime.ts @@ -12,6 +12,7 @@ import { ToolFailure, ToolResultPart, type ToolResultValue, + Usage, } from "./schema" import { type AnyTool, type ExecutableTools, type Tools, toDefinitions } from "./tool" @@ -72,31 +73,70 @@ export const stream = (options: StreamOptions): Stream.Strea tools: [...options.request.tools.filter((tool) => !runtimeToolNames.has(tool.name)), ...runtimeTools], }) - const loop = (request: LLMRequest, step: number): Stream.Stream => + const loop = ( + request: LLMRequest, + step: number, + usage: Usage | undefined, + providerMetadata: ProviderMetadata | undefined, + ): Stream.Stream => Stream.unwrap( Effect.gen(function* () { - const state: StepState = { assistantContent: [], toolCalls: [], finishReason: undefined } + const state: StepState = { + assistantContent: [], + toolCalls: [], + finishReason: undefined, + usage: undefined, + providerMetadata: undefined, + } const modelStream = options .stream(request) + .pipe(Stream.map((event) => indexStep(event, step))) .pipe(Stream.tap((event) => Effect.sync(() => accumulate(state, event)))) + .pipe(Stream.filter((event) => event.type !== "finish")) const continuation = Stream.unwrap( Effect.gen(function* () { - if (state.finishReason !== "tool-calls" || state.toolCalls.length === 0) return Stream.empty - if (options.toolExecution === "none") return Stream.empty + const totalUsage = addUsage(usage, state.usage) + const totalProviderMetadata = mergeProviderMetadata(providerMetadata, state.providerMetadata) + const finishStream = Stream.fromIterable([ + LLMEvent.finish({ + reason: state.finishReason ?? "unknown", + usage: totalUsage, + providerMetadata: totalProviderMetadata, + }), + ]) + + if (state.finishReason !== "tool-calls" || state.toolCalls.length === 0) return finishStream + if (options.toolExecution === "none") return finishStream const dispatched = yield* Effect.forEach( state.toolCalls, - (call) => dispatch(tools, call).pipe(Effect.map((result) => [call, result] as const)), + (call) => + dispatch(tools, call).pipe(Effect.map((result) => [call, result.result, result.error] as const)), { concurrency }, ) - const resultStream = Stream.fromIterable(dispatched.flatMap(([call, result]) => emitEvents(call, result))) + const resultStream = Stream.fromIterable( + dispatched.flatMap(([call, result, error]) => emitEvents(call, result, error)), + ) - if (!options.stopWhen) return resultStream - if (options.stopWhen({ step, request })) return resultStream + if (!options.stopWhen) return resultStream.pipe(Stream.concat(finishStream)) + if (options.stopWhen({ step, request })) return resultStream.pipe(Stream.concat(finishStream)) - return resultStream.pipe(Stream.concat(loop(followUpRequest(request, state, dispatched), step + 1))) + return resultStream.pipe( + Stream.concat( + loop( + followUpRequest( + request, + state, + dispatched.map(([call, result]) => [call, result] as const), + ), + step + 1, + totalUsage, + totalProviderMetadata, + ), + ), + ) }), ) @@ -104,13 +144,21 @@ export const stream = (options: StreamOptions): Stream.Strea }), ) - return loop(initialRequest, 0) + return loop(initialRequest, 0, undefined, undefined) +} + +const indexStep = (event: LLMEvent, index: number): LLMEvent => { + if (event.type === "step-start") return LLMEvent.stepStart({ index }) + if (event.type === "step-finish") return LLMEvent.stepFinish({ ...event, index }) + return event } interface StepState { assistantContent: ContentPart[] toolCalls: ToolCallPart[] finishReason: FinishReason | undefined + usage: Usage | undefined + providerMetadata: ProviderMetadata | undefined } const accumulate = (state: StepState, event: LLMEvent) => { @@ -154,11 +202,45 @@ const accumulate = (state: StepState, event: LLMEvent) => { ) return } - if (event.type === "request-finish") { - state.finishReason = event.reason + if (event.type === "step-finish") { + state.finishReason = event.reason === "stop" && state.toolCalls.length > 0 ? "tool-calls" : event.reason + state.usage = addUsage(state.usage, event.usage) + state.providerMetadata = mergeProviderMetadata(state.providerMetadata, event.providerMetadata) + return + } + if (event.type === "finish") { + state.finishReason ??= event.reason + state.usage ??= event.usage + state.providerMetadata = mergeProviderMetadata(state.providerMetadata, event.providerMetadata) } } +const addUsage = (left: Usage | undefined, right: Usage | undefined) => { + if (!left) return right + if (!right) return left + type UsageKey = + | "inputTokens" + | "outputTokens" + | "nonCachedInputTokens" + | "cacheReadInputTokens" + | "cacheWriteInputTokens" + | "reasoningTokens" + | "totalTokens" + const sum = (key: UsageKey) => + left[key] === undefined && right[key] === undefined ? undefined : (left[key] ?? 0) + (right[key] ?? 0) + + return new Usage({ + inputTokens: sum("inputTokens"), + outputTokens: sum("outputTokens"), + nonCachedInputTokens: sum("nonCachedInputTokens"), + cacheReadInputTokens: sum("cacheReadInputTokens"), + cacheWriteInputTokens: sum("cacheWriteInputTokens"), + reasoningTokens: sum("reasoningTokens"), + totalTokens: sum("totalTokens"), + providerMetadata: mergeProviderMetadata(left.providerMetadata, right.providerMetadata), + }) +} + const sameProviderMetadata = (left: ProviderMetadata | undefined, right: ProviderMetadata | undefined) => left === right || JSON.stringify(left) === JSON.stringify(right) @@ -194,23 +276,27 @@ const appendStreamingText = ( state.assistantContent.push({ type, text, providerMetadata }) } -const dispatch = (tools: Tools, call: ToolCallPart): Effect.Effect => { +const dispatch = (tools: Tools, call: ToolCallPart): Effect.Effect<{ result: ToolResultValue; error?: unknown }> => { const tool = tools[call.name] - if (!tool) return Effect.succeed({ type: "error" as const, value: `Unknown tool: ${call.name}` }) + if (!tool) return Effect.succeed({ result: { type: "error" as const, value: `Unknown tool: ${call.name}` } }) if (!tool.execute) - return Effect.succeed({ type: "error" as const, value: `Tool has no execute handler: ${call.name}` }) + return Effect.succeed({ result: { type: "error" as const, value: `Tool has no execute handler: ${call.name}` } }) - return decodeAndExecute(tool, call.input).pipe( + return decodeAndExecute(tool, call).pipe( Effect.catchTag("LLM.ToolFailure", (failure) => - Effect.succeed({ type: "error" as const, value: failure.message } satisfies ToolResultValue), + Effect.succeed({ + result: { type: "error" as const, value: failure.message } satisfies ToolResultValue, + error: failure.error, + }), ), + Effect.map((result) => ("result" in result ? result : { result })), ) } -const decodeAndExecute = (tool: AnyTool, input: unknown): Effect.Effect => - tool._decode(input).pipe( +const decodeAndExecute = (tool: AnyTool, call: ToolCallPart): Effect.Effect => + tool._decode(call.input).pipe( Effect.mapError((error) => new ToolFailure({ message: `Invalid tool input: ${error.message}` })), - Effect.flatMap((decoded) => tool.execute!(decoded)), + Effect.flatMap((decoded) => tool.execute!(decoded, { id: call.id, name: call.name })), Effect.flatMap((value) => tool._encode(value).pipe( Effect.mapError( @@ -224,10 +310,10 @@ const decodeAndExecute = (tool: AnyTool, input: unknown): Effect.Effect ({ type: "json", value: encoded })), ) -const emitEvents = (call: ToolCallPart, result: ToolResultValue): ReadonlyArray => +const emitEvents = (call: ToolCallPart, result: ToolResultValue, error: unknown): ReadonlyArray => result.type === "error" ? [ - LLMEvent.toolError({ id: call.id, name: call.name, message: String(result.value) }), + LLMEvent.toolError({ id: call.id, name: call.name, message: String(result.value), error }), LLMEvent.toolResult({ id: call.id, name: call.name, result }), ] : [LLMEvent.toolResult({ id: call.id, name: call.name, result })] diff --git a/packages/llm/src/tool.ts b/packages/llm/src/tool.ts index 311c8798b6f..df0a1cd3d32 100644 --- a/packages/llm/src/tool.ts +++ b/packages/llm/src/tool.ts @@ -1,5 +1,5 @@ import { Effect, JsonSchema, Schema } from "effect" -import type { ToolDefinition as ToolDefinitionClass } from "./schema" +import type { ToolCallPart, ToolDefinition as ToolDefinitionClass } from "./schema" import { ToolDefinition, ToolFailure } from "./schema" /** @@ -8,9 +8,14 @@ import { ToolDefinition, ToolFailure } from "./schema" * beyond pure data conversion belongs in the handler closure. */ export type ToolSchema = Schema.Codec +export interface ToolExecuteContext { + readonly id: ToolCallPart["id"] + readonly name: ToolCallPart["name"] +} export type ToolExecute, Success extends ToolSchema> = ( params: Schema.Schema.Type, + context?: ToolExecuteContext, ) => Effect.Effect, ToolFailure> /** @@ -61,7 +66,7 @@ type TypedToolConfig = { type DynamicToolConfig = { readonly description: string readonly jsonSchema: JsonSchema.JsonSchema - readonly execute?: (params: unknown) => Effect.Effect + readonly execute?: (params: unknown, context?: ToolExecuteContext) => Effect.Effect } /** @@ -110,7 +115,7 @@ export function make, Success extends ToolSch export function make(config: { readonly description: string readonly jsonSchema: JsonSchema.JsonSchema - readonly execute: (params: unknown) => Effect.Effect + readonly execute: (params: unknown, context?: ToolExecuteContext) => Effect.Effect }): AnyExecutableTool export function make(config: { readonly description: string diff --git a/packages/llm/sst-env.d.ts b/packages/llm/sst-env.d.ts index 64441936d7a..f25b9714550 100644 --- a/packages/llm/sst-env.d.ts +++ b/packages/llm/sst-env.d.ts @@ -7,4 +7,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/llm/test/adapter.test.ts b/packages/llm/test/adapter.test.ts index 5ac8b9d818f..80349a5ae5a 100644 --- a/packages/llm/test/adapter.test.ts +++ b/packages/llm/test/adapter.test.ts @@ -51,7 +51,7 @@ const request = LLM.request({ const raiseEvent = (event: FakeEvent): import("../src/schema").LLMEvent => event.type === "finish" - ? { type: "request-finish", reason: event.reason } + ? { type: "finish", reason: event.reason } : { type: "text-delta", id: "text-0", text: event.text } const fakeProtocol = Protocol.make({ @@ -112,8 +112,8 @@ describe("llm route", () => { const events = Array.from(yield* llm.stream(request).pipe(Stream.runCollect)) const response = yield* llm.generate(request) - expect(events.map((event) => event.type)).toEqual(["text-delta", "request-finish"]) - expect(response.events.map((event) => event.type)).toEqual(["text-delta", "request-finish"]) + expect(events.map((event) => event.type)).toEqual(["text-delta", "finish"]) + expect(response.events.map((event) => event.type)).toEqual(["text-delta", "finish"]) }), ) diff --git a/packages/llm/test/cache-policy.test.ts b/packages/llm/test/cache-policy.test.ts new file mode 100644 index 00000000000..ac700b58fc7 --- /dev/null +++ b/packages/llm/test/cache-policy.test.ts @@ -0,0 +1,266 @@ +import { describe, expect, test } from "bun:test" +import { Effect } from "effect" +import { CacheHint, LLM, Message } from "../src" +import { LLMClient } from "../src/route" +import * as AnthropicMessages from "../src/protocols/anthropic-messages" +import * as BedrockConverse from "../src/protocols/bedrock-converse" +import * as Gemini from "../src/protocols/gemini" +import * as OpenAIChat from "../src/protocols/openai-chat" +import { applyCachePolicy } from "../src/cache-policy" +import { it } from "./lib/effect" + +const anthropicModel = AnthropicMessages.model({ + id: "claude-sonnet-4-5", + baseURL: "https://api.anthropic.test/v1/", + headers: { "x-api-key": "test" }, +}) + +const bedrockModel = BedrockConverse.model({ + id: "anthropic.claude-3-5-sonnet-20241022-v2:0", + credentials: { region: "us-east-1", accessKeyId: "fixture", secretAccessKey: "fixture" }, +}) + +const openaiModel = OpenAIChat.model({ + id: "gpt-4o-mini", + baseURL: "https://api.openai.test/v1/", + headers: { authorization: "Bearer test" }, +}) + +const geminiModel = Gemini.model({ + id: "gemini-2.5-flash", + baseURL: "https://generativelanguage.test/v1beta/", + headers: { "x-goog-api-key": "test" }, +}) + +describe("applyCachePolicy", () => { + it.effect("undefined cache resolves to 'auto' (the recommended default)", () => + Effect.gen(function* () { + const prepared = yield* LLMClient.prepare( + LLM.request({ + model: anthropicModel, + system: "You are concise.", + prompt: "hi", + }), + ) + + // No explicit cache field → auto policy fires → last system part + latest + // user message both get cache_control markers. + expect(prepared.body).toMatchObject({ + system: [{ type: "text", text: "You are concise.", cache_control: { type: "ephemeral" } }], + messages: [{ role: "user", content: [{ type: "text", text: "hi", cache_control: { type: "ephemeral" } }] }], + }) + }), + ) + + it.effect("'auto' marks the last tool, last system part, and latest user message on Anthropic", () => + Effect.gen(function* () { + const prepared = yield* LLMClient.prepare( + LLM.request({ + model: anthropicModel, + system: "Sys A", + tools: [{ name: "t1", description: "t1", inputSchema: { type: "object", properties: {} } }], + messages: [ + Message.user("first user"), + Message.assistant("assistant reply"), + Message.user("latest user message"), + ], + cache: "auto", + }), + ) + + expect(prepared.body).toMatchObject({ + tools: [{ name: "t1", cache_control: { type: "ephemeral" } }], + system: [{ type: "text", text: "Sys A", cache_control: { type: "ephemeral" } }], + messages: [ + { role: "user", content: [{ type: "text", text: "first user" }] }, + { role: "assistant", content: [{ type: "text", text: "assistant reply" }] }, + { + role: "user", + content: [{ type: "text", text: "latest user message", cache_control: { type: "ephemeral" } }], + }, + ], + }) + }), + ) + + it.effect("'auto' is a no-op on OpenAI (implicit caching protocol)", () => + Effect.gen(function* () { + const prepared = yield* LLMClient.prepare( + LLM.request({ + model: openaiModel, + system: "Sys", + prompt: "hi", + cache: "auto", + }), + ) + + const body = prepared.body as { messages: Array<{ content: unknown }> } + // OpenAI doesn't accept cache_control on messages — policy must skip. + const flat = JSON.stringify(body) + expect(flat).not.toContain("cache_control") + expect(flat).not.toContain("cachePoint") + }), + ) + + it.effect("'auto' is a no-op on Gemini (out-of-band caching protocol)", () => + Effect.gen(function* () { + const prepared = yield* LLMClient.prepare( + LLM.request({ + model: geminiModel, + system: "Sys", + prompt: "hi", + cache: "auto", + }), + ) + + const flat = JSON.stringify(prepared.body) + expect(flat).not.toContain("cache_control") + expect(flat).not.toContain("cachePoint") + }), + ) + + it.effect("'auto' on Bedrock emits cachePoint markers in the right places", () => + Effect.gen(function* () { + const prepared = yield* LLMClient.prepare( + LLM.request({ + model: bedrockModel, + system: "Sys", + tools: [{ name: "t1", description: "t1", inputSchema: { type: "object", properties: {} } }], + messages: [Message.user("first user"), Message.assistant("reply"), Message.user("latest user")], + cache: "auto", + }), + ) + + expect(prepared.body).toMatchObject({ + toolConfig: { + tools: [{ toolSpec: { name: "t1" } }, { cachePoint: { type: "default" } }], + }, + system: [{ text: "Sys" }, { cachePoint: { type: "default" } }], + messages: [ + { role: "user", content: [{ text: "first user" }] }, + { role: "assistant", content: [{ text: "reply" }] }, + { role: "user", content: [{ text: "latest user" }, { cachePoint: { type: "default" } }] }, + ], + }) + }), + ) + + it.effect("'none' disables auto placement even when manual hints exist", () => + Effect.gen(function* () { + const prepared = yield* LLMClient.prepare( + LLM.request({ + model: anthropicModel, + system: "Sys", + tools: [{ name: "t1", description: "t1", inputSchema: { type: "object", properties: {} } }], + prompt: "hi", + cache: "none", + }), + ) + + expect(prepared.body).toMatchObject({ + tools: [{ name: "t1", cache_control: undefined }], + system: [{ type: "text", text: "Sys", cache_control: undefined }], + }) + }), + ) + + it.effect("granular object form: tools-only marks just tools", () => + Effect.gen(function* () { + const prepared = yield* LLMClient.prepare( + LLM.request({ + model: anthropicModel, + system: "Sys", + tools: [{ name: "t1", description: "t1", inputSchema: { type: "object", properties: {} } }], + prompt: "hi", + cache: { tools: true }, + }), + ) + + expect(prepared.body).toMatchObject({ + tools: [{ name: "t1", cache_control: { type: "ephemeral" } }], + system: [{ type: "text", text: "Sys", cache_control: undefined }], + }) + }), + ) + + it.effect("auto policy preserves manual CacheHints on other parts", () => + Effect.gen(function* () { + const prepared = yield* LLMClient.prepare( + LLM.request({ + model: anthropicModel, + system: [ + { type: "text", text: "first system", cache: new CacheHint({ type: "ephemeral", ttlSeconds: 3600 }) }, + { type: "text", text: "last system" }, + ], + prompt: "hi", + cache: "auto", + }), + ) + + const body = prepared.body as { system: Array<{ text: string; cache_control?: unknown }> } + expect(body.system[0]?.cache_control).toEqual({ type: "ephemeral", ttl: "1h" }) + expect(body.system[1]?.cache_control).toEqual({ type: "ephemeral" }) + }), + ) + + it.effect("ttlSeconds in the policy flows through to wire markers", () => + Effect.gen(function* () { + const prepared = yield* LLMClient.prepare( + LLM.request({ + model: anthropicModel, + system: "Sys", + prompt: "hi", + cache: { system: true, ttlSeconds: 3600 }, + }), + ) + + expect(prepared.body).toMatchObject({ + system: [{ type: "text", text: "Sys", cache_control: { type: "ephemeral", ttl: "1h" } }], + }) + }), + ) + + it.effect("messages: { tail: 2 } marks the last 2 message boundaries", () => + Effect.gen(function* () { + const prepared = yield* LLMClient.prepare( + LLM.request({ + model: anthropicModel, + messages: [Message.user("u1"), Message.assistant("a1"), Message.user("u2"), Message.assistant("a2")], + cache: { messages: { tail: 2 } }, + }), + ) + + const body = prepared.body as { messages: Array<{ content: Array<{ cache_control?: unknown }> }> } + expect(body.messages[0]?.content[0]?.cache_control).toBeUndefined() + expect(body.messages[1]?.content[0]?.cache_control).toBeUndefined() + expect(body.messages[2]?.content[0]?.cache_control).toEqual({ type: "ephemeral" }) + expect(body.messages[3]?.content[0]?.cache_control).toEqual({ type: "ephemeral" }) + }), + ) + + it.effect("'latest-assistant' marks the last assistant message", () => + Effect.gen(function* () { + const prepared = yield* LLMClient.prepare( + LLM.request({ + model: anthropicModel, + messages: [Message.user("u1"), Message.assistant("a1"), Message.user("u2")], + cache: { messages: "latest-assistant" }, + }), + ) + + const body = prepared.body as { messages: Array<{ content: Array<{ cache_control?: unknown }> }> } + expect(body.messages[0]?.content[0]?.cache_control).toBeUndefined() + expect(body.messages[1]?.content[0]?.cache_control).toEqual({ type: "ephemeral" }) + expect(body.messages[2]?.content[0]?.cache_control).toBeUndefined() + }), + ) + + test("returns the same request reference when policy is a no-op (pure function)", () => { + const request = LLM.request({ + model: anthropicModel, + prompt: "hi", + cache: "none", + }) + expect(applyCachePolicy(request)).toBe(request) + }) +}) diff --git a/packages/llm/test/fixtures/recordings/anthropic-messages-cache/writes-then-reads-cache-control-on-identical-second-call.json b/packages/llm/test/fixtures/recordings/anthropic-messages-cache/writes-then-reads-cache-control-on-identical-second-call.json new file mode 100644 index 00000000000..8cf2be05c14 --- /dev/null +++ b/packages/llm/test/fixtures/recordings/anthropic-messages-cache/writes-then-reads-cache-control-on-identical-second-call.json @@ -0,0 +1,48 @@ +{ + "version": 1, + "metadata": { + "name": "anthropic-messages-cache/writes-then-reads-cache-control-on-identical-second-call", + "recordedAt": "2026-05-11T01:52:54.319Z", + "tags": ["prefix:anthropic-messages-cache", "provider:anthropic", "protocol:anthropic-messages", "cache"] + }, + "interactions": [ + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://api.anthropic.com/v1/messages", + "headers": { + "anthropic-version": "2023-06-01", + "content-type": "application/json" + }, + "body": "{\"model\":\"claude-haiku-4-5-20251001\",\"system\":[{\"type\":\"text\",\"text\":\"You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. \",\"cache_control\":{\"type\":\"ephemeral\"}}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"Say hi.\"}]}],\"stream\":true,\"max_tokens\":16,\"temperature\":0}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream; charset=utf-8" + }, + "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_01NSbhSJdF1R6Uz81RRKxd55\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":9,\"cache_creation_input_tokens\":5752,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":5752,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":2,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Hi.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"stop_details\":null},\"usage\":{\"input_tokens\":9,\"cache_creation_input_tokens\":5752,\"cache_read_input_tokens\":0,\"output_tokens\":5} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n" + } + }, + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://api.anthropic.com/v1/messages", + "headers": { + "anthropic-version": "2023-06-01", + "content-type": "application/json" + }, + "body": "{\"model\":\"claude-haiku-4-5-20251001\",\"system\":[{\"type\":\"text\",\"text\":\"You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. \",\"cache_control\":{\"type\":\"ephemeral\"}}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"Say hi.\"}]}],\"stream\":true,\"max_tokens\":16,\"temperature\":0}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream; charset=utf-8" + }, + "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_01W9dNB2vnT7HoPQmDfKyniu\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":9,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":5752,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":2,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Hi.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"stop_details\":null},\"usage\":{\"input_tokens\":9,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":5752,\"output_tokens\":5} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n" + } + } + ] +} diff --git a/packages/llm/test/fixtures/recordings/gemini-cache/reports-cachedcontenttokencount-on-identical-second-call.json b/packages/llm/test/fixtures/recordings/gemini-cache/reports-cachedcontenttokencount-on-identical-second-call.json new file mode 100644 index 00000000000..0145756887e --- /dev/null +++ b/packages/llm/test/fixtures/recordings/gemini-cache/reports-cachedcontenttokencount-on-identical-second-call.json @@ -0,0 +1,46 @@ +{ + "version": 1, + "metadata": { + "name": "gemini-cache/reports-cachedcontenttokencount-on-identical-second-call", + "recordedAt": "2026-05-11T01:55:40.600Z", + "tags": ["prefix:gemini-cache", "provider:google", "protocol:gemini", "cache"] + }, + "interactions": [ + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:streamGenerateContent?alt=sse", + "headers": { + "content-type": "application/json" + }, + "body": "{\"contents\":[{\"role\":\"user\",\"parts\":[{\"text\":\"Say hi.\"}]}],\"systemInstruction\":{\"parts\":[{\"text\":\"You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. \"}]},\"generationConfig\":{\"maxOutputTokens\":16,\"temperature\":0}}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream" + }, + "body": "" + } + }, + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:streamGenerateContent?alt=sse", + "headers": { + "content-type": "application/json" + }, + "body": "{\"contents\":[{\"role\":\"user\",\"parts\":[{\"text\":\"Say hi.\"}]}],\"systemInstruction\":{\"parts\":[{\"text\":\"You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. \"}]},\"generationConfig\":{\"maxOutputTokens\":16,\"temperature\":0}}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream" + }, + "body": "" + } + } + ] +} diff --git a/packages/llm/test/fixtures/recordings/openai-responses-cache/reports-cached-tokens-on-identical-second-call.json b/packages/llm/test/fixtures/recordings/openai-responses-cache/reports-cached-tokens-on-identical-second-call.json new file mode 100644 index 00000000000..25b561197c3 --- /dev/null +++ b/packages/llm/test/fixtures/recordings/openai-responses-cache/reports-cached-tokens-on-identical-second-call.json @@ -0,0 +1,46 @@ +{ + "version": 1, + "metadata": { + "name": "openai-responses-cache/reports-cached-tokens-on-identical-second-call", + "recordedAt": "2026-05-11T01:41:58.951Z", + "tags": ["prefix:openai-responses-cache", "provider:openai", "protocol:openai-responses", "cache"] + }, + "interactions": [ + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://api.openai.com/v1/responses", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"gpt-4.1-mini\",\"input\":[{\"role\":\"system\",\"content\":\"You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. \"},{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Say hi.\"}]}],\"prompt_cache_key\":\"recorded-cache-test\",\"max_output_tokens\":16,\"temperature\":0,\"stream\":true}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream; charset=utf-8" + }, + "body": "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_00b4acfe385b75d6006a0133e252e4819faecb37d96affd4bf\",\"object\":\"response\",\"created_at\":1778463714,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":16,\"max_tool_calls\":null,\"model\":\"gpt-4.1-mini-2025-04-14\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":\"recorded-cache-test\",\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"effort\":null,\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":0.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tools\":[],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}\n\nevent: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_00b4acfe385b75d6006a0133e252e4819faecb37d96affd4bf\",\"object\":\"response\",\"created_at\":1778463714,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":16,\"max_tool_calls\":null,\"model\":\"gpt-4.1-mini-2025-04-14\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":\"recorded-cache-test\",\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"effort\":null,\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":0.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tools\":[],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}\n\nevent: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_00b4acfe385b75d6006a0133e42ad8819f83824a88e1160e09\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":2}\n\nevent: response.content_part.added\ndata: {\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_00b4acfe385b75d6006a0133e42ad8819f83824a88e1160e09\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":3}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Hi\",\"item_id\":\"msg_00b4acfe385b75d6006a0133e42ad8819f83824a88e1160e09\",\"logprobs\":[],\"obfuscation\":\"NSLkknb2f6J7MB\",\"output_index\":0,\"sequence_number\":4}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".\",\"item_id\":\"msg_00b4acfe385b75d6006a0133e42ad8819f83824a88e1160e09\",\"logprobs\":[],\"obfuscation\":\"ywmEAhs1uKOLkln\",\"output_index\":0,\"sequence_number\":5}\n\nevent: response.output_text.done\ndata: {\"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_00b4acfe385b75d6006a0133e42ad8819f83824a88e1160e09\",\"logprobs\":[],\"output_index\":0,\"sequence_number\":6,\"text\":\"Hi.\"}\n\nevent: response.content_part.done\ndata: {\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_00b4acfe385b75d6006a0133e42ad8819f83824a88e1160e09\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Hi.\"},\"sequence_number\":7}\n\nevent: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_00b4acfe385b75d6006a0133e42ad8819f83824a88e1160e09\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Hi.\"}],\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":8}\n\nevent: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_00b4acfe385b75d6006a0133e252e4819faecb37d96affd4bf\",\"object\":\"response\",\"created_at\":1778463714,\"status\":\"completed\",\"background\":false,\"completed_at\":1778463716,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":16,\"max_tool_calls\":null,\"model\":\"gpt-4.1-mini-2025-04-14\",\"moderation\":null,\"output\":[{\"id\":\"msg_00b4acfe385b75d6006a0133e42ad8819f83824a88e1160e09\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Hi.\"}],\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":\"recorded-cache-test\",\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"effort\":null,\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":0.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tools\":[],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":4765,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens\":3,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":4768},\"user\":null,\"metadata\":{}},\"sequence_number\":9}\n\n" + } + }, + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://api.openai.com/v1/responses", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"gpt-4.1-mini\",\"input\":[{\"role\":\"system\",\"content\":\"You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. \"},{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Say hi.\"}]}],\"prompt_cache_key\":\"recorded-cache-test\",\"max_output_tokens\":16,\"temperature\":0,\"stream\":true}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream; charset=utf-8" + }, + "body": "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_06a66d5dbf005c28006a0133e48a28819d957163a92a5a56cc\",\"object\":\"response\",\"created_at\":1778463716,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":16,\"max_tool_calls\":null,\"model\":\"gpt-4.1-mini-2025-04-14\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":\"recorded-cache-test\",\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"effort\":null,\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":0.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tools\":[],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}\n\nevent: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_06a66d5dbf005c28006a0133e48a28819d957163a92a5a56cc\",\"object\":\"response\",\"created_at\":1778463716,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":16,\"max_tool_calls\":null,\"model\":\"gpt-4.1-mini-2025-04-14\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":\"recorded-cache-test\",\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"effort\":null,\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":0.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tools\":[],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}\n\nevent: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_06a66d5dbf005c28006a0133e6a2b0819d90b31eabe0bb0568\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":2}\n\nevent: response.content_part.added\ndata: {\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_06a66d5dbf005c28006a0133e6a2b0819d90b31eabe0bb0568\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":3}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Hi\",\"item_id\":\"msg_06a66d5dbf005c28006a0133e6a2b0819d90b31eabe0bb0568\",\"logprobs\":[],\"obfuscation\":\"qLgi78ygFGnuw7\",\"output_index\":0,\"sequence_number\":4}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".\",\"item_id\":\"msg_06a66d5dbf005c28006a0133e6a2b0819d90b31eabe0bb0568\",\"logprobs\":[],\"obfuscation\":\"dyQaYugaXCUfkYH\",\"output_index\":0,\"sequence_number\":5}\n\nevent: response.output_text.done\ndata: {\"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_06a66d5dbf005c28006a0133e6a2b0819d90b31eabe0bb0568\",\"logprobs\":[],\"output_index\":0,\"sequence_number\":6,\"text\":\"Hi.\"}\n\nevent: response.content_part.done\ndata: {\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_06a66d5dbf005c28006a0133e6a2b0819d90b31eabe0bb0568\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Hi.\"},\"sequence_number\":7}\n\nevent: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_06a66d5dbf005c28006a0133e6a2b0819d90b31eabe0bb0568\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Hi.\"}],\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":8}\n\nevent: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_06a66d5dbf005c28006a0133e48a28819d957163a92a5a56cc\",\"object\":\"response\",\"created_at\":1778463716,\"status\":\"completed\",\"background\":false,\"completed_at\":1778463718,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":16,\"max_tool_calls\":null,\"model\":\"gpt-4.1-mini-2025-04-14\",\"moderation\":null,\"output\":[{\"id\":\"msg_06a66d5dbf005c28006a0133e6a2b0819d90b31eabe0bb0568\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Hi.\"}],\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":\"recorded-cache-test\",\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"effort\":null,\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":0.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tools\":[],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":4765,\"input_tokens_details\":{\"cached_tokens\":4608},\"output_tokens\":3,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":4768},\"user\":null,\"metadata\":{}},\"sequence_number\":9}\n\n" + } + } + ] +} diff --git a/packages/llm/test/llm.test.ts b/packages/llm/test/llm.test.ts index e9ef58afa83..a20c48411eb 100644 --- a/packages/llm/test/llm.test.ts +++ b/packages/llm/test/llm.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from "bun:test" import { LLM, LLMResponse } from "../src" -import { LLMRequest, Message, ModelRef, ToolChoice, ToolDefinition } from "../src/schema" +import { LLMRequest, Message, ModelRef, ToolCallPart, ToolChoice, ToolDefinition, ToolResultPart } from "../src/schema" describe("llm constructors", () => { test("builds canonical schema classes from ergonomic input", () => { @@ -28,7 +28,7 @@ describe("llm constructors", () => { }) const updated = LLM.updateRequest(base, { generation: { maxTokens: 20 }, - messages: [...base.messages, LLM.assistant("Hi.")], + messages: [...base.messages, Message.assistant("Hi.")], }) expect(updated).toBeInstanceOf(LLMRequest) @@ -70,7 +70,7 @@ describe("llm constructors", () => { model: LLM.model({ id: "fake-model", provider: "fake", route: "openai-chat", baseURL: "https://fake.local" }), prompt: "Say hello.", }) - const updated = LLMRequest.update(base, { messages: [...base.messages, LLM.assistant("Hi.")] }) + const updated = LLMRequest.update(base, { messages: [...base.messages, Message.assistant("Hi.")] }) expect(updated).toBeInstanceOf(LLMRequest) expect(updated.id).toBe("req_1") @@ -91,18 +91,18 @@ describe("llm constructors", () => { }) test("builds tool choices from names and tools", () => { - const tool = LLM.toolDefinition({ name: "lookup", description: "Lookup data", inputSchema: { type: "object" } }) + const tool = ToolDefinition.make({ name: "lookup", description: "Lookup data", inputSchema: { type: "object" } }) expect(tool).toBeInstanceOf(ToolDefinition) - expect(LLM.toolChoice("lookup")).toEqual(new ToolChoice({ type: "tool", name: "lookup" })) - expect(LLM.toolChoiceName("required")).toEqual(new ToolChoice({ type: "tool", name: "required" })) - expect(LLM.toolChoice(tool)).toEqual(new ToolChoice({ type: "tool", name: "lookup" })) + expect(ToolChoice.make("lookup")).toEqual(new ToolChoice({ type: "tool", name: "lookup" })) + expect(ToolChoice.named("required")).toEqual(new ToolChoice({ type: "tool", name: "required" })) + expect(ToolChoice.make(tool)).toEqual(new ToolChoice({ type: "tool", name: "lookup" })) }) test("builds tool choice modes from reserved strings", () => { - expect(LLM.toolChoice("auto")).toEqual(new ToolChoice({ type: "auto" })) - expect(LLM.toolChoice("none")).toEqual(new ToolChoice({ type: "none" })) - expect(LLM.toolChoice("required")).toEqual(new ToolChoice({ type: "required" })) + expect(ToolChoice.make("auto")).toEqual(new ToolChoice({ type: "auto" })) + expect(ToolChoice.make("none")).toEqual(new ToolChoice({ type: "none" })) + expect(ToolChoice.make("required")).toEqual(new ToolChoice({ type: "required" })) expect( LLM.request({ model: LLM.model({ id: "fake-model", provider: "fake", route: "openai-chat", baseURL: "https://fake.local" }), @@ -113,11 +113,11 @@ describe("llm constructors", () => { }) test("builds assistant tool calls and tool result messages", () => { - const call = LLM.toolCall({ id: "call_1", name: "lookup", input: { query: "weather" } }) - const result = LLM.toolResult({ id: "call_1", name: "lookup", result: { temperature: 72 } }) + const call = ToolCallPart.make({ id: "call_1", name: "lookup", input: { query: "weather" } }) + const result = ToolResultPart.make({ id: "call_1", name: "lookup", result: { temperature: 72 } }) - expect(LLM.assistant([call]).content).toEqual([call]) - expect(LLM.toolMessage(result).content).toEqual([ + expect(Message.assistant([call]).content).toEqual([call]) + expect(Message.tool(result).content).toEqual([ { type: "tool-result", id: "call_1", name: "lookup", result: { type: "json", value: { temperature: 72 } } }, ]) }) @@ -127,7 +127,7 @@ describe("llm constructors", () => { LLMResponse.text({ events: [ { type: "text-delta", id: "text-0", text: "hi" }, - { type: "request-finish", reason: "stop" }, + { type: "finish", reason: "stop" }, ], }), ).toBe("hi") diff --git a/packages/llm/test/provider/anthropic-messages-cache.recorded.test.ts b/packages/llm/test/provider/anthropic-messages-cache.recorded.test.ts index b048d53ba0c..68b7e0a4ae1 100644 --- a/packages/llm/test/provider/anthropic-messages-cache.recorded.test.ts +++ b/packages/llm/test/provider/anthropic-messages-cache.recorded.test.ts @@ -20,6 +20,9 @@ const cacheRequest = LLM.request({ model, system: [{ type: "text", text: LARGE_CACHEABLE_SYSTEM, cache: new CacheHint({ type: "ephemeral" }) }], prompt: "Say hi.", + // Manual hint on the system part is the only marker we want here — skip the + // auto-policy's latest-user-message breakpoint so the cassette body matches. + cache: "none", generation: { maxTokens: 16, temperature: 0 }, }) @@ -28,7 +31,11 @@ const recorded = recordedTests({ provider: "anthropic", protocol: "anthropic-messages", requires: ["ANTHROPIC_API_KEY"], - options: { redactor: Redactor.defaults({ requestHeaders: { allow: ["content-type", "anthropic-version"] } }) }, + // Two identical requests in one cassette — replay walks the cassette in + // recording order so the second call replays the cached-hit interaction. + options: { + redactor: Redactor.defaults({ requestHeaders: { allow: ["content-type", "anthropic-version"] } }), + }, }) describe("Anthropic Messages cache recorded", () => { diff --git a/packages/llm/test/provider/anthropic-messages.recorded.test.ts b/packages/llm/test/provider/anthropic-messages.recorded.test.ts index aa5b258d3d5..5fefae51d40 100644 --- a/packages/llm/test/provider/anthropic-messages.recorded.test.ts +++ b/packages/llm/test/provider/anthropic-messages.recorded.test.ts @@ -1,7 +1,7 @@ import { Redactor } from "@opencode-ai/http-recorder" import { describe, expect } from "bun:test" import { Effect } from "effect" -import { LLM, LLMError } from "../../src" +import { LLM, LLMError, Message, ToolCallPart } from "../../src" import { LLMClient } from "../../src/route" import * as AnthropicMessages from "../../src/protocols/anthropic-messages" import { weatherToolName } from "../recorded-scenarios" @@ -16,12 +16,12 @@ const malformedToolOrderRequest = LLM.request({ id: "recorded_anthropic_malformed_tool_order", model, messages: [ - LLM.assistant([ - LLM.toolCall({ id: "call_1", name: weatherToolName, input: { city: "Paris" } }), + Message.assistant([ + ToolCallPart.make({ id: "call_1", name: weatherToolName, input: { city: "Paris" } }), { type: "text", text: "I will check the weather." }, ]), - LLM.toolMessage({ id: "call_1", name: weatherToolName, result: { temperature: "72F" } }), - LLM.user("Use that result to answer briefly."), + Message.tool({ id: "call_1", name: weatherToolName, result: { temperature: "72F" } }), + Message.user("Use that result to answer briefly."), ], tools: [{ name: weatherToolName, description: "Get weather", inputSchema: { type: "object", properties: {} } }], }) diff --git a/packages/llm/test/provider/anthropic-messages.test.ts b/packages/llm/test/provider/anthropic-messages.test.ts index 2f2b2a3e861..71204bcd639 100644 --- a/packages/llm/test/provider/anthropic-messages.test.ts +++ b/packages/llm/test/provider/anthropic-messages.test.ts @@ -1,6 +1,6 @@ import { describe, expect } from "bun:test" import { Effect } from "effect" -import { CacheHint, LLM, LLMError } from "../../src" +import { CacheHint, LLM, LLMError, Message, ToolCallPart, Usage } from "../../src" import { LLMClient } from "../../src/route" import * as AnthropicMessages from "../../src/protocols/anthropic-messages" import { it } from "../lib/effect" @@ -18,6 +18,9 @@ const request = LLM.request({ model, system: { type: "text", text: "You are concise.", cache: new CacheHint({ type: "ephemeral" }) }, prompt: "Say hello.", + // This fixture predates the `cache: "auto"` default; pin the policy off so + // existing wire-shape assertions only see the manual hint on the system part. + cache: "none", generation: { maxTokens: 20, temperature: 0 }, }) @@ -44,10 +47,11 @@ describe("Anthropic Messages route", () => { id: "req_tool_result", model, messages: [ - LLM.user("What is the weather?"), - LLM.assistant([LLM.toolCall({ id: "call_1", name: "lookup", input: { query: "weather" } })]), - LLM.toolMessage({ id: "call_1", name: "lookup", result: { forecast: "sunny" } }), + Message.user("What is the weather?"), + Message.assistant([ToolCallPart.make({ id: "call_1", name: "lookup", input: { query: "weather" } })]), + Message.tool({ id: "call_1", name: "lookup", result: { forecast: "sunny" } }), ], + cache: "none", }), ) @@ -73,7 +77,7 @@ describe("Anthropic Messages route", () => { LLM.request({ model, messages: [ - LLM.assistant([ + Message.assistant([ { type: "reasoning", text: "thinking", providerMetadata: { anthropic: { signature: "sig_1" } } }, ]), ], @@ -110,16 +114,17 @@ describe("Anthropic Messages route", () => { expect(response.text).toBe("Hello!") expect(response.reasoning).toBe("thinking") expect(response.usage).toMatchObject({ - inputTokens: 5, + inputTokens: 6, outputTokens: 2, + nonCachedInputTokens: 5, cacheReadInputTokens: 1, - totalTokens: 7, + totalTokens: 8, }) expect(response.events.find((event) => event.type === "reasoning-end")).toMatchObject({ providerMetadata: { anthropic: { signature: "sig_1" } }, }) expect(response.events.at(-1)).toMatchObject({ - type: "request-finish", + type: "finish", reason: "stop", providerMetadata: { anthropic: { stopSequence: "\n\nHuman:" } }, }) @@ -141,18 +146,46 @@ describe("Anthropic Messages route", () => { tools: [{ name: "lookup", description: "Lookup data", inputSchema: { type: "object" } }], }), ).pipe(Effect.provide(fixedResponse(body))) + const usage = new Usage({ + inputTokens: 5, + outputTokens: 1, + nonCachedInputTokens: 5, + cacheReadInputTokens: undefined, + cacheWriteInputTokens: undefined, + totalTokens: 6, + providerMetadata: { anthropic: { input_tokens: 5, output_tokens: 1 } }, + }) expect(response.toolCalls).toEqual([ - { type: "tool-call", id: "call_1", name: "lookup", input: { query: "weather" } }, + { + type: "tool-call", + id: "call_1", + name: "lookup", + input: { query: "weather" }, + providerExecuted: undefined, + providerMetadata: undefined, + }, ]) expect(response.events).toEqual([ + { type: "step-start", index: 0 }, + { type: "tool-input-start", id: "call_1", name: "lookup" }, { type: "tool-input-delta", id: "call_1", name: "lookup", text: '{"query"' }, { type: "tool-input-delta", id: "call_1", name: "lookup", text: ':"weather"}' }, - { type: "tool-call", id: "call_1", name: "lookup", input: { query: "weather" } }, + { type: "tool-input-end", id: "call_1", name: "lookup", providerMetadata: undefined }, + { + type: "tool-call", + id: "call_1", + name: "lookup", + input: { query: "weather" }, + providerExecuted: undefined, + providerMetadata: undefined, + }, + { type: "step-finish", index: 0, reason: "tool-calls", usage, providerMetadata: undefined }, { - type: "request-finish", + type: "finish", reason: "tool-calls", - usage: { inputTokens: 5, outputTokens: 1, totalTokens: 6, native: { input_tokens: 5, output_tokens: 1 } }, + providerMetadata: undefined, + usage, }, ]) }), @@ -242,7 +275,7 @@ describe("Anthropic Messages route", () => { providerMetadata: { anthropic: { blockType: "web_search_tool_result" } }, }) expect(response.text).toBe("Found it.") - expect(response.events.at(-1)).toMatchObject({ type: "request-finish", reason: "stop" }) + expect(response.events.at(-1)).toMatchObject({ type: "finish", reason: "stop" }) }), ) @@ -293,8 +326,8 @@ describe("Anthropic Messages route", () => { id: "req_round_trip", model, messages: [ - LLM.user("Search for something."), - LLM.assistant([ + Message.user("Search for something."), + Message.assistant([ { type: "tool-call", id: "srvtoolu_abc", @@ -311,7 +344,7 @@ describe("Anthropic Messages route", () => { }, { type: "text", text: "Found it." }, ]), - LLM.user("Thanks."), + Message.user("Thanks."), ], }), ) @@ -344,7 +377,7 @@ describe("Anthropic Messages route", () => { id: "req_unknown_server_tool", model, messages: [ - LLM.assistant([ + Message.assistant([ { type: "tool-result", id: "srvtoolu_abc", @@ -367,7 +400,7 @@ describe("Anthropic Messages route", () => { LLM.request({ id: "req_media", model, - messages: [LLM.user({ type: "media", mediaType: "image/png", data: "AAECAw==" })], + messages: [Message.user({ type: "media", mediaType: "image/png", data: "AAECAw==" })], }), ).pipe(Effect.flip) @@ -405,9 +438,9 @@ describe("Anthropic Messages route", () => { }, ], messages: [ - LLM.user("What's the weather?"), - LLM.assistant([LLM.toolCall({ id: "call_1", name: "lookup", input: {} })]), - LLM.toolMessage({ + Message.user("What's the weather?"), + Message.assistant([ToolCallPart.make({ id: "call_1", name: "lookup", input: {} })]), + Message.tool({ id: "call_1", name: "lookup", result: { temp: 72 }, @@ -490,7 +523,7 @@ describe("Anthropic Messages route", () => { }, ], system: [{ type: "text", text: "system-tail", cache: hint }], - messages: [LLM.user([{ type: "text", text: "message-tail", cache: hint }])], + messages: [Message.user([{ type: "text", text: "message-tail", cache: hint }])], }), ) diff --git a/packages/llm/test/provider/bedrock-converse-cache.recorded.test.ts b/packages/llm/test/provider/bedrock-converse-cache.recorded.test.ts index 23dd697b9a4..2771046f80f 100644 --- a/packages/llm/test/provider/bedrock-converse-cache.recorded.test.ts +++ b/packages/llm/test/provider/bedrock-converse-cache.recorded.test.ts @@ -27,6 +27,9 @@ const cacheRequest = LLM.request({ model, system: [{ type: "text", text: LARGE_CACHEABLE_SYSTEM, cache: new CacheHint({ type: "ephemeral" }) }], prompt: "Say hi.", + // Manual hint on the system part is the only marker we want here — skip the + // auto-policy's latest-user-message breakpoint so the cassette body matches. + cache: "none", generation: { maxTokens: 16, temperature: 0 }, }) @@ -35,6 +38,8 @@ const recorded = recordedTests({ provider: "amazon-bedrock", protocol: "bedrock-converse", requires: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"], + // Two identical requests in one cassette — replay walks the cassette in + // recording order so the second call replays the cached-hit interaction. }) describe("Bedrock Converse cache recorded", () => { diff --git a/packages/llm/test/provider/bedrock-converse.test.ts b/packages/llm/test/provider/bedrock-converse.test.ts index afadd89ac74..ffdd6e80082 100644 --- a/packages/llm/test/provider/bedrock-converse.test.ts +++ b/packages/llm/test/provider/bedrock-converse.test.ts @@ -2,7 +2,7 @@ import { EventStreamCodec } from "@smithy/eventstream-codec" import { fromUtf8, toUtf8 } from "@smithy/util-utf8" import { describe, expect } from "bun:test" import { Effect } from "effect" -import { CacheHint, LLM } from "../../src" +import { CacheHint, LLM, Message, ToolCallPart, ToolChoice } from "../../src" import { LLMClient } from "../../src/route" import * as BedrockConverse from "../../src/protocols/bedrock-converse" import { it } from "../lib/effect" @@ -63,6 +63,9 @@ const baseRequest = LLM.request({ model, system: "You are concise.", prompt: "Say hello.", + // Wire-shape assertions in this file predate the `cache: "auto"` default; + // pin the policy off so they only exercise the lowering path itself. + cache: "none", generation: { maxTokens: 64, temperature: 0 }, }) @@ -91,7 +94,7 @@ describe("Bedrock Converse route", () => { inputSchema: { type: "object", properties: { query: { type: "string" } }, required: ["query"] }, }, ], - toolChoice: LLM.toolChoice({ type: "required" }), + toolChoice: ToolChoice.make({ type: "required" }), }), ) @@ -121,10 +124,11 @@ describe("Bedrock Converse route", () => { id: "req_history", model, messages: [ - LLM.user("What is the weather?"), - LLM.assistant([LLM.toolCall({ id: "tool_1", name: "lookup", input: { query: "weather" } })]), - LLM.toolMessage({ id: "tool_1", name: "lookup", result: { forecast: "sunny" } }), + Message.user("What is the weather?"), + Message.assistant([ToolCallPart.make({ id: "tool_1", name: "lookup", input: { query: "weather" } })]), + Message.tool({ id: "tool_1", name: "lookup", result: { forecast: "sunny" } }), ], + cache: "none", }), ) @@ -165,12 +169,12 @@ describe("Bedrock Converse route", () => { const response = yield* LLMClient.generate(baseRequest).pipe(Effect.provide(fixedBytes(body))) expect(response.text).toBe("Hello!") - const finishes = response.events.filter((event) => event.type === "request-finish") + const finishes = response.events.filter((event) => event.type === "finish") // Bedrock splits the finish across `messageStop` (carries reason) and // `metadata` (carries usage). We consolidate them into a single - // terminal `request-finish` event with both. + // terminal `finish` event with both. expect(finishes).toHaveLength(1) - expect(finishes[0]).toMatchObject({ type: "request-finish", reason: "stop" }) + expect(finishes[0]).toMatchObject({ type: "finish", reason: "stop" }) expect(response.usage).toMatchObject({ inputTokens: 5, outputTokens: 2, @@ -209,7 +213,7 @@ describe("Bedrock Converse route", () => { { type: "tool-input-delta", id: "tool_1", name: "lookup", text: '{"query"' }, { type: "tool-input-delta", id: "tool_1", name: "lookup", text: ':"weather"}' }, ]) - expect(response.events.at(-1)).toMatchObject({ type: "request-finish", reason: "tool-calls" }) + expect(response.events.at(-1)).toMatchObject({ type: "finish", reason: "tool-calls" }) }), ) @@ -290,8 +294,8 @@ describe("Bedrock Converse route", () => { model, system: [{ type: "text", text: "System prefix.", cache }], messages: [ - LLM.user([{ type: "text", text: "User prefix.", cache }]), - LLM.assistant([{ type: "text", text: "Assistant prefix.", cache }]), + Message.user([{ type: "text", text: "User prefix.", cache }]), + Message.assistant([{ type: "text", text: "Assistant prefix.", cache }]), ], generation: { maxTokens: 16, temperature: 0 }, }), @@ -331,7 +335,7 @@ describe("Bedrock Converse route", () => { id: "req_image", model, messages: [ - LLM.user([ + Message.user([ { type: "text", text: "What is in this image?" }, { type: "media", mediaType: "image/png", data: "AAAA" }, { type: "media", mediaType: "image/jpeg", data: "BBBB" }, @@ -339,6 +343,7 @@ describe("Bedrock Converse route", () => { { type: "media", mediaType: "image/webp", data: "DDDD" }, ]), ], + cache: "none", }), ) @@ -366,7 +371,7 @@ describe("Bedrock Converse route", () => { LLM.request({ id: "req_image_bytes", model, - messages: [LLM.user([{ type: "media", mediaType: "image/png", data: new Uint8Array([1, 2, 3, 4, 5]) }])], + messages: [Message.user([{ type: "media", mediaType: "image/png", data: new Uint8Array([1, 2, 3, 4, 5]) }])], }), ) @@ -389,7 +394,7 @@ describe("Bedrock Converse route", () => { id: "req_doc", model, messages: [ - LLM.user([ + Message.user([ { type: "media", mediaType: "application/pdf", data: "PDFDATA", filename: "report.pdf" }, { type: "media", mediaType: "text/csv", data: "CSVDATA" }, ]), @@ -419,7 +424,7 @@ describe("Bedrock Converse route", () => { LLM.request({ id: "req_bad_image", model, - messages: [LLM.user([{ type: "media", mediaType: "image/svg+xml", data: "x" }])], + messages: [Message.user([{ type: "media", mediaType: "image/svg+xml", data: "x" }])], }), ).pipe(Effect.flip) @@ -433,7 +438,7 @@ describe("Bedrock Converse route", () => { LLM.request({ id: "req_bad_doc", model, - messages: [LLM.user([{ type: "media", mediaType: "application/x-tar", data: "x", filename: "a.tar" }])], + messages: [Message.user([{ type: "media", mediaType: "application/x-tar", data: "x", filename: "a.tar" }])], }), ).pipe(Effect.flip) @@ -466,10 +471,11 @@ describe("Bedrock Converse route", () => { model, tools: [{ name: "lookup", description: "lookup", inputSchema: { type: "object", properties: {} }, cache }], messages: [ - LLM.user("What's the weather?"), - LLM.assistant([LLM.toolCall({ id: "call_1", name: "lookup", input: {} })]), - LLM.toolMessage({ id: "call_1", name: "lookup", result: { temp: 72 }, cache }), + Message.user("What's the weather?"), + Message.assistant([ToolCallPart.make({ id: "call_1", name: "lookup", input: {} })]), + Message.tool({ id: "call_1", name: "lookup", result: { temp: 72 }, cache }), ], + cache: "none", }), ) @@ -555,6 +561,7 @@ describe("Bedrock Converse recorded", () => { model: recordedModel(), system: "Reply with the single word 'Hello'.", prompt: "Say hello.", + cache: "none", generation: { maxTokens: 16, temperature: 0 }, }), ) @@ -576,7 +583,8 @@ describe("Bedrock Converse recorded", () => { system: "Call tools exactly as requested.", prompt: "Call get_weather with city exactly Paris.", tools: [weatherTool], - toolChoice: LLM.toolChoice(weatherTool), + toolChoice: ToolChoice.make(weatherTool), + cache: "none", generation: { maxTokens: 80, temperature: 0 }, }), ) diff --git a/packages/llm/test/provider/gemini-cache.recorded.test.ts b/packages/llm/test/provider/gemini-cache.recorded.test.ts index 145728fdc69..b86980c43da 100644 --- a/packages/llm/test/provider/gemini-cache.recorded.test.ts +++ b/packages/llm/test/provider/gemini-cache.recorded.test.ts @@ -8,7 +8,7 @@ import { recordedTests } from "../recorded-test" const model = Gemini.model({ id: "gemini-2.5-flash", - apiKey: process.env.GEMINI_API_KEY ?? "fixture", + apiKey: process.env.GOOGLE_GENERATIVE_AI_API_KEY ?? process.env.GEMINI_API_KEY ?? "fixture", }) // Gemini does implicit prefix caching on 2.5+ models above ~1024 tokens. The @@ -28,7 +28,9 @@ const recorded = recordedTests({ prefix: "gemini-cache", provider: "google", protocol: "gemini", - requires: ["GEMINI_API_KEY"], + requires: ["GOOGLE_GENERATIVE_AI_API_KEY"], + // Two identical requests in one cassette — replay walks the cassette in + // recording order so the second call replays the cached-hit interaction. }) describe("Gemini cache recorded", () => { diff --git a/packages/llm/test/provider/gemini.test.ts b/packages/llm/test/provider/gemini.test.ts index 9de4e0dc257..7e6bbc8466d 100644 --- a/packages/llm/test/provider/gemini.test.ts +++ b/packages/llm/test/provider/gemini.test.ts @@ -1,6 +1,6 @@ import { describe, expect } from "bun:test" import { Effect } from "effect" -import { LLM, LLMError } from "../../src" +import { LLM, LLMError, Message, ToolCallPart, Usage } from "../../src" import { LLMClient } from "../../src/route" import * as Gemini from "../../src/protocols/gemini" import { it } from "../lib/effect" @@ -49,12 +49,12 @@ describe("Gemini route", () => { ], toolChoice: { type: "tool", name: "lookup" }, messages: [ - LLM.user([ + Message.user([ { type: "text", text: "What is in this image?" }, { type: "media", mediaType: "image/png", data: "AAECAw==" }, ]), - LLM.assistant([LLM.toolCall({ id: "call_1", name: "lookup", input: { query: "weather" } })]), - LLM.toolMessage({ id: "call_1", name: "lookup", result: { forecast: "sunny" } }), + Message.assistant([ToolCallPart.make({ id: "call_1", name: "lookup", input: { query: "weather" } })]), + Message.tool({ id: "call_1", name: "lookup", result: { forecast: "sunny" } }), ], }), ) @@ -198,32 +198,43 @@ describe("Gemini route", () => { expect(response.reasoning).toBe("thinking") expect(response.usage).toMatchObject({ inputTokens: 5, - outputTokens: 2, + outputTokens: 3, + nonCachedInputTokens: 4, + cacheReadInputTokens: 1, reasoningTokens: 1, + totalTokens: 7, + }) + const usage = new Usage({ + inputTokens: 5, + outputTokens: 3, + nonCachedInputTokens: 4, cacheReadInputTokens: 1, + reasoningTokens: 1, totalTokens: 7, + providerMetadata: { + google: { + promptTokenCount: 5, + candidatesTokenCount: 2, + totalTokenCount: 7, + thoughtsTokenCount: 1, + cachedContentTokenCount: 1, + }, + }, }) expect(response.events).toEqual([ + { type: "step-start", index: 0 }, + { type: "reasoning-start", id: "reasoning-0" }, { type: "reasoning-delta", id: "reasoning-0", text: "thinking" }, + { type: "text-start", id: "text-0" }, { type: "text-delta", id: "text-0", text: "Hello" }, { type: "text-delta", id: "text-0", text: "!" }, + { type: "reasoning-end", id: "reasoning-0" }, + { type: "text-end", id: "text-0" }, + { type: "step-finish", index: 0, reason: "stop", usage, providerMetadata: undefined }, { - type: "request-finish", + type: "finish", reason: "stop", - usage: { - inputTokens: 5, - outputTokens: 2, - reasoningTokens: 1, - cacheReadInputTokens: 1, - totalTokens: 7, - native: { - promptTokenCount: 5, - candidatesTokenCount: 2, - totalTokenCount: 7, - thoughtsTokenCount: 1, - cachedContentTokenCount: 1, - }, - }, + usage, }, ]) }), @@ -248,21 +259,41 @@ describe("Gemini route", () => { tools: [{ name: "lookup", description: "Lookup data", inputSchema: { type: "object" } }], }), ).pipe(Effect.provide(fixedResponse(body))) + const usage = new Usage({ + inputTokens: 5, + outputTokens: 1, + nonCachedInputTokens: 5, + cacheReadInputTokens: undefined, + reasoningTokens: undefined, + totalTokens: 6, + providerMetadata: { google: { promptTokenCount: 5, candidatesTokenCount: 1 } }, + }) expect(response.toolCalls).toEqual([ - { type: "tool-call", id: "tool_0", name: "lookup", input: { query: "weather" } }, + { + type: "tool-call", + id: "tool_0", + name: "lookup", + input: { query: "weather" }, + providerExecuted: undefined, + providerMetadata: undefined, + }, ]) expect(response.events).toEqual([ - { type: "tool-call", id: "tool_0", name: "lookup", input: { query: "weather" } }, + { type: "step-start", index: 0 }, { - type: "request-finish", + type: "tool-call", + id: "tool_0", + name: "lookup", + input: { query: "weather" }, + providerExecuted: undefined, + providerMetadata: undefined, + }, + { type: "step-finish", index: 0, reason: "tool-calls", usage, providerMetadata: undefined }, + { + type: "finish", reason: "tool-calls", - usage: { - inputTokens: 5, - outputTokens: 1, - totalTokens: 6, - native: { promptTokenCount: 5, candidatesTokenCount: 1 }, - }, + usage, }, ]) }), @@ -294,7 +325,7 @@ describe("Gemini route", () => { { type: "tool-call", id: "tool_0", name: "lookup", input: { query: "weather" } }, { type: "tool-call", id: "tool_1", name: "lookup", input: { query: "news" } }, ]) - expect(response.events.at(-1)).toMatchObject({ type: "request-finish", reason: "tool-calls" }) + expect(response.events.at(-1)).toMatchObject({ type: "finish", reason: "tool-calls" }) }), ) @@ -313,8 +344,10 @@ describe("Gemini route", () => { ), ) - expect(length.events).toEqual([{ type: "request-finish", reason: "length" }]) - expect(filtered.events).toEqual([{ type: "request-finish", reason: "content-filter" }]) + expect(length.events.map((event) => event.type)).toEqual(["step-start", "step-finish", "finish"]) + expect(length.events.at(-1)).toMatchObject({ type: "finish", reason: "length" }) + expect(filtered.events.map((event) => event.type)).toEqual(["step-start", "step-finish", "finish"]) + expect(filtered.events.at(-1)).toMatchObject({ type: "finish", reason: "content-filter" }) }), ) @@ -348,7 +381,7 @@ describe("Gemini route", () => { LLM.request({ id: "req_media", model, - messages: [LLM.assistant({ type: "media", mediaType: "image/png", data: "AAECAw==" })], + messages: [Message.assistant({ type: "media", mediaType: "image/png", data: "AAECAw==" })], }), ).pipe(Effect.flip) diff --git a/packages/llm/test/provider/openai-chat.test.ts b/packages/llm/test/provider/openai-chat.test.ts index f72b3c6f43a..649503943d2 100644 --- a/packages/llm/test/provider/openai-chat.test.ts +++ b/packages/llm/test/provider/openai-chat.test.ts @@ -1,7 +1,7 @@ import { describe, expect } from "bun:test" import { Effect, Schema, Stream } from "effect" import { HttpClientRequest } from "effect/unstable/http" -import { LLM, LLMError } from "../../src" +import { LLM, LLMError, Message, ToolCallPart, Usage } from "../../src" import * as Azure from "../../src/providers/azure" import * as OpenAI from "../../src/providers/openai" import * as OpenAIChat from "../../src/protocols/openai-chat" @@ -152,9 +152,9 @@ describe("OpenAI Chat route", () => { id: "req_tool_result", model, messages: [ - LLM.user("What is the weather?"), - LLM.assistant([LLM.toolCall({ id: "call_1", name: "lookup", input: { query: "weather" } })]), - LLM.toolMessage({ id: "call_1", name: "lookup", result: { forecast: "sunny" } }), + Message.user("What is the weather?"), + Message.assistant([ToolCallPart.make({ id: "call_1", name: "lookup", input: { query: "weather" } })]), + Message.tool({ id: "call_1", name: "lookup", result: { forecast: "sunny" } }), ], }), ) @@ -188,7 +188,7 @@ describe("OpenAI Chat route", () => { LLM.request({ id: "req_media", model, - messages: [LLM.user({ type: "media", mediaType: "image/png", data: "AAECAw==" })], + messages: [Message.user({ type: "media", mediaType: "image/png", data: "AAECAw==" })], }), ).pipe(Effect.flip) @@ -202,7 +202,7 @@ describe("OpenAI Chat route", () => { LLM.request({ id: "req_reasoning", model, - messages: [LLM.assistant({ type: "reasoning", text: "hidden" })], + messages: [Message.assistant({ type: "reasoning", text: "hidden" })], }), ).pipe(Effect.flip) @@ -225,28 +225,36 @@ describe("OpenAI Chat route", () => { }), ) const response = yield* LLMClient.generate(request).pipe(Effect.provide(fixedResponse(body))) + const usage = new Usage({ + inputTokens: 5, + outputTokens: 2, + nonCachedInputTokens: 4, + cacheReadInputTokens: 1, + reasoningTokens: 0, + totalTokens: 7, + providerMetadata: { + openai: { + prompt_tokens: 5, + completion_tokens: 2, + total_tokens: 7, + prompt_tokens_details: { cached_tokens: 1 }, + completion_tokens_details: { reasoning_tokens: 0 }, + }, + }, + }) expect(response.text).toBe("Hello!") expect(response.events).toEqual([ + { type: "step-start", index: 0 }, + { type: "text-start", id: "text-0" }, { type: "text-delta", id: "text-0", text: "Hello" }, { type: "text-delta", id: "text-0", text: "!" }, + { type: "text-end", id: "text-0" }, + { type: "step-finish", index: 0, reason: "stop", usage, providerMetadata: undefined }, { - type: "request-finish", + type: "finish", reason: "stop", - usage: { - inputTokens: 5, - outputTokens: 2, - reasoningTokens: 0, - cacheReadInputTokens: 1, - totalTokens: 7, - native: { - prompt_tokens: 5, - completion_tokens: 2, - total_tokens: 7, - prompt_tokens_details: { cached_tokens: 1 }, - completion_tokens_details: { reasoning_tokens: 0 }, - }, - }, + usage, }, ]) }), @@ -269,10 +277,21 @@ describe("OpenAI Chat route", () => { ).pipe(Effect.provide(fixedResponse(body))) expect(response.events).toEqual([ + { type: "step-start", index: 0 }, + { type: "tool-input-start", id: "call_1", name: "lookup", providerMetadata: undefined }, { type: "tool-input-delta", id: "call_1", name: "lookup", text: '{"query"' }, { type: "tool-input-delta", id: "call_1", name: "lookup", text: ':"weather"}' }, - { type: "tool-call", id: "call_1", name: "lookup", input: { query: "weather" } }, - { type: "request-finish", reason: "tool-calls", usage: undefined }, + { type: "tool-input-end", id: "call_1", name: "lookup", providerMetadata: undefined }, + { + type: "tool-call", + id: "call_1", + name: "lookup", + input: { query: "weather" }, + providerExecuted: undefined, + providerMetadata: undefined, + }, + { type: "step-finish", index: 0, reason: "tool-calls", usage: undefined, providerMetadata: undefined }, + { type: "finish", reason: "tool-calls", usage: undefined }, ]) }), ) @@ -293,6 +312,8 @@ describe("OpenAI Chat route", () => { ).pipe(Effect.provide(fixedResponse(body))) expect(response.events).toEqual([ + { type: "step-start", index: 0 }, + { type: "tool-input-start", id: "call_1", name: "lookup", providerMetadata: undefined }, { type: "tool-input-delta", id: "call_1", name: "lookup", text: '{"query"' }, { type: "tool-input-delta", id: "call_1", name: "lookup", text: ':"weather"}' }, ]) @@ -352,7 +373,7 @@ describe("OpenAI Chat route", () => { const events = Array.from( yield* LLMClient.stream(request).pipe(Stream.take(1), Stream.runCollect, Effect.provide(fixedResponse(body))), ) - expect(events.map((event) => event.type)).toEqual(["text-delta"]) + expect(events.map((event) => event.type)).toEqual(["step-start"]) }), ) }) diff --git a/packages/llm/test/provider/openai-compatible-chat.test.ts b/packages/llm/test/provider/openai-compatible-chat.test.ts index 627e6ef4a0f..50aac410914 100644 --- a/packages/llm/test/provider/openai-compatible-chat.test.ts +++ b/packages/llm/test/provider/openai-compatible-chat.test.ts @@ -1,7 +1,7 @@ import { describe, expect } from "bun:test" import { Effect, Schema } from "effect" import { HttpClientRequest } from "effect/unstable/http" -import { LLM } from "../../src" +import { LLM, Message, ToolCallPart } from "../../src" import { LLMClient } from "../../src/route" import * as OpenAICompatible from "../../src/providers/openai-compatible" import * as OpenAICompatibleChat from "../../src/protocols/openai-compatible-chat" @@ -157,9 +157,9 @@ describe("OpenAI-compatible Chat route", () => { ], toolChoice: "lookup", messages: [ - LLM.user("What is the weather?"), - LLM.assistant([LLM.toolCall({ id: "call_1", name: "lookup", input: { query: "weather" } })]), - LLM.toolMessage({ id: "call_1", name: "lookup", result: { forecast: "sunny" } }), + Message.user("What is the weather?"), + Message.assistant([ToolCallPart.make({ id: "call_1", name: "lookup", input: { query: "weather" } })]), + Message.tool({ id: "call_1", name: "lookup", result: { forecast: "sunny" } }), ], }), ) @@ -231,7 +231,7 @@ describe("OpenAI-compatible Chat route", () => { expect(response.text).toBe("Hello!") expect(response.usage).toMatchObject({ inputTokens: 5, outputTokens: 2, totalTokens: 7 }) - expect(response.events.at(-1)).toMatchObject({ type: "request-finish", reason: "stop" }) + expect(response.events.at(-1)).toMatchObject({ type: "finish", reason: "stop" }) }), ) }) diff --git a/packages/llm/test/provider/openai-responses-cache.recorded.test.ts b/packages/llm/test/provider/openai-responses-cache.recorded.test.ts index 0ac3dfe2b97..2b67a0a4f2d 100644 --- a/packages/llm/test/provider/openai-responses-cache.recorded.test.ts +++ b/packages/llm/test/provider/openai-responses-cache.recorded.test.ts @@ -29,6 +29,9 @@ const recorded = recordedTests({ provider: "openai", protocol: "openai-responses", requires: ["OPENAI_API_KEY"], + // Two identical requests in one cassette — replay walks the cassette in + // recording order so the second call replays the cached-hit interaction, + // not the cold-miss one. }) describe("OpenAI Responses cache recorded", () => { diff --git a/packages/llm/test/provider/openai-responses.test.ts b/packages/llm/test/provider/openai-responses.test.ts index 0df7ec0cee4..8430689531b 100644 --- a/packages/llm/test/provider/openai-responses.test.ts +++ b/packages/llm/test/provider/openai-responses.test.ts @@ -1,7 +1,7 @@ import { describe, expect } from "bun:test" import { ConfigProvider, Effect, Layer, Stream } from "effect" import { Headers, HttpClientRequest } from "effect/unstable/http" -import { LLM, LLMError } from "../../src" +import { LLM, LLMError, Message, ToolCallPart, Usage } from "../../src" import { Auth, LLMClient, RequestExecutor, WebSocketExecutor } from "../../src/route" import * as Azure from "../../src/providers/azure" import * as OpenAI from "../../src/providers/openai" @@ -251,9 +251,9 @@ describe("OpenAI Responses route", () => { id: "req_tool_result", model, messages: [ - LLM.user("What is the weather?"), - LLM.assistant([LLM.toolCall({ id: "call_1", name: "lookup", input: { query: "weather" } })]), - LLM.toolMessage({ id: "call_1", name: "lookup", result: { forecast: "sunny" } }), + Message.user("What is the weather?"), + Message.assistant([ToolCallPart.make({ id: "call_1", name: "lookup", input: { query: "weather" } })]), + Message.tool({ id: "call_1", name: "lookup", result: { forecast: "sunny" } }), ], }), ) @@ -337,29 +337,43 @@ describe("OpenAI Responses route", () => { }, ) const response = yield* LLMClient.generate(request).pipe(Effect.provide(fixedResponse(body))) + const usage = new Usage({ + inputTokens: 5, + outputTokens: 2, + nonCachedInputTokens: 4, + cacheReadInputTokens: 1, + reasoningTokens: 0, + totalTokens: 7, + providerMetadata: { + openai: { + input_tokens: 5, + output_tokens: 2, + total_tokens: 7, + input_tokens_details: { cached_tokens: 1 }, + output_tokens_details: { reasoning_tokens: 0 }, + }, + }, + }) expect(response.text).toBe("Hello!") expect(response.events).toEqual([ + { type: "step-start", index: 0 }, + { type: "text-start", id: "msg_1" }, { type: "text-delta", id: "msg_1", text: "Hello" }, { type: "text-delta", id: "msg_1", text: "!" }, + { type: "text-end", id: "msg_1" }, { - type: "request-finish", + type: "step-finish", + index: 0, reason: "stop", providerMetadata: { openai: { responseId: "resp_1", serviceTier: "default" } }, - usage: { - inputTokens: 5, - outputTokens: 2, - reasoningTokens: 0, - cacheReadInputTokens: 1, - totalTokens: 7, - native: { - input_tokens: 5, - output_tokens: 2, - total_tokens: 7, - input_tokens_details: { cached_tokens: 1 }, - output_tokens_details: { reasoning_tokens: 0 }, - }, - }, + usage, + }, + { + type: "finish", + reason: "stop", + providerMetadata: { openai: { responseId: "resp_1", serviceTier: "default" } }, + usage, }, ]) }), @@ -391,8 +405,24 @@ describe("OpenAI Responses route", () => { tools: [{ name: "lookup", description: "Lookup data", inputSchema: { type: "object" } }], }), ).pipe(Effect.provide(fixedResponse(body))) + const usage = new Usage({ + inputTokens: 5, + outputTokens: 1, + nonCachedInputTokens: 5, + cacheReadInputTokens: undefined, + reasoningTokens: undefined, + totalTokens: 6, + providerMetadata: { openai: { input_tokens: 5, output_tokens: 1 } }, + }) expect(response.events).toEqual([ + { type: "step-start", index: 0 }, + { + type: "tool-input-start", + id: "call_1", + name: "lookup", + providerMetadata: { openai: { itemId: "item_1" } }, + }, { type: "tool-input-delta", id: "call_1", @@ -405,17 +435,26 @@ describe("OpenAI Responses route", () => { name: "lookup", text: ':"weather"}', }, + { + type: "tool-input-end", + id: "call_1", + name: "lookup", + providerMetadata: { openai: { itemId: "item_1" } }, + }, { type: "tool-call", id: "call_1", name: "lookup", input: { query: "weather" }, + providerExecuted: undefined, providerMetadata: { openai: { itemId: "item_1" } }, }, + { type: "step-finish", index: 0, reason: "tool-calls", usage, providerMetadata: undefined }, { - type: "request-finish", + type: "finish", reason: "tool-calls", - usage: { inputTokens: 5, outputTokens: 1, totalTokens: 6, native: { input_tokens: 5, output_tokens: 1 } }, + providerMetadata: undefined, + usage, }, ]) }), @@ -503,7 +542,7 @@ describe("OpenAI Responses route", () => { LLM.request({ id: "req_media", model, - messages: [LLM.user({ type: "media", mediaType: "image/png", data: "AAECAw==" })], + messages: [Message.user({ type: "media", mediaType: "image/png", data: "AAECAw==" })], }), ).pipe(Effect.flip) diff --git a/packages/llm/test/recorded-scenarios.ts b/packages/llm/test/recorded-scenarios.ts index 8a02bc3a0a1..3af7a776088 100644 --- a/packages/llm/test/recorded-scenarios.ts +++ b/packages/llm/test/recorded-scenarios.ts @@ -1,6 +1,6 @@ import { expect } from "bun:test" import { Effect, Schema, Stream } from "effect" -import { LLM, LLMEvent, LLMResponse, type LLMRequest, type ModelRef } from "../src" +import { LLM, LLMEvent, LLMResponse, ToolChoice, ToolDefinition, type LLMRequest, type ModelRef } from "../src" import { LLMClient } from "../src/route" import { tool } from "../src/tool" @@ -18,7 +18,7 @@ export const LARGE_CACHEABLE_SYSTEM = (() => { return sentence.repeat(250) })() -export const weatherTool = LLM.toolDefinition({ +export const weatherTool = ToolDefinition.make({ name: weatherToolName, description: "Get current weather for a city.", inputSchema: { @@ -51,6 +51,7 @@ export const textRequest = (input: { model: input.model, system: "You are concise.", prompt: input.prompt ?? "Reply with exactly: Hello!", + cache: "none", generation: input.temperature === false ? { maxTokens: input.maxTokens ?? 20 } @@ -69,7 +70,8 @@ export const weatherToolRequest = (input: { system: "Call tools exactly as requested.", prompt: "Call get_weather with city exactly Paris.", tools: [weatherTool], - toolChoice: LLM.toolChoice(weatherTool), + toolChoice: ToolChoice.make(weatherTool), + cache: "none", generation: input.temperature === false ? { maxTokens: input.maxTokens ?? 80 } @@ -88,6 +90,7 @@ export const weatherToolLoopRequest = (input: { model: input.model, system: input.system ?? "Use the get_weather tool, then answer in one short sentence.", prompt: "What is the weather in Paris?", + cache: "none", generation: input.temperature === false ? { maxTokens: input.maxTokens ?? 80 } @@ -117,8 +120,8 @@ export const runWeatherToolLoop = (request: LLMRequest) => export const expectFinish = ( events: ReadonlyArray, - reason: Extract["reason"], -) => expect(events.at(-1)).toMatchObject({ type: "request-finish", reason }) + reason: Extract["reason"], +) => expect(events.at(-1)).toMatchObject({ type: "finish", reason }) export const expectWeatherToolCall = (response: LLMResponse) => expect(response.toolCalls).toMatchObject([ @@ -126,10 +129,12 @@ export const expectWeatherToolCall = (response: LLMResponse) => ]) export const expectWeatherToolLoop = (events: ReadonlyArray) => { - const finishes = events.filter(LLMEvent.is.requestFinish) - expect(finishes).toHaveLength(2) - expect(finishes[0]?.reason).toBe("tool-calls") - expect(finishes.at(-1)?.reason).toBe("stop") + const finishes = events.filter(LLMEvent.is.finish) + expect(finishes).toHaveLength(1) + expect(finishes[0]?.reason).toBe("stop") + + const stepFinishes = events.filter(LLMEvent.is.stepFinish) + expect(stepFinishes.map((event) => event.reason)).toEqual(["tool-calls", "stop"]) const toolCalls = events.filter(LLMEvent.is.toolCall) expect(toolCalls).toHaveLength(1) @@ -269,7 +274,7 @@ export const eventSummary = (events: ReadonlyArray) => { summary.push({ type: "tool-error", name: event.name, message: event.message }) continue } - if (event.type === "request-finish") { + if (event.type === "finish") { summary.push({ type: "finish", reason: event.reason, usage: usageSummary(event.usage) }) } } diff --git a/packages/llm/test/schema.test.ts b/packages/llm/test/schema.test.ts index 46eb85b0753..01d6fadd9f5 100644 --- a/packages/llm/test/schema.test.ts +++ b/packages/llm/test/schema.test.ts @@ -1,6 +1,7 @@ import { describe, expect, test } from "bun:test" import { Schema } from "effect" -import { ContentPart, LLMEvent, LLMRequest, ModelID, ModelLimits, ModelRef, ProviderID } from "../src/schema" +import { ContentPart, LLMEvent, LLMRequest, ModelID, ModelLimits, ModelRef, ProviderID, Usage } from "../src/schema" +import { ProviderShared } from "../src/protocols/shared" const model = new ModelRef({ id: ModelID.make("fake-model"), @@ -43,8 +44,40 @@ describe("llm schema", () => { expect(() => Schema.decodeUnknownSync(LLMEvent)({ type: "bogus" })).toThrow() }) + test("finish constructors accept usage input", () => { + expect(LLMEvent.stepFinish({ index: 0, reason: "stop", usage: { inputTokens: 1 } }).usage).toBeInstanceOf(Usage) + expect(LLMEvent.finish({ reason: "stop", usage: { outputTokens: 2 } }).usage).toBeInstanceOf(Usage) + }) + test("content part tagged union exposes guards", () => { expect(ContentPart.guards.text({ type: "text", text: "hi" })).toBe(true) expect(ContentPart.guards.media({ type: "text", text: "hi" })).toBe(false) }) }) + +describe("LLM.Usage", () => { + test("subtractTokens clamps non-sensical breakdowns to zero", () => { + // Defense against a provider reporting cached_tokens > prompt_tokens or + // reasoning_tokens > completion_tokens — the negative would otherwise + // round-trip through the pipeline and crash strict downstream schemas. + expect(ProviderShared.subtractTokens(5, 3)).toBe(2) + expect(ProviderShared.subtractTokens(5, 10)).toBe(0) + expect(ProviderShared.subtractTokens(5, undefined)).toBe(5) + expect(ProviderShared.subtractTokens(undefined, 3)).toBeUndefined() + expect(ProviderShared.subtractTokens(undefined, undefined)).toBeUndefined() + }) + + test("sumTokens returns undefined only when every input is undefined", () => { + expect(ProviderShared.sumTokens(1, 2, 3)).toBe(6) + expect(ProviderShared.sumTokens(1, undefined, 3)).toBe(4) + expect(ProviderShared.sumTokens(undefined, undefined, undefined)).toBeUndefined() + expect(ProviderShared.sumTokens()).toBeUndefined() + }) + + test("visibleOutputTokens clamps reasoning > output to zero", () => { + expect(new Usage({ outputTokens: 10, reasoningTokens: 4 }).visibleOutputTokens).toBe(6) + expect(new Usage({ outputTokens: 10 }).visibleOutputTokens).toBe(10) + expect(new Usage({ outputTokens: 4, reasoningTokens: 10 }).visibleOutputTokens).toBe(0) + expect(new Usage({}).visibleOutputTokens).toBe(0) + }) +}) diff --git a/packages/llm/test/tool-runtime.test.ts b/packages/llm/test/tool-runtime.test.ts index 7251dee8af8..81389a466b2 100644 --- a/packages/llm/test/tool-runtime.test.ts +++ b/packages/llm/test/tool-runtime.test.ts @@ -1,10 +1,11 @@ import { describe, expect } from "bun:test" import { Effect, Schema, Stream } from "effect" -import { LLM, LLMEvent, LLMRequest, LLMResponse } from "../src" +import { GenerationOptions, LLM, LLMEvent, LLMRequest, LLMResponse, ToolChoice } from "../src" import { LLMClient } from "../src/route" import * as AnthropicMessages from "../src/protocols/anthropic-messages" import * as OpenAIChat from "../src/protocols/openai-chat" -import { tool, ToolFailure } from "../src/tool" +import { tool, ToolFailure, type ToolExecuteContext } from "../src/tool" +import { ToolRuntime } from "../src/tool-runtime" import { it } from "./lib/effect" import * as TestToolRuntime from "./lib/tool-runtime" import { dynamicResponse, scriptedResponses } from "./lib/http" @@ -24,6 +25,7 @@ const baseRequest = LLM.request({ model, prompt: "Use the tool.", }) +const weatherFailureCause = new Error("weather lookup denied") const get_weather = tool({ description: "Get current weather for a city.", @@ -31,7 +33,8 @@ const get_weather = tool({ success: Schema.Struct({ temperature: Schema.Number, condition: Schema.String }), execute: ({ city }) => Effect.gen(function* () { - if (city === "FAIL") return yield* new ToolFailure({ message: `Weather lookup failed for ${city}` }) + if (city === "FAIL") + return yield* new ToolFailure({ message: `Weather lookup failed for ${city}`, error: weatherFailureCause }) return { temperature: 22, condition: "sunny" } }), }) @@ -78,29 +81,33 @@ describe("LLMClient tools", () => { yield* TestToolRuntime.runTools({ request: LLMRequest.update(baseRequest, { - generation: LLM.generation({ maxTokens: 50 }), - toolChoice: LLM.toolChoice("auto"), + generation: GenerationOptions.make({ maxTokens: 50 }), + toolChoice: ToolChoice.make("auto"), }), tools: { get_weather }, }).pipe(Stream.runCollect, Effect.provide(layer)) - const second = bodies[1] as { - readonly messages?: ReadonlyArray> - readonly tools?: ReadonlyArray - readonly tool_choice?: unknown - readonly max_tokens?: unknown - } - - expect(second.max_tokens).toBe(50) - expect(second.tool_choice).toBe("auto") - expect(second.tools).toHaveLength(1) - expect(second.messages?.map((message) => message.role)).toEqual(["user", "assistant", "tool"]) - expect(second.messages?.[1]).toMatchObject({ + const second = bodies[1] + if (!second || typeof second !== "object") throw new Error("Expected second request body") + const messages = Reflect.get(second, "messages") + const tools = Reflect.get(second, "tools") + + expect(Reflect.get(second, "max_tokens")).toBe(50) + expect(Reflect.get(second, "tool_choice")).toBe("auto") + expect(tools).toHaveLength(1) + expect( + Array.isArray(messages) + ? messages.map((message) => + message && typeof message === "object" ? Reflect.get(message, "role") : undefined, + ) + : undefined, + ).toEqual(["user", "assistant", "tool"]) + expect(Array.isArray(messages) ? messages[1] : undefined).toMatchObject({ role: "assistant", content: null, tool_calls: [{ id: "call_1", type: "function", function: { name: "get_weather" } }], }) - expect(second.messages?.[2]).toMatchObject({ + expect(Array.isArray(messages) ? messages[2] : undefined).toMatchObject({ role: "tool", tool_call_id: "call_1", content: '{"temperature":22,"condition":"sunny"}', @@ -129,7 +136,7 @@ describe("LLMClient tools", () => { name: "get_weather", result: { type: "json", value: { temperature: 22, condition: "sunny" } }, }) - expect(events.at(-1)?.type).toBe("request-finish") + expect(events.at(-1)?.type).toBe("finish") expect(LLMResponse.text({ events })).toBe("It's sunny in Paris.") }), ) @@ -148,11 +155,40 @@ describe("LLMClient tools", () => { ), ) - expect(events.filter(LLMEvent.is.requestFinish)).toHaveLength(1) + expect(events.filter(LLMEvent.is.finish)).toHaveLength(1) expect(events.find(LLMEvent.is.toolResult)).toMatchObject({ type: "tool-result", id: "call_1" }) }), ) + it.effect("passes tool call context to execute", () => + Effect.gen(function* () { + let context: ToolExecuteContext | undefined + const contextual = tool({ + description: "Capture tool context.", + parameters: Schema.Struct({ value: Schema.String }), + success: Schema.Struct({ ok: Schema.Boolean }), + execute: (_params, ctx) => + Effect.sync(() => { + context = ctx + return { ok: true } + }), + }) + const events = Array.from( + yield* TestToolRuntime.runTools({ request: baseRequest, tools: { contextual } }).pipe( + Stream.runCollect, + Effect.provide( + scriptedResponses([ + sseEvents(toolCallChunk("call_ctx", "contextual", '{"value":"x"}'), finishChunk("tool_calls")), + ]), + ), + ), + ) + + expect(events.some(LLMEvent.is.toolResult)).toBe(true) + expect(context).toEqual({ id: "call_ctx", name: "contextual" }) + }), + ) + it.effect("can expose tool schemas without executing tool calls", () => Effect.gen(function* () { const layer = scriptedResponses([ @@ -297,6 +333,7 @@ describe("LLMClient tools", () => { const toolError = events.find(LLMEvent.is.toolError) expect(toolError).toMatchObject({ type: "tool-error", id: "call_1", name: "get_weather" }) expect(toolError?.message).toBe("Weather lookup failed for FAIL") + expect(toolError?.error).toBe(weatherFailureCause) }), ) @@ -313,7 +350,14 @@ describe("LLMClient tools", () => { ), ) - expect(events.map((event) => event.type)).toEqual(["text-delta", "request-finish"]) + expect(events.map((event) => event.type)).toEqual([ + "step-start", + "text-start", + "text-delta", + "text-end", + "step-finish", + "finish", + ]) expect(LLMResponse.text({ events })).toBe("Done.") }), ) @@ -336,7 +380,57 @@ describe("LLMClient tools", () => { ), ) - expect(events.filter(LLMEvent.is.requestFinish)).toHaveLength(2) + expect(events.filter(LLMEvent.is.finish)).toHaveLength(1) + expect(events.filter(LLMEvent.is.stepStart).map((event) => event.index)).toEqual([0, 1]) + expect(events.filter(LLMEvent.is.stepFinish).map((event) => event.index)).toEqual([0, 1]) + }), + ) + + it.effect("emits one final finish with aggregate usage", () => + Effect.gen(function* () { + let calls = 0 + const events = Array.from( + yield* ToolRuntime.stream({ + request: baseRequest, + tools: { get_weather }, + stopWhen: ToolRuntime.stepCountIs(2), + stream: () => + Stream.fromIterable( + calls++ === 0 + ? [ + LLMEvent.stepStart({ index: 0 }), + LLMEvent.toolCall({ id: "call_1", name: "get_weather", input: { city: "Paris" } }), + LLMEvent.stepFinish({ + index: 0, + reason: "tool-calls", + usage: { inputTokens: 1, outputTokens: 2, totalTokens: 3 }, + }), + LLMEvent.finish({ + reason: "tool-calls", + usage: { inputTokens: 1, outputTokens: 2, totalTokens: 3 }, + }), + ] + : [ + LLMEvent.stepStart({ index: 0 }), + LLMEvent.textDelta({ id: "text_1", text: "Done." }), + LLMEvent.stepFinish({ + index: 0, + reason: "stop", + usage: { inputTokens: 4, outputTokens: 5, totalTokens: 9 }, + }), + LLMEvent.finish({ reason: "stop", usage: { inputTokens: 4, outputTokens: 5, totalTokens: 9 } }), + ], + ), + }).pipe(Stream.runCollect), + ) + + expect(events.filter(LLMEvent.is.stepFinish).map((event) => event.index)).toEqual([0, 1]) + expect(events.filter(LLMEvent.is.finish)).toHaveLength(1) + expect(events.find(LLMEvent.is.finish)?.usage).toMatchObject({ + inputTokens: 5, + outputTokens: 7, + totalTokens: 12, + }) }), ) @@ -355,7 +449,7 @@ describe("LLMClient tools", () => { }).pipe(Stream.runCollect, Effect.provide(layer)), ) - expect(events.filter(LLMEvent.is.requestFinish)).toHaveLength(1) + expect(events.filter(LLMEvent.is.finish)).toHaveLength(1) expect(events.find(LLMEvent.is.toolResult)).toMatchObject({ type: "tool-result", id: "call_1" }) }), ) diff --git a/packages/llm/test/tool-stream.test.ts b/packages/llm/test/tool-stream.test.ts index 04a0035c993..b005d2666c8 100644 --- a/packages/llm/test/tool-stream.test.ts +++ b/packages/llm/test/tool-stream.test.ts @@ -21,11 +21,17 @@ describe("ToolStream", () => { if (ToolStream.isError(second)) return yield* second const finished = yield* ToolStream.finish(ADAPTER, second.tools, 0) - expect(first.event).toEqual({ type: "tool-input-delta", id: "call_1", name: "lookup", text: '{"query"' }) - expect(second.event).toEqual({ type: "tool-input-delta", id: "call_1", name: "lookup", text: ':"weather"}' }) + expect(first.events).toEqual([ + { type: "tool-input-start", id: "call_1", name: "lookup" }, + { type: "tool-input-delta", id: "call_1", name: "lookup", text: '{"query"' }, + ]) + expect(second.events).toEqual([{ type: "tool-input-delta", id: "call_1", name: "lookup", text: ':"weather"}' }]) expect(finished).toEqual({ tools: {}, - event: { type: "tool-call", id: "call_1", name: "lookup", input: { query: "weather" } }, + events: [ + { type: "tool-input-end", id: "call_1", name: "lookup" }, + { type: "tool-call", id: "call_1", name: "lookup", input: { query: "weather" } }, + ], }) }), ) @@ -50,7 +56,10 @@ describe("ToolStream", () => { expect(finished).toEqual({ tools: {}, - event: { type: "tool-call", id: "call_1", name: "lookup", input: { query: "final" } }, + events: [ + { type: "tool-input-end", id: "call_1", name: "lookup" }, + { type: "tool-call", id: "call_1", name: "lookup", input: { query: "final" } }, + ], }) }), ) @@ -73,7 +82,9 @@ describe("ToolStream", () => { expect(finished).toEqual({ tools: {}, events: [ + { type: "tool-input-end", id: "call_1", name: "lookup" }, { type: "tool-call", id: "call_1", name: "lookup", input: {} }, + { type: "tool-input-end", id: "call_2", name: "web_search" }, { type: "tool-call", id: "call_2", diff --git a/packages/opencode/migration/20260510033149_session_usage/migration.sql b/packages/opencode/migration/20260510033149_session_usage/migration.sql new file mode 100644 index 00000000000..68e12aad09a --- /dev/null +++ b/packages/opencode/migration/20260510033149_session_usage/migration.sql @@ -0,0 +1,6 @@ +ALTER TABLE `session` ADD `cost` real DEFAULT 0 NOT NULL;--> statement-breakpoint +ALTER TABLE `session` ADD `tokens_input` integer DEFAULT 0 NOT NULL;--> statement-breakpoint +ALTER TABLE `session` ADD `tokens_output` integer DEFAULT 0 NOT NULL;--> statement-breakpoint +ALTER TABLE `session` ADD `tokens_reasoning` integer DEFAULT 0 NOT NULL;--> statement-breakpoint +ALTER TABLE `session` ADD `tokens_cache_read` integer DEFAULT 0 NOT NULL;--> statement-breakpoint +ALTER TABLE `session` ADD `tokens_cache_write` integer DEFAULT 0 NOT NULL; diff --git a/packages/opencode/migration/20260510033149_session_usage/snapshot.json b/packages/opencode/migration/20260510033149_session_usage/snapshot.json new file mode 100644 index 00000000000..ce5e56f48c4 --- /dev/null +++ b/packages/opencode/migration/20260510033149_session_usage/snapshot.json @@ -0,0 +1,1519 @@ +{ + "version": "7", + "dialect": "sqlite", + "id": "be5eae31-b7f8-4292-8827-c36a524abd1b", + "prevIds": ["630a93f2-c6c6-4191-a351-868d8f3a05d4"], + "ddl": [ + { + "name": "account_state", + "entityType": "tables" + }, + { + "name": "account", + "entityType": "tables" + }, + { + "name": "control_account", + "entityType": "tables" + }, + { + "name": "workspace", + "entityType": "tables" + }, + { + "name": "project", + "entityType": "tables" + }, + { + "name": "message", + "entityType": "tables" + }, + { + "name": "part", + "entityType": "tables" + }, + { + "name": "permission", + "entityType": "tables" + }, + { + "name": "session_message", + "entityType": "tables" + }, + { + "name": "session", + "entityType": "tables" + }, + { + "name": "todo", + "entityType": "tables" + }, + { + "name": "session_share", + "entityType": "tables" + }, + { + "name": "event_sequence", + "entityType": "tables" + }, + { + "name": "event", + "entityType": "tables" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "account_state" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "active_account_id", + "entityType": "columns", + "table": "account_state" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "active_org_id", + "entityType": "columns", + "table": "account_state" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "email", + "entityType": "columns", + "table": "account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "url", + "entityType": "columns", + "table": "account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "access_token", + "entityType": "columns", + "table": "account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "refresh_token", + "entityType": "columns", + "table": "account" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "token_expiry", + "entityType": "columns", + "table": "account" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "account" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "email", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "url", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "access_token", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "refresh_token", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "token_expiry", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "active", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "type", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": "''", + "generated": null, + "name": "name", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "branch", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "directory", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "extra", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "project_id", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_used", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "worktree", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "vcs", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "name", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "icon_url", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "icon_url_override", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "icon_color", + "entityType": "columns", + "table": "project" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "project" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "project" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_initialized", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "sandboxes", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "commands", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "message" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "session_id", + "entityType": "columns", + "table": "message" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "message" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "message" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "data", + "entityType": "columns", + "table": "message" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "part" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "message_id", + "entityType": "columns", + "table": "part" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "session_id", + "entityType": "columns", + "table": "part" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "part" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "part" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "data", + "entityType": "columns", + "table": "part" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "project_id", + "entityType": "columns", + "table": "permission" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "permission" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "permission" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "data", + "entityType": "columns", + "table": "permission" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "session_message" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "session_id", + "entityType": "columns", + "table": "session_message" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "type", + "entityType": "columns", + "table": "session_message" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "session_message" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "session_message" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "data", + "entityType": "columns", + "table": "session_message" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "project_id", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "workspace_id", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "parent_id", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "slug", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "directory", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "path", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "title", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "version", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "share_url", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "summary_additions", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "summary_deletions", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "summary_files", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "summary_diffs", + "entityType": "columns", + "table": "session" + }, + { + "type": "real", + "notNull": true, + "autoincrement": false, + "default": "0", + "generated": null, + "name": "cost", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": "0", + "generated": null, + "name": "tokens_input", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": "0", + "generated": null, + "name": "tokens_output", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": "0", + "generated": null, + "name": "tokens_reasoning", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": "0", + "generated": null, + "name": "tokens_cache_read", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": "0", + "generated": null, + "name": "tokens_cache_write", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "revert", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "permission", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "agent", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "model", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_compacting", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_archived", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "session_id", + "entityType": "columns", + "table": "todo" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "content", + "entityType": "columns", + "table": "todo" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "status", + "entityType": "columns", + "table": "todo" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "priority", + "entityType": "columns", + "table": "todo" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "position", + "entityType": "columns", + "table": "todo" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "todo" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "todo" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "session_id", + "entityType": "columns", + "table": "session_share" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "session_share" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "secret", + "entityType": "columns", + "table": "session_share" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "url", + "entityType": "columns", + "table": "session_share" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "session_share" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "session_share" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "aggregate_id", + "entityType": "columns", + "table": "event_sequence" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "seq", + "entityType": "columns", + "table": "event_sequence" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "owner_id", + "entityType": "columns", + "table": "event_sequence" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "event" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "aggregate_id", + "entityType": "columns", + "table": "event" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "seq", + "entityType": "columns", + "table": "event" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "type", + "entityType": "columns", + "table": "event" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "data", + "entityType": "columns", + "table": "event" + }, + { + "columns": ["active_account_id"], + "tableTo": "account", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "nameExplicit": false, + "name": "fk_account_state_active_account_id_account_id_fk", + "entityType": "fks", + "table": "account_state" + }, + { + "columns": ["project_id"], + "tableTo": "project", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_workspace_project_id_project_id_fk", + "entityType": "fks", + "table": "workspace" + }, + { + "columns": ["session_id"], + "tableTo": "session", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_message_session_id_session_id_fk", + "entityType": "fks", + "table": "message" + }, + { + "columns": ["message_id"], + "tableTo": "message", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_part_message_id_message_id_fk", + "entityType": "fks", + "table": "part" + }, + { + "columns": ["project_id"], + "tableTo": "project", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_permission_project_id_project_id_fk", + "entityType": "fks", + "table": "permission" + }, + { + "columns": ["session_id"], + "tableTo": "session", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_session_message_session_id_session_id_fk", + "entityType": "fks", + "table": "session_message" + }, + { + "columns": ["project_id"], + "tableTo": "project", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_session_project_id_project_id_fk", + "entityType": "fks", + "table": "session" + }, + { + "columns": ["session_id"], + "tableTo": "session", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_todo_session_id_session_id_fk", + "entityType": "fks", + "table": "todo" + }, + { + "columns": ["session_id"], + "tableTo": "session", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_session_share_session_id_session_id_fk", + "entityType": "fks", + "table": "session_share" + }, + { + "columns": ["aggregate_id"], + "tableTo": "event_sequence", + "columnsTo": ["aggregate_id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_event_aggregate_id_event_sequence_aggregate_id_fk", + "entityType": "fks", + "table": "event" + }, + { + "columns": ["email", "url"], + "nameExplicit": false, + "name": "control_account_pk", + "entityType": "pks", + "table": "control_account" + }, + { + "columns": ["session_id", "position"], + "nameExplicit": false, + "name": "todo_pk", + "entityType": "pks", + "table": "todo" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "account_state_pk", + "table": "account_state", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "account_pk", + "table": "account", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "workspace_pk", + "table": "workspace", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "project_pk", + "table": "project", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "message_pk", + "table": "message", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "part_pk", + "table": "part", + "entityType": "pks" + }, + { + "columns": ["project_id"], + "nameExplicit": false, + "name": "permission_pk", + "table": "permission", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "session_message_pk", + "table": "session_message", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "session_pk", + "table": "session", + "entityType": "pks" + }, + { + "columns": ["session_id"], + "nameExplicit": false, + "name": "session_share_pk", + "table": "session_share", + "entityType": "pks" + }, + { + "columns": ["aggregate_id"], + "nameExplicit": false, + "name": "event_sequence_pk", + "table": "event_sequence", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "event_pk", + "table": "event", + "entityType": "pks" + }, + { + "columns": [ + { + "value": "session_id", + "isExpression": false + }, + { + "value": "time_created", + "isExpression": false + }, + { + "value": "id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "message_session_time_created_id_idx", + "entityType": "indexes", + "table": "message" + }, + { + "columns": [ + { + "value": "message_id", + "isExpression": false + }, + { + "value": "id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "part_message_id_id_idx", + "entityType": "indexes", + "table": "part" + }, + { + "columns": [ + { + "value": "session_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "part_session_idx", + "entityType": "indexes", + "table": "part" + }, + { + "columns": [ + { + "value": "session_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "session_message_session_idx", + "entityType": "indexes", + "table": "session_message" + }, + { + "columns": [ + { + "value": "session_id", + "isExpression": false + }, + { + "value": "type", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "session_message_session_type_idx", + "entityType": "indexes", + "table": "session_message" + }, + { + "columns": [ + { + "value": "time_created", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "session_message_time_created_idx", + "entityType": "indexes", + "table": "session_message" + }, + { + "columns": [ + { + "value": "project_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "session_project_idx", + "entityType": "indexes", + "table": "session" + }, + { + "columns": [ + { + "value": "workspace_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "session_workspace_idx", + "entityType": "indexes", + "table": "session" + }, + { + "columns": [ + { + "value": "parent_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "session_parent_idx", + "entityType": "indexes", + "table": "session" + }, + { + "columns": [ + { + "value": "session_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "todo_session_idx", + "entityType": "indexes", + "table": "todo" + } + ], + "renames": [] +} diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 5e79852fcf8..5d59bb9ca74 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -33,11 +33,6 @@ "bun": "./src/pty/pty.bun.ts", "node": "./src/pty/pty.node.ts", "default": "./src/pty/pty.bun.ts" - }, - "#httpapi-server": { - "bun": "./src/server/httpapi-server.node.ts", - "node": "./src/server/httpapi-server.node.ts", - "default": "./src/server/httpapi-server.node.ts" } }, "devDependencies": { @@ -125,6 +120,7 @@ "@octokit/rest": "catalog:", "@openauthjs/openauth": "catalog:", "@opencode-ai/script": "workspace:*", + "@opencode-ai/ui": "workspace:*", "@openrouter/ai-sdk-provider": "2.8.1", "@opentelemetry/api": "1.9.0", "@opentelemetry/context-async-hooks": "2.6.1", @@ -163,6 +159,7 @@ "gray-matter": "4.0.3", "hono": "catalog:", "hono-openapi": "catalog:", + "htmlparser2": "8.0.2", "iconv-lite": "0.7.2", "ignore": "7.0.5", "immer": "11.1.4", diff --git a/packages/opencode/parsers-config.ts b/packages/opencode/parsers-config.ts index b4951afa225..9bcb9e85c78 100644 --- a/packages/opencode/parsers-config.ts +++ b/packages/opencode/parsers-config.ts @@ -286,5 +286,15 @@ export default { ], }, }, + { + filetype: "diff", + aliases: ["udiff", "patch"], + wasm: "https://github.com/tree-sitter-grammars/tree-sitter-diff/releases/download/v0.1.0/tree-sitter-diff.wasm", + queries: { + highlights: [ + "https://raw.githubusercontent.com/tree-sitter-grammars/tree-sitter-diff/master/queries/highlights.scm", + ], + }, + }, ], } diff --git a/packages/opencode/script/build.ts b/packages/opencode/script/build.ts index 86172e640ba..43bb1a5fabc 100755 --- a/packages/opencode/script/build.ts +++ b/packages/opencode/script/build.ts @@ -250,7 +250,9 @@ const targets = singleFlag // kilocode_change start - prepare one validated models snapshot before any target compile const snapshot = await prepareModelsSnapshot() -console.log(`Prepared models snapshot from ${snapshot.source} (${snapshot.providers} providers, ${snapshot.models} models)`) +console.log( + `Prepared models snapshot from ${snapshot.source} (${snapshot.providers} providers, ${snapshot.models} models)`, +) // kilocode_change end await $`rm -rf dist` diff --git a/packages/opencode/script/kilocode/models-snapshot.ts b/packages/opencode/script/kilocode/models-snapshot.ts index 7891dbdbe99..81a81431164 100644 --- a/packages/opencode/script/kilocode/models-snapshot.ts +++ b/packages/opencode/script/kilocode/models-snapshot.ts @@ -45,7 +45,9 @@ async function replace(file: string, body: string) { await fs.writeFile(tmp, body) await fs.rename(tmp, file) } catch (err) { - await fs.rm(tmp, { force: true }).catch((cause) => console.warn(`Failed to remove temporary models snapshot ${tmp}`, cause)) + await fs + .rm(tmp, { force: true }) + .catch((cause) => console.warn(`Failed to remove temporary models snapshot ${tmp}`, cause)) throw err } } diff --git a/packages/opencode/script/schema.ts b/packages/opencode/script/schema.ts index b1a587075eb..b34eaf7f0e1 100755 --- a/packages/opencode/script/schema.ts +++ b/packages/opencode/script/schema.ts @@ -1,63 +1,76 @@ #!/usr/bin/env bun -import { z } from "zod" import { Config } from "@/config/config" -import { TuiConfig } from "../src/cli/cmd/tui/config/tui" - -function generate(schema: z.ZodType) { - const result = z.toJSONSchema(schema, { - io: "input", // Generate input shape (treats optional().default() as not required) - /** - * We'll use the `default` values of the field as the only value in `examples`. - * This will ensure no docs are needed to be read, as the configuration is - * self-documenting. - * - * See https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-00#rfc.section.9.5 - */ - override(ctx) { - const schema = ctx.jsonSchema - - // Preserve strictness: set additionalProperties: false for objects - if ( - schema && - typeof schema === "object" && - schema.type === "object" && - schema.additionalProperties === undefined - ) { - schema.additionalProperties = false - } - - // Add examples and default descriptions for string fields with defaults - if (schema && typeof schema === "object" && "type" in schema && schema.type === "string" && schema?.default) { - if (!schema.examples) { - schema.examples = [schema.default] - } - - schema.description = [schema.description || "", `default: \`${String(schema.default)}\``] - .filter(Boolean) - .join("\n\n") - .trim() - } - }, - }) as Record & { - allowComments?: boolean - allowTrailingCommas?: boolean +import { Schema } from "effect" +import { TuiInfo } from "../src/cli/cmd/tui/config/tui-schema" + +type JsonSchema = Record +const MODEL_REF = "https://models.dev/model-schema.json#/$defs/Model" + +function generateEffect(schema: Schema.Top) { + const document = Schema.toJsonSchemaDocument(schema) + const normalized = normalize({ + $schema: "https://json-schema.org/draft/2020-12/schema", + ...document.schema, + $defs: document.definitions, + }) + if (!isRecord(normalized)) throw new Error("schema generator produced a non-object schema") + const restored = restoreModelRefs(normalized) + if (!isRecord(restored)) throw new Error("schema generator produced a non-object schema") + restored.allowComments = true + restored.allowTrailingCommas = true + return restored +} + +function normalize(value: unknown): unknown { + if (Array.isArray(value)) return value.map(normalize) + if (!isRecord(value)) return value + + const schema = Object.fromEntries(Object.entries(value).map(([key, item]) => [key, normalize(item)])) + + if (Array.isArray(schema.anyOf)) { + const anyOf = schema.anyOf.filter((item) => !isRecord(item) || item.type !== "null") + if (anyOf.length !== schema.anyOf.length) { + const { anyOf: _, ...rest } = schema + if (anyOf.length === 1 && isRecord(anyOf[0])) return normalize({ ...anyOf[0], ...rest }) + return { ...rest, anyOf } + } + } + + if (Array.isArray(schema.allOf) && schema.allOf.length === 1 && isRecord(schema.allOf[0])) { + const { allOf: _, ...rest } = schema + return normalize({ ...schema.allOf[0], ...rest }) } - // used for json lsps since config supports jsonc - result.allowComments = true - result.allowTrailingCommas = true + if (schema.type === "integer" && schema.maximum === undefined) { + return { ...schema, maximum: Number.MAX_SAFE_INTEGER } + } + + return schema +} + +function restoreModelRefs(value: unknown, key?: string): unknown { + if (Array.isArray(value)) return value.map((item) => restoreModelRefs(item)) + if (!isRecord(value)) return value + + const schema = Object.fromEntries(Object.entries(value).map(([name, item]) => [name, restoreModelRefs(item, name)])) + if ((key === "model" || key === "small_model") && schema.type === "string") { + return { ...schema, $ref: MODEL_REF } + } + return schema +} - return result +function isRecord(value: unknown): value is JsonSchema { + return typeof value === "object" && value !== null && !Array.isArray(value) } const configFile = process.argv[2] const tuiFile = process.argv[3] console.log(configFile) -await Bun.write(configFile, JSON.stringify(generate(Config.Info.zod), null, 2)) +await Bun.write(configFile, JSON.stringify(generateEffect(Config.Info), null, 2)) if (tuiFile) { console.log(tuiFile) - await Bun.write(tuiFile, JSON.stringify(generate(TuiConfig.JsonSchemaInfo), null, 2)) + await Bun.write(tuiFile, JSON.stringify(generateEffect(TuiInfo), null, 2)) } diff --git a/packages/opencode/specs/effect/error-boundaries-plan.md b/packages/opencode/specs/effect/error-boundaries-plan.md new file mode 100644 index 00000000000..763bf5ea5e5 --- /dev/null +++ b/packages/opencode/specs/effect/error-boundaries-plan.md @@ -0,0 +1,235 @@ +# Error Boundaries Plan + +Plan for removing `NamedError` as connective tissue while keeping public +wire contracts stable. + +## Desired Shape + +```text +Domain/service error + Schema.TaggedErrorClass + - catchable with catchTag / catchTags + - appears in service method error type + - no HTTP status + - no toObject() + +HTTP public error + Schema.ErrorClass / TaggedErrorClass with httpApiStatus + - endpoint-declared public contract + - owns legacy { name, data } only when that is the SDK wire shape + +CLI/user rendering + FormatError and small format helpers + - converts domain errors to text + - preserves useful structured fields + +Session/model-visible error + first-class session/message error schema or helper + - owns { name, data } event/message shape + - not a service error class +``` + +The important rule: a service error should not also be the HTTP body, CLI +formatter, and session event body. Each seam adapts the error into the +shape it owns. + +## Concrete Example: Provider Model Not Found + +Before: + +```ts +export const ModelNotFoundError = NamedError.create("ProviderModelNotFoundError", { + providerID: ProviderID, + modelID: ModelID, + suggestions: Schema.optional(Schema.Array(Schema.String)), +}) +``` + +Problems: + +- Throwing it inside `Effect.fn` made it behave like a defect unless a + compatibility bridge caught it. +- HTTP middleware knew that this one domain error should be a `400`. +- Callers read `.data.*`, which couples them to the legacy `{ name, data }` + wire shape. + +After: + +```ts +export class ModelNotFoundError extends Schema.TaggedErrorClass()("ProviderModelNotFoundError", { + providerID: ProviderID, + modelID: ModelID, + suggestions: Schema.optional(Schema.Array(Schema.String)), + cause: Schema.optional(Schema.Defect), +}) {} + +export interface Interface { + readonly getModel: (providerID: ProviderID, modelID: ModelID) => Effect.Effect +} +``` + +Boundary adapters: + +```text +CLI +└─ FormatError sees _tag ProviderModelNotFoundError -> nice text + +Session prompt +└─ catch ModelNotFoundError -> publish Session.Event.Error as message/session wire shape + +HTTP route +└─ catch ModelNotFoundError -> declared BadRequest public API error when the endpoint needs it + +HTTP middleware +└─ no Provider.ModelNotFoundError knowledge +``` + +## Refining Known Promise Failures + +Use `EffectPromise.refineRejection(...)` when a Promise boundary can reject +with many unknown values, but only one or two rejection classes are expected +domain failures. Unknown rejections stay defects; the helper maps only known +rejection shapes to typed errors. + +```ts +const language = + yield * + EffectPromise.refineRejection( + async () => loadFromProvider(), + (cause) => (cause instanceof NoSuchModelError ? new ModelNotFoundError({ providerID, modelID, cause }) : undefined), + ) +``` + +Use this when the Promise can genuinely reject and most rejection values are +still defects for the current module. Use `Effect.tryPromise({ try, catch })` +when every rejection should become the same expected error type. Use +`Effect.promise(...)` only when rejection means a defect and you do not need +to refine known rejection classes. + +## Helper Modules We Probably Want + +Add helpers only when repeated call sites prove the seam is real. + +### HTTP API Errors + +Likely location: `src/server/routes/instance/httpapi/errors.ts`. + +Purpose: + +- construct public HTTP error bodies +- preserve legacy `{ name, data }` where needed +- attach `httpApiStatus` + +Good helpers: + +```ts +notFound(message) +badRequest(message) +unknown() +``` + +Avoid: + +```ts +mapAnyDomainError(error) +``` + +That recreates the giant middleware mapper problem. + +### Session / Message Error Wire Helpers + +Likely location: near `src/session/message-error.ts` or a new narrow +module such as `src/session/event-error.ts`. + +Purpose: + +- construct the `{ name, data }` shape used by `Session.Event.Error` and + assistant message errors +- replace `new NamedError.Unknown(...).toObject()` call sites +- keep model-visible error bodies separate from service/domain errors + +Good helpers: + +```ts +unknown(message) +agentNotFound(agent, available) +commandNotFound(command, available) +modelNotFound(error: Provider.ModelNotFoundError) +``` + +### CLI Formatters + +Likely location: `src/cli/error.ts` until repetition demands domain-local +format helpers. + +Purpose: + +- produce human-readable terminal messages from typed errors +- support old `{ name, data }` shapes only while compatibility is needed + +## Migration Queue + +### Remove Domain Knowledge From HTTP Middleware + +- [x] Storage not found no longer maps through defect fallback. +- [x] Worktree expected errors moved to typed errors. +- [x] Provider auth expected errors moved to typed errors. +- [x] Provider model not found no longer needs an HTTP middleware status + special case. +- [ ] Convert `Session.BusyError` and map it at route boundaries. +- [ ] Delete the broad `NamedError` middleware branch once no route relies + on defect-wrapped legacy domain errors. +- [ ] Keep one final unknown-defect fallback that logs `Cause.pretty(cause)` + and returns a safe `500` body. + +### Remaining `NamedError.create(...)` Service Errors + +These should become `Schema.TaggedErrorClass` when touched: + +- [ ] `src/provider/provider.ts` — `ProviderInitError`. +- [ ] `src/storage/db.ts` — database `NotFoundError`. +- [ ] `src/mcp/index.ts` — `MCPFailed`. +- [ ] `src/skill/index.ts` — `SkillInvalidError`, + `SkillNameMismatchError`. +- [ ] `src/lsp/client.ts` — `LSPInitializeError`. +- [ ] `src/ide/index.ts` — install errors. +- [ ] `src/config/error.ts`, `src/config/config.ts`, + `src/config/markdown.ts` — config errors. These already render well + in the CLI, so migrate carefully and preserve diagnostics. + +### Session / Message Wire Errors + +These are not ordinary service errors. They mostly build `{ name, data }` +objects for model-visible/session-visible output. + +- [ ] Add a first-class session/message error wire helper. +- [ ] Replace `new NamedError.Unknown(...).toObject()` in + `src/session/prompt.ts`. +- [ ] Replace `new NamedError.Unknown(...).toObject()` in config/skill/plugin + session event publishing. +- [ ] Move `src/session/message-error.ts` and `src/session/message-v2.ts` + away from `NamedError.create(...)` once the wire helper exists. +- [ ] Update retry/message tests to assert the wire schema/helper output, + not `NamedError` instances. + +### CLI Rendering + +- [x] Tagged config errors render with useful diagnostics. +- [x] Provider model not found renders from both old `{ name, data }` and + new `_tag` shapes. +- [ ] Add typed render cases as more `NamedError.create(...)` domains move + to `Schema.TaggedErrorClass`. +- [ ] Eventually remove old-shape compatibility branches when no callers can + produce them. + +## PR Checklist + +For each migrated error: + +- [ ] Domain error is `Schema.TaggedErrorClass`. +- [ ] Service method exposes the typed error in its error channel. +- [ ] No service error has `toObject()` just for compatibility. +- [ ] CLI, HTTP, and session/message adapters each own their output shape. +- [ ] HTTP middleware gets smaller or stays unchanged. +- [ ] Focused tests cover the domain error and any public rendering/wire + shape touched by the PR. diff --git a/packages/opencode/specs/effect/errors.md b/packages/opencode/specs/effect/errors.md index 746e658693d..69298bde5ca 100644 --- a/packages/opencode/specs/effect/errors.md +++ b/packages/opencode/specs/effect/errors.md @@ -1,40 +1,25 @@ -# Typed error migration +# Typed Error Migration -Plan for moving `packages/opencode` from temporary defect/`NamedError` -compatibility toward typed Effect service errors and explicit HTTP error -contracts. +This note expands the `ERR`, `RENDER`, and `HTTP` tracks from +[`todo.md`](./todo.md). It is the current reference for expected failures, +typed service errors, and HTTP error boundaries. + +For the migration architecture and queue, see +[`error-boundaries-plan.md`](./error-boundaries-plan.md). ## Goal - Expected service failures live on the Effect error channel. - Service interfaces expose those failures in their return types. -- Domain errors are authored with Effect Schema so they are reusable by services, - tests, HTTP routes, tools, and OpenAPI generation. -- HTTP status codes and wire compatibility are handled at the HTTP boundary, not - inside service modules. -- `Effect.die`, `throw`, `catchDefect`, and global cause inspection are reserved - for defects, compatibility bridges, or final fallback behavior. - -## Current State - -- Many migrated services use Effect internally, but expected failures are still a - mix of `NamedError.create(...)`, `namedSchemaError(...)`, `class extends Error`, - `throw`, and `Effect.die(...)`. -- Some services already use `Schema.TaggedErrorClass`, for example `Account`, - `Auth`, `Permission`, `Question`, `Installation`, and parts of - `Workspace`. -- Legacy Hono error handling recognizes `NamedError`, `Session.BusyError`, and a - few name-based cases, then emits the legacy `{ name, data }` JSON body. -- Effect `HttpApi` only knows how to encode errors that are declared on the - endpoint, group, or middleware. Undeclared expected errors become defects and - eventually fall through to generic HTTP handling. -- The temporary HttpApi error middleware catches defect-wrapped legacy errors to - preserve runtime behavior, but it is intentionally a bridge rather than the - final model. - -## End State +- Domain errors are authored with `Schema.TaggedErrorClass`. +- `Effect.die(...)` is reserved for defects: bugs, impossible states, + violated invariants, and final unknown-boundary fallbacks. +- HTTP status codes and public wire bodies are handled at HTTP route + boundaries, not inside service modules. +- User-facing boundaries render useful structured error details instead of + opaque `Error: SomeName` strings. -Service modules own domain failures. +## Service Error Shape ```ts export class SessionBusyError extends Schema.TaggedErrorClass()("SessionBusyError", { @@ -49,281 +34,90 @@ export interface Interface { } ``` -HTTP modules own transport mapping. - -```ts -const get = Effect.fn("SessionHttpApi.get")(function* (ctx: { params: { sessionID: SessionID } }) { - return yield* session - .get(ctx.params.sessionID) - .pipe( - Effect.catchTag("StorageNotFoundError", () => new SessionNotFoundHttpError({ sessionID: ctx.params.sessionID })), - ) -}) -``` - -HTTP-visible error schemas carry their own response status through Effect -HttpApi's `httpApiStatus` annotation. Prefer `HttpApiSchema.status(...)`, or the -equivalent declaration annotation, instead of maintaining a parallel status map. - -```ts -export class SessionNotFoundHttpError extends Schema.TaggedErrorClass()( - "SessionNotFoundHttpError", - { - sessionID: SessionID, - message: Schema.String, - }, - { httpApiStatus: 404 }, -) {} -``` +Rules: -Endpoint definitions still declare which HTTP-visible error schemas can be -emitted. The status annotation is only used if the error is part of the endpoint, -group, or middleware error schema and the handler fails with that error on the -typed error channel. - -```ts -HttpApiEndpoint.get("get", SessionPaths.get, { - success: Session.Info, - error: [SessionNotFoundHttpError, SessionBusyHttpError], -}) -``` - -The service error and HTTP error may be the same class when the wire shape is a -deliberate public contract. They should be different classes when the service -error contains internals, low-level causes, retry hints, or anything that should -not be exposed to API clients. - -## Rules - -- Use `Schema.TaggedErrorClass` for new expected domain errors. -- Include `cause: Schema.optional(Schema.Defect)` only when preserving an - underlying unknown failure is useful for logs or callers. -- Export a domain-level error union from each service module, for example - `export type Error = NotFoundError | BusyError | Storage.Error`. -- Put expected errors in service method signatures, for example - `Effect.Effect`. -- Use `yield* new DomainError(...)` for direct early failures inside +- Use `Schema.TaggedErrorClass` for expected domain failures. +- Export a domain-level `Error` union from each service module. +- Put expected errors in service method signatures. +- Use `yield* new DomainError(...)` for direct early failures in `Effect.gen` / `Effect.fn`. -- Use `Effect.try({ try, catch })`, `Effect.mapError`, or `Effect.catchTag` to - convert external exceptions into domain errors. -- Use `HttpApiSchema.status(...)` or `{ httpApiStatus: code }` on HTTP-visible - error schemas so Effect `HttpApiBuilder` and OpenAPI generation get the status - from the schema itself. -- Do not use `Effect.die(...)` for user, IO, validation, missing-resource, auth, - provider, worktree, or busy-state failures. -- Do not use `catchDefect` to recover expected domain errors. If recovery is - needed, the upstream effect should fail with a typed error instead. -- Do not make service modules import `HttpApiError`, `HttpServerResponse`, HTTP - status codes, or route-specific error schemas. -- Keep raw `HttpRouter` routes free to use `HttpServerRespondable` when that is - the right transport abstraction, but prefer declared `HttpApi` errors for - normal JSON API endpoints. +- Use `Schema.Defect` for unknown cause fields when preserving the cause is + useful for logs or callers. +- Use `Effect.try(...)`, `Effect.tryPromise(...)`, `Effect.mapError`, + `Effect.catchTag`, and `Effect.catchTags` to translate external + failures into domain errors. +- Do not use `throw`, `Effect.die(...)`, or `catchDefect` for expected + user, IO, validation, missing-resource, auth, provider, worktree, or + busy-state failures. ## HTTP Boundary Shape -Create an HttpApi-local error module, likely -`src/server/routes/instance/httpapi/errors.ts`. +Service modules stay transport-agnostic. They should not import HTTP +status codes, `HttpApiError`, `HttpServerResponse`, or route-specific +error schemas. -That module should provide: - -- Legacy-compatible public schemas for `{ name, data }` error bodies that must - remain SDK-compatible during the Hono migration. -- Small constructors or mapping helpers for common API errors such as not found, - bad request, conflict, and unknown internal errors. -- Route-group-specific adapters only when they encode domain-specific public - data. -- A single place to document which public error shape is legacy-compatible and - which shape is new Effect-native API surface. - -Avoid one giant `unknown -> status` mapper. Prefer small, explicit mappers close -to the handler or route group. +HTTP handlers translate service errors into public endpoint errors: ```ts -const mapSessionError = (effect: Effect.Effect) => - effect.pipe( - Effect.catchTag("StorageNotFoundError", (error) => new SessionNotFoundHttpError({ message: error.message })), - Effect.catchTag("SessionBusyError", (error) => new SessionBusyHttpError({ message: error.message })), - ) +const get = Effect.fn("SessionHttpApi.get")(function* (ctx: { params: { sessionID: SessionID } }) { + return yield* session + .get(ctx.params.sessionID) + .pipe(Effect.catchTag("StorageNotFoundError", () => notFound("Session not found"))) +}) ``` -Use built-in `HttpApiError.BadRequest`, `HttpApiError.NotFound`, and related -types only when their generated response body and SDK surface are intentionally -acceptable. Use a custom schema-backed error when clients need the legacy -`{ name, data }` body or a domain-specific error payload. - -## Migration Phases +Endpoint definitions declare which public errors can be emitted. Public +HTTP error schemas carry their response status with `httpApiStatus` or the +equivalent HttpApi schema annotation. -### 1. Stabilize The Bridge +The service error and HTTP error may be the same class only when the wire +shape is intentionally public. Use separate HTTP error schemas when the +service error contains internals, low-level causes, retry hints, or data +that should not be exposed to API clients. -Keep the temporary HttpApi error middleware only as a compatibility bridge while -typed errors are introduced. +## Mapping Guidance -- Add tests that prove the bridge catches legacy `NamedError` defects. -- Add tests that prove declared HttpApi errors still use the declared endpoint - contract. -- Stop returning stack traces in unknown HTTP `500` responses; log the full - `Cause.pretty(cause)` server-side instead. -- Add a comment or TODO that names this plan and states the bridge must shrink - as route groups migrate. +- Keep one-off translations inline in the handler. +- Extract tiny shared helpers when the same translation repeats across a + route group. +- Do not create one giant `unknown -> status` mapper. +- Do not grow generic HTTP middleware into a registry of domain errors. +- Preserve existing public `{ name, data }` bodies until a deliberate + breaking API change. +- Use built-in `HttpApiError.*` only when its generated body and SDK + surface are intentionally the public contract. -### 2. Define The Shared HTTP Error Helpers +## Middleware Guidance -Add the `httpapi/errors.ts` module before converting route groups. +HTTP middleware should be cross-cutting: auth, context, schema decode +formatting, routing, and final unknown-defect fallback. -- Define a legacy `{ name, data }` body helper for SDK-compatible errors. -- Define `UnknownError` for generic internal failures with a safe public message. -- Define `BadRequestError` and `NotFoundError` equivalents only if the actual - wire body must match the legacy Hono SDK surface. -- Put the HTTP status on the public schema with `HttpApiSchema.status(...)` or - `{ httpApiStatus: code }`; do not keep a separate name-to-status table. -- Keep conversion helpers pure and small. They should not inspect `Cause` or - accept `unknown` unless they are final fallback helpers. +The current compatibility middleware still knows about some legacy domain +errors. As route groups declare expected errors and handlers map them, that +middleware should shrink. It should not gain new name checks. -### 3. Convert One Vertical Slice +Unknown `500` responses should log full details server-side with +`Cause.pretty(cause)` and return a safe public body. -Start with session read routes because they already have local `mapNotFound` -logic and are heavily covered by existing HttpApi tests. +## Migration Order -- Convert `Session.BusyError` from a plain `Error` to a typed service error, or - add a typed wrapper while preserving the old constructor until callers are - migrated. -- Replace `catchDefect` in `httpapi/handlers/session.ts` with typed error - mapping. -- Add endpoint error schemas for the affected session endpoints. -- Prove behavior with focused tests in `test/server/httpapi-session.test.ts`. -- Remove the migrated cases from the global compatibility middleware. - -### 4. Convert Legacy NamedError Domains - -Move legacy `NamedError.create(...)` services to Effect Schema-backed errors in -small domain PRs. - -Priority order: - -1. `storage/storage.ts` and `storage/db.ts` not-found errors. -2. `worktree/index.ts` `Worktree*` errors. -3. `provider/auth.ts` validation failures and `provider/provider.ts` model-not-found errors. -4. `mcp/index.ts`, `skill/index.ts`, `lsp/client.ts`, and `ide/index.ts` service errors. -5. Config and CLI-only errors after HTTP-facing domains are stable. - -For each domain: - -- Replace `NamedError.create(...)` with `Schema.TaggedErrorClass` when the error - is primarily a service error. -- Keep or add a separate HTTP error schema when the legacy `{ name, data }` wire - shape must remain stable. -- Update service interface return types to include the new error union. -- Replace `throw new X(...)` inside `Effect.fn` with `yield* new X(...)`. -- Replace async exceptions with `Effect.try({ catch })` or explicit `mapError`. -- Add service-level tests that assert the error tag and data, not just the HTTP - status. - -### 5. Declare HttpApi Errors Group By Group - -For each HttpApi group: - -- Inventory every service call and the typed errors it can return. -- Add only the public error schemas that endpoint can actually emit. -- Map service errors to HTTP errors in the handler file. -- Keep built-in `HttpApiError` only for generic request/validation failures where - the generated contract is accepted. -- Update `httpapi/public.ts` compatibility transforms only when the generated - spec cannot represent the desired source shape directly. -- Regenerate the SDK after OpenAPI-visible changes and verify the diff is - intentional. - -Suggested route order: - -1. `session` not-found and busy-state reads. -2. `experimental` worktree mutations. -3. `provider` auth and model selection errors. -4. `mcp` OAuth and connection errors. -5. Remaining route groups as Hono deletion work progresses. - -### 6. Remove Defect Recovery - -After enough route groups declare their expected errors: - -- Delete `catchDefect` recovery for domain errors. -- Delete name-prefix checks such as `error.name.startsWith("Worktree")` from - HTTP middleware. -- Delete `NamedError` branches from the Effect HttpApi compatibility middleware - once no Effect route depends on them. -- Leave one final unknown-defect fallback that logs server-side and returns a - safe generic `500` body. - -## Inventory Checklist - -Use this checklist when touching a service or route group. - -- [ ] Does the service interface expose every expected failure in the Effect - error type? -- [ ] Are user-caused, provider-caused, IO, auth, missing-resource, and busy-state - failures modeled as typed errors instead of defects? -- [ ] Does the service avoid importing HTTP status, `HttpApiError`, or response - classes? -- [ ] Does the handler map each service error into a declared endpoint error? -- [ ] Does the endpoint `error` field include every public error the handler can - emit? -- [ ] Does OpenAPI/SDK output either stay byte-identical or have an explicitly - reviewed diff? -- [ ] Do tests cover both service-level error typing and HTTP-level status/body? -- [ ] Did the PR remove any now-unneeded case from the temporary compatibility - middleware? - -## Testing Requirements - -For service conversions: - -- Test the service method directly with `testEffect(...)`. -- Assert on `_tag` or class identity and the structured fields. -- Avoid testing by string-matching `Cause.pretty(...)`. - -For HttpApi conversions: - -- Add or update the focused `test/server/httpapi-*.test.ts` file. -- Assert status code, content type, and exact JSON body for declared public - errors. -- Add a regression test that the temporary middleware is no longer needed for the - migrated route. -- Keep bridge/parity tests aligned with legacy Hono behavior until Hono is - deleted or the SDK contract intentionally changes. - -## Verification Commands - -Run from `packages/opencode` unless noted otherwise. - -```bash -bun run prettier --write -bunx oxlint -bun typecheck -bun run test -- test/server/httpapi-session.test.ts -``` - -Run SDK generation from the repo root when schemas or OpenAPI-visible errors -change. - -```bash -./packages/sdk/js/script/build.ts -``` +Prefer small vertical slices: -## Open Questions +1. Fix rendering at one user-visible boundary. +2. Convert one service domain to `Schema.TaggedErrorClass` errors. +3. Map those errors at the affected HTTP handlers. +4. Remove the corresponding name-based middleware branch if possible. +5. Add or update focused tests for both service error tags and HTTP wire + bodies. -- Should legacy V1 routes keep `{ name, data }` forever while V2 routes expose a - more Effect-native tagged error body? -- Should storage not-found remain generic, or should callers map it to - domain-specific not-found errors before crossing service boundaries? -- Should `namedSchemaError(...)` stay as a long-term public-wire helper, or only - as a migration bridge for old `NamedError` contracts? -- Which SDK version boundary lets us stop remapping built-in Effect HttpApi error - schemas in `httpapi/public.ts`? +Good early domains are storage not-found, worktree errors, and provider +auth validation errors because they currently drive HTTP behavior. -## Success Criteria +## Checklist For A PR -- New service code no longer uses `die` for expected failures. -- A route reviewer can read an endpoint definition and see every public error it - can return. -- The temporary HttpApi error middleware shrinks over time instead of gaining new - name-based cases. -- Service tests prove domain error types without going through HTTP. -- HTTP tests prove status/body contracts without relying on defect recovery. +- [ ] Expected failures are typed errors, not defects. +- [ ] Service method signatures expose the expected error union. +- [ ] HTTP handlers translate domain errors at the boundary. +- [ ] Public HTTP error bodies preserve existing wire contracts. +- [ ] Generic middleware gets smaller or stays unchanged. +- [ ] Focused tests cover the service error and any public HTTP response. diff --git a/packages/opencode/specs/effect/guide.md b/packages/opencode/specs/effect/guide.md new file mode 100644 index 00000000000..5df02934485 --- /dev/null +++ b/packages/opencode/specs/effect/guide.md @@ -0,0 +1,251 @@ +# Effect Guide + +How we write Effect code in `packages/opencode`. The companion roadmap is +[`todo.md`](./todo.md). + +This guide describes the preferred shape for new work and migrations. If a +legacy file differs, migrate it only when it is already in scope. + +## Service Shape + +Use one module per service: flat top-level exports, traced Effect methods, +explicit layers, and a self-reexport at the bottom. + +```ts +export interface Interface { + readonly get: (id: FooID) => Effect.Effect +} + +export class Service extends Context.Service()("@opencode/Foo") {} + +export const layer = Layer.effect( + Service, + Effect.gen(function* () { + const state = yield* InstanceState.make(Effect.fn("Foo.state")(() => Effect.succeed({}))) + + const get = Effect.fn("Foo.get")(function* (id: FooID) { + const s = yield* InstanceState.get(state) + return yield* loadFoo(s, id) + }) + + return Service.of({ get }) + }), +) + +export const defaultLayer = layer.pipe(Layer.provide(FooDep.defaultLayer)) + +export * as Foo from "./foo" +``` + +Rules: + +- Do not use `export namespace Foo { ... }`. +- Use `Effect.fn("Foo.method")` for public service methods. +- Use `Effect.fnUntraced` for small internal helpers that do not need a + span. +- Keep helpers as non-exported top-level declarations in the same file. +- Self-reexport with `export * as Foo from "."` for `index.ts`, otherwise + `export * as Foo from "./foo"`. +- In `src/config`, keep the existing top-of-file self-export pattern. + +## Runtime Boundaries + +Most code should run through [`AppRuntime`](../../src/effect/app-runtime.ts). +It hosts `AppLayer`, shares the global `memoMap`, and restores the current +instance/workspace refs when crossing from non-Effect code. + +Use `AppRuntime.runPromise(effect)` at app boundaries such as CLI commands, +HTTP handlers, or plain async adapters. + +`makeRuntime(...)` still exists for a few intentional service-local +boundaries and migration leftovers. Do not add a new service-local runtime +unless the service truly cannot live in `AppLayer`. + +## Runtime Flags + +Read opencode runtime flags through +[`RuntimeFlags.Service`](../../src/effect/runtime-flags.ts), not through +mutable `Flag` or late `process.env` reads. + +Tests should vary behavior with explicit layer variants: + +```ts +const it = testEffect(MyService.defaultLayer.pipe(Layer.provide(RuntimeFlags.layer({ experimentalScout: true })))) +``` + +Do not mutate `process.env` or `Flag` after services/layers are built. + +## Per-Instance State + +Use [`InstanceState`](../../src/effect/instance-state.ts) when two open +directories should not share one copy of a service's state. It is backed by +a `ScopedCache`, keyed by directory, and disposed automatically when an +instance is unloaded. + +Put subscriptions, finalizers, and scoped background work inside the +`InstanceState.make(...)` initializer: + +```ts +const cache = + yield * + InstanceState.make( + Effect.fn("Foo.state")(function* () { + const bus = yield* Bus.Service + + yield* bus.subscribeAll().pipe( + Stream.runForEach((event) => handleEvent(event)), + Effect.forkScoped, + ) + + yield* Effect.acquireRelease(openResource, closeResource) + + return yield* loadInitialState() + }), + ) +``` + +Do not add separate `started` flags on top of `InstanceState`. Let +`ScopedCache` handle run-once and deduplication. + +To make `init()` non-blocking, fork at the caller/bootstrap boundary. Do +not fork inside `InstanceState.make(...)` just to return early with +partially initialized state. + +## Errors + +Expected domain failures belong on the Effect error channel. Defects are +for bugs, impossible states, and final unknown-boundary fallbacks. + +```ts +export class SessionBusyError extends Schema.TaggedErrorClass()("SessionBusyError", { + sessionID: SessionID, + message: Schema.String, +}) {} + +export type Error = Storage.Error | SessionBusyError + +export interface Interface { + readonly get: (id: SessionID) => Effect.Effect +} +``` + +Rules: + +- Use `Schema.TaggedErrorClass` for new expected domain errors. +- Export a domain-level `Error` union from service modules. +- In `Effect.gen` / `Effect.fn`, prefer `yield* new MyError(...)` for + direct expected failures. +- Use `Schema.Defect` for unknown cause fields. +- Use `Effect.try(...)`, `Effect.tryPromise(...)`, `Effect.mapError`, + `Effect.catchTag`, and `Effect.catchTags` to translate external + failures into domain errors. +- Do not use `Effect.die(...)` for user, IO, validation, missing-resource, + auth, provider, or busy-state failures. + +## HTTP Error Boundaries + +Service modules stay HTTP-agnostic. They should not import HTTP status +codes, `HttpApiError`, `HttpServerResponse`, or route-specific error +schemas. + +HTTP handlers translate service errors into endpoint-declared public error +schemas. Keep mappings inline when they are one-off; extract tiny shared +helpers only when the same translation repeats. + +Do not turn generic middleware into a registry of domain errors. Middleware +should handle cross-cutting concerns and the final unknown-defect fallback. + +Preserve legacy public wire shapes, such as `{ name, data }`, until a +deliberate breaking API change. + +## Schemas + +Use Effect Schema as the source of truth. + +- Use `Schema.Class` for exported data objects with a clear identity. +- Use `Schema.Struct` for local shapes and simple nested objects. +- Use `Schema.brand` for single-value IDs. +- Reuse named refinements instead of re-spelling constraints. +- Prefer narrow boundary helpers over generic Schema-to-Zod bridges. + +Intentional boundaries: + +- Public plugin tools still expose Zod through `tool.schema = z`. +- Tool parameter JSON Schema is generated through tool-specific helpers. +- Public config and TUI schemas are generated through the schema script. + +## Preferred Services + +In effectified code, yield existing services instead of dropping to ad hoc +platform APIs. + +- Use `AppFileSystem.Service` instead of raw `fs/promises` for app file IO. +- Use `AppProcess.Service` instead of direct `ChildProcessSpawner.spawn` or + legacy process helpers. +- Use `HttpClient.HttpClient` instead of raw `fetch` inside Effect code. +- Use `Path.Path`, `Config`, `Clock`, and `DateTime` when already inside + Effect. +- Use `Effect.callback` for callback-based APIs. +- Use `Effect.void` instead of `Effect.succeed(undefined)`. +- Use `Effect.cached` when concurrent callers should share one in-flight + computation. + +For background loops, use `Effect.repeat` or `Effect.schedule` with +`Effect.forkScoped` in the owning layer/state scope. + +## Promise And ALS Bridges + +[`EffectBridge`](../../src/effect/bridge.ts) is the sanctioned helper for +Promise/callback interop that needs to preserve instance/workspace context. +Keep it, but reduce its dependency on legacy `Instance.current` / +`Instance.restore` over time. + +`Instance.bind` / `Instance.restore` are transitional legacy tools. Use +them only for native callbacks that still require legacy ALS context. Do +not use them for `setTimeout`, `Promise.then`, `EventEmitter.on`, or +Effect fibers. + +## Testing + +Detailed test migration rules live in +[`test/EFFECT_TEST_MIGRATION.md`](../../test/EFFECT_TEST_MIGRATION.md). + +Core pattern: + +```ts +const it = testEffect(Layer.mergeAll(MyService.defaultLayer)) + +describe("my service", () => { + it.instance("does the thing", () => + Effect.gen(function* () { + const svc = yield* MyService.Service + expect(yield* svc.run()).toEqual("ok") + }), + ) +}) +``` + +Rules: + +- Use `it.effect(...)` for TestClock/TestConsole tests. +- Use `it.live(...)` for real timers, filesystem mtimes, child processes, + git, locks, or other live integration behavior. +- Use `it.instance(...)` for service tests that need a scoped instance. +- Prefer Effect-aware fixtures from `test/fixture/fixture.ts`. +- Avoid sleeps; wait for real events or deterministic state transitions. +- Avoid mutable `process.env`, `Flag`, or module-global changes after + layers are built. +- Use `Layer.mock` for partial service stubs. +- Avoid custom `ManagedRuntime`, `attach(...)`, or ad hoc `run(...)` test + wrappers. + +## Verification + +From `packages/opencode`: + +```bash +bun run typecheck +bun run test -- path/to/test.ts +``` + +Do not run tests from the repo root; the repo has a guard for that. diff --git a/packages/opencode/specs/effect/migration.md b/packages/opencode/specs/effect/migration.md index 01af9da6ce7..5355feccc72 100644 --- a/packages/opencode/specs/effect/migration.md +++ b/packages/opencode/specs/effect/migration.md @@ -1,299 +1,62 @@ -# Effect patterns +# Effect Migration Patterns -Practical reference for new and migrated Effect code in `packages/opencode`. +This is the compact reference for moving code toward the current Effect +shape. The high-level roadmap is [`todo.md`](./todo.md); examples and +rules are in [`guide.md`](./guide.md). -## Choose scope +## Default Shape -Use `InstanceState` (from `src/effect/instance-state.ts`) for services that need per-directory state, per-instance cleanup, or project-bound background work. InstanceState uses a `ScopedCache` keyed by directory, so each open project gets its own copy of the state that is automatically cleaned up on disposal. +- Service methods return `Effect`. +- Service methods are named with `Effect.fn("Domain.method")`. +- Expected failures are typed errors on the error channel. +- Dependencies are yielded once at layer construction and closed over by + methods. +- `defaultLayer` wires production dependencies; tests can use open layers + when replacing dependencies. -Use `makeRuntime` (from `src/effect/run-service.ts`) to create a per-service `ManagedRuntime` that lazily initializes and shares layers via a global `memoMap`. Returns `{ runPromise, runFork, runCallback }`. +## Instance State -- Global services (no per-directory state): Account, Auth, AppFileSystem, Installation, Truncate, Worktree -- Instance-scoped (per-directory state via InstanceState): Agent, Bus, Command, Config, File, FileWatcher, Format, LSP, MCP, Permission, Plugin, ProviderAuth, Pty, Question, SessionStatus, Skill, Snapshot, ToolRegistry, Vcs +Use `InstanceState` for per-directory state, subscriptions, scoped +background work, and per-instance cleanup. -Rule of thumb: if two open directories should not share one copy of the service, it needs `InstanceState`. +Do not add ad hoc `started` flags on top of `InstanceState`; the scoped +cache handles run-once and concurrent deduplication. -## Instance context transition +## Runtime Boundaries -See `instance-context.md` for the phased plan to remove the legacy ALS / promise-backed `Instance` helper and move request / CLI / tool boundaries onto Effect-provided instance scope. +Prefer `AppRuntime` for crossing from non-Effect code into the shared app +layer. -## Service shape +`makeRuntime(...)` exists for intentional service-local boundaries and +legacy facades. Do not add new service-local runtimes unless the service is +genuinely outside `AppLayer`. -Every service follows the same pattern: one module, flat top-level exports, traced Effect methods, and a self-reexport at the bottom when the file is the public module. +## Platform Edges -```ts -export interface Interface { - readonly get: (id: FooID) => Effect.Effect -} +- Use `AppFileSystem.Service` instead of raw filesystem APIs in + effectified services. +- Use `AppProcess.Service` instead of raw process wrappers. +- Use `HttpClient.HttpClient` instead of raw `fetch` in Effect code. +- Use `Effect.cached` for shared in-flight work. +- Use `Effect.callback` for callback APIs. -export class Service extends Context.Service()("@opencode/Foo") {} +## Tests During Migration -export const layer = Layer.effect( - Service, - Effect.gen(function* () { - const state = yield* InstanceState.make( - Effect.fn("Foo.state")(() => Effect.succeed({ ... })), - ) +When migrating code, migrate touched tests toward +[`test/EFFECT_TEST_MIGRATION.md`](../../test/EFFECT_TEST_MIGRATION.md): - const get = Effect.fn("Foo.get")(function* (id: FooID) { - const s = yield* InstanceState.get(state) - // ... - }) +- `testEffect(...)` +- `it.effect`, `it.live`, or `it.instance` +- explicit layers for behavior changes +- deterministic waits instead of sleeps +- no mutable env/global flags after layers are built - return Service.of({ get }) - }), -) +## Migration Checklist -export const defaultLayer = layer.pipe(Layer.provide(FooDep.layer)) - -export * as Foo from "." -``` - -Rules: - -- Keep the service surface in one module; prefer flat top-level exports over `export namespace Foo { ... }` -- Use `Effect.fn("Foo.method")` for Effect methods -- Use a self-reexport (`export * as Foo from "."` or `"./foo"`) for the public namespace projection -- Avoid service-local `makeRuntime(...)` facades unless a file is still intentionally in the older migration phase -- No `Layer.fresh` for normal per-directory isolation; use `InstanceState` - -## Schema → Zod interop - -When a service uses Effect Schema internally but needs Zod schemas for the HTTP layer, derive Zod from Schema using the `zod()` helper from `@opencode-ai/core/effect-zod`: - -```ts -import { zod } from "@opencode-ai/core/effect-zod" - -export const ZodInfo = zod(Info) // derives z.ZodType from Schema.Union -``` - -See `Auth.ZodInfo` for the canonical example. - -## InstanceState init patterns - -The `InstanceState.make` init callback receives a `Scope`, so you can use `Effect.acquireRelease`, `Effect.addFinalizer`, and `Effect.forkScoped` inside it. Resources acquired this way are automatically cleaned up when the instance is disposed or invalidated by `ScopedCache`. This makes it the right place for: - -- **Subscriptions**: Yield `Bus.Service` at the layer level, then use `Stream` + `forkScoped` inside the init closure. The fiber is automatically interrupted when the instance scope closes: - -```ts -const bus = yield * Bus.Service - -const cache = - yield * - InstanceState.make( - Effect.fn("Foo.state")(function* (ctx) { - // ... load state ... - - yield* bus.subscribeAll().pipe( - Stream.runForEach((event) => - Effect.sync(() => { - /* handle */ - }), - ), - Effect.forkScoped, - ) - - return { - /* state */ - } - }), - ) -``` - -- **Resource cleanup**: Use `Effect.acquireRelease` or `Effect.addFinalizer` for resources that need teardown (native watchers, process handles, etc.): - -```ts -yield * - Effect.acquireRelease( - Effect.sync(() => nativeAddon.watch(dir)), - (watcher) => Effect.sync(() => watcher.close()), - ) -``` - -- **Background fibers**: Use `Effect.forkScoped` — the fiber is interrupted on disposal. -- **Side effects at init**: Config notification, event wiring, etc. all belong in the init closure. Callers just do `InstanceState.get(cache)` to trigger everything, and `ScopedCache` deduplicates automatically. - -The key insight: don't split init into a separate method with a `started` flag. Put everything in the `InstanceState.make` closure and let `ScopedCache` handle the run-once semantics. - -## Effect.cached for deduplication - -Use `Effect.cached` when multiple concurrent callers should share a single in-flight computation. It memoizes the result and deduplicates concurrent fibers — second caller joins the first caller's fiber instead of starting a new one. - -```ts -// Inside the layer — yield* to initialize the memo -let cached = yield * Effect.cached(loadExpensive()) - -const get = Effect.fn("Foo.get")(function* () { - return yield* cached // concurrent callers share the same fiber -}) - -// To invalidate: swap in a fresh memo -const invalidate = Effect.fn("Foo.invalidate")(function* () { - cached = yield* Effect.cached(loadExpensive()) -}) -``` - -Prefer `Effect.cached` over these patterns: - -- Storing a `Fiber.Fiber | undefined` with manual check-and-fork (e.g. `file/index.ts` `ensure`) -- Storing a `Promise` task for deduplication (e.g. `skill/index.ts` `ensure`) -- `let cached: X | undefined` with check-and-load (races when two callers see `undefined` before either resolves) - -`Effect.cached` handles the run-once + concurrent-join semantics automatically. For invalidatable caches, reassign with `yield* Effect.cached(...)` — the old memo is discarded. - -## Scheduled Tasks - -For loops or periodic work, use `Effect.repeat` or `Effect.schedule` with `Effect.forkScoped` in the layer definition. - -## Preferred Effect services - -In effectified services, prefer yielding existing Effect services over dropping down to ad hoc platform APIs. - -Prefer these first: - -- `FileSystem.FileSystem` instead of raw `fs/promises` for effectful file I/O -- `ChildProcessSpawner.ChildProcessSpawner` with `ChildProcess.make(...)` instead of custom process wrappers -- `HttpClient.HttpClient` instead of raw `fetch` -- `Path.Path` instead of mixing path helpers into service code when you already need a path service -- `Config` for effect-native configuration reads -- `Clock` / `DateTime` for time reads inside effects - -## Child processes - -For child process work in services, yield `ChildProcessSpawner.ChildProcessSpawner` in the layer and use `ChildProcess.make(...)`. - -Keep shelling-out code inside the service, not in callers. - -## Shared leaf models - -Shared schema or model files can stay outside the service namespace when lower layers also depend on them. - -That is fine for leaf files like `schema.ts`. Keep the service surface in the owning namespace. - -## Migration checklist - -Service-shape migrated (single namespace, traced methods, `InstanceState` where needed). - -This checklist is only about the service shape migration. Many of these services still keep `makeRuntime(...)` plus async facade exports; that facade-removal phase is tracked separately in `facades.md`. - -- [x] `Account` — `account/index.ts` -- [x] `Agent` — `agent/agent.ts` -- [x] `AppFileSystem` — `filesystem/index.ts` -- [x] `Auth` — `auth/index.ts` (uses `zod()` helper for Schema→Zod interop) -- [x] `Bus` — `bus/index.ts` -- [x] `Command` — `command/index.ts` -- [x] `Config` — `config/config.ts` -- [x] `Discovery` — `skill/discovery.ts` (dependency-only layer, no standalone runtime) -- [x] `File` — `file/index.ts` -- [x] `FileWatcher` — `file/watcher.ts` -- [x] `Format` — `format/index.ts` -- [x] `Installation` — `installation/index.ts` -- [x] `LSP` — `lsp/index.ts` -- [x] `MCP` — `mcp/index.ts` -- [x] `McpAuth` — `mcp/auth.ts` -- [x] `Permission` — `permission/index.ts` -- [x] `Plugin` — `plugin/index.ts` -- [x] `Project` — `project/project.ts` -- [x] `ProviderAuth` — `provider/auth.ts` -- [x] `Pty` — `pty/index.ts` -- [x] `Question` — `question/index.ts` -- [x] `SessionStatus` — `session/status.ts` -- [x] `Skill` — `skill/index.ts` -- [x] `Snapshot` — `snapshot/index.ts` -- [x] `ToolRegistry` — `tool/registry.ts` -- [x] `Truncate` — `tool/truncate.ts` -- [x] `Vcs` — `project/vcs.ts` -- [x] `Worktree` — `worktree/index.ts` - -- [x] `Session` — `session/index.ts` -- [x] `SessionProcessor` — `session/processor.ts` -- [x] `SessionPrompt` — `session/prompt.ts` -- [x] `SessionCompaction` — `session/compaction.ts` -- [x] `SessionSummary` — `session/summary.ts` -- [x] `SessionRevert` — `session/revert.ts` -- [x] `Instruction` — `session/instruction.ts` -- [x] `SystemPrompt` — `session/system.ts` -- [x] `Provider` — `provider/provider.ts` -- [x] `Storage` — `storage/storage.ts` -- [x] `ShareNext` — `share/share-next.ts` -- [x] `SessionTodo` — `session/todo.ts` - -Still open at the service-shape level: - -- [ ] `SyncEvent` — `sync/index.ts` (deferred pending sync with James) -- [ ] `Workspace` — `control-plane/workspace.ts` (deferred pending sync with James) - -## Tool migration - -Tool-specific migration guidance and checklist live in `tools.md`. - -## Effect service adoption in already-migrated code - -Some already-effectified areas still use raw `Filesystem.*` or `Process.spawn` in their implementation or helper modules. These are low-hanging fruit — the layers already exist, they just need the dependency swap. - -### `Filesystem.*` → `AppFileSystem.Service` (yield in layer) - -- [x] `config/config.ts` — `installDependencies()` now uses `AppFileSystem` -- [x] `provider/provider.ts` — recent model state now reads via `AppFileSystem.Service` - -### `Process.spawn` → `ChildProcessSpawner` (yield in layer) - -- [x] `format/formatter.ts` — direct `Process.spawn()` checks removed (`air`, `uv`) -- [ ] `lsp/server.ts` — multiple `Process.spawn()` installs/download helpers - -## Filesystem consolidation - -`util/filesystem.ts` is still used widely across `src/`, and raw `fs` / `fs/promises` imports still exist in multiple tooling and infrastructure files. As services and tools are effectified, they should switch from `Filesystem.*` to yielding `AppFileSystem.Service` where possible — this should happen naturally during each migration, not as a separate sweep. - -Tool-specific filesystem cleanup notes live in `tools.md`. - -## Primitives & utilities - -- [ ] `util/lock.ts` — reader-writer lock → Effect Semaphore/Permit -- [ ] `util/flock.ts` — file-based distributed lock with heartbeat → Effect.repeat + addFinalizer -- [ ] `util/process.ts` — child process spawn wrapper → return Effect instead of Promise -- [ ] `util/lazy.ts` — replace uses in Effect code with Effect.cached; keep for sync-only code - -## Destroying the facades - -This phase is no longer broadly open. There are 5 `makeRuntime(...)` call sites under `src/`, and only a small subset are still ordinary facade-removal targets. The live checklist now lives in `facades.md`. - -These facades exist because cyclic imports used to force each service to build its own independent runtime. Now that the layer DAG is acyclic and `AppRuntime` (`src/effect/app-runtime.ts`) composes everything into one `ManagedRuntime`, we're removing them. - -### Process - -For each service, the migration is roughly: - -1. **Find callers.** `grep -n "Namespace\.(methodA|methodB|...)"` across `src/` and `test/`. Skip the service file itself. -2. **Migrate production callers.** For each effectful caller that does `Effect.tryPromise(() => Namespace.method(...))`: - - Add the service to the caller's layer R type (`Layer.Layer`) - - Yield it at the top of the layer: `const ns = yield* Namespace.Service` - - Replace `Effect.tryPromise(() => Namespace.method(...))` with `yield* ns.method(...)` (or `ns.method(...).pipe(Effect.orElseSucceed(...))` for the common fallback case) - - Add `Layer.provide(Namespace.defaultLayer)` to the caller's own `defaultLayer` chain -3. **Fix tests that used the caller's raw `.layer`.** Any test that composes `Caller.layer` (not `defaultLayer`) needs to also provide the newly-required service tag. The fastest fix is usually switching to `Caller.defaultLayer` since it now pulls in the new dependency. -4. **Migrate test callers of the facade.** Tests calling `Namespace.method(...)` directly get converted to full effectful style using `testEffect(Namespace.defaultLayer)` + `it.live` / `it.effect` + `yield* svc.method(...)`. Don't wrap the test body in `Effect.promise(async () => {...})` — do the whole thing in `Effect.gen` and use `AppFileSystem.Service` / `tmpdirScoped` / `Effect.addFinalizer` for what used to be raw `fs` / `Bun.write` / `try/finally`. -5. **Delete the facades.** Once `grep` shows zero callers, remove the `export async function` block AND the `makeRuntime(...)` line from the service namespace. Also remove the now-unused `import { makeRuntime }`. - -### Pitfalls - -- **Layer caching inside tests.** `testEffect(layer)` constructs the Storage (or whatever) service once and memoizes it. If a test then tries `inner.pipe(Effect.provide(customStorage))` to swap in a differently-configured Storage, the outer cached one wins and the inner provision is a no-op. Fix: wrap the overriding layer in `Layer.fresh(...)`, which forces a new instance to be built instead of hitting the memoMap cache. This lets a single `testEffect(...)` serve both simple and per-test-customized cases. -- **`Effect.tryPromise` → `yield*` drops the Promise layer.** The old code was `Effect.tryPromise(() => Storage.read(...))` — a `tryPromise` wrapper because the facade returned a Promise. The new code is `yield* storage.read(...)` directly — the service method already returns an Effect, so no wrapper is needed. Don't reach for `Effect.promise` or `Effect.tryPromise` during migration; if you're using them on a service method call, you're doing it wrong. -- **Raw `.layer` test callers break silently in the type checker.** When you add a new R requirement to a service's `.layer`, any test that composes it raw (not `defaultLayer`) becomes under-specified. `tsgo` will flag this — the error looks like `Type 'Storage.Service' is not assignable to type '... | Service | TestConsole'`. Usually the fix is to switch that composition to `defaultLayer`, or add `Layer.provide(NewDep.defaultLayer)` to the custom composition. -- **Tests that do async setup with `fs`, `Bun.write`, `tmpdir`.** Convert these to `AppFileSystem.Service` calls inside `Effect.gen`, and use `tmpdirScoped()` instead of `tmpdir()` so cleanup happens via the scope finalizer. For file operations on the actual filesystem (not via a service), a small helper like `const writeJson = Effect.fnUntraced(function* (file, value) { const fs = yield* AppFileSystem.Service; yield* fs.makeDirectory(path.dirname(file), { recursive: true }); yield* fs.writeFileString(file, JSON.stringify(value, null, 2)) })` keeps the migration tests clean. - -### Migration log - -- `SessionStatus` — migrated 2026-04-11. Replaced the last route and retry-policy callers with `AppRuntime.runPromise(SessionStatus.Service.use(...))` and removed the `makeRuntime(...)` facade. -- `ShareNext` — migrated 2026-04-11. Swapped remaining async callers to `AppRuntime.runPromise(ShareNext.Service.use(...))`, removed the `makeRuntime(...)` facade, and kept instance bootstrap on the shared app runtime. -- `SessionTodo` — migrated 2026-04-10. Already matched the target service shape in `session/todo.ts`: single namespace, traced Effect methods, and no `makeRuntime(...)` facade remained; checklist updated to reflect the completed migration. -- `Storage` — migrated 2026-04-10. One production caller (`Session.diff`) and all storage.test.ts tests converted to effectful style. Facades and `makeRuntime` removed. -- `SessionRunState` — migrated 2026-04-11. Single caller in `server/routes/instance/session.ts` converted; facade removed. -- `Account` — migrated 2026-04-11. Callers in `server/routes/instance/experimental.ts` and `cli/cmd/account.ts` converted; facade removed. -- `Instruction` — migrated 2026-04-11. Test-only callers converted; facade removed. -- `FileWatcher` — migrated 2026-04-11. Callers in `project/bootstrap.ts` and test converted; facade removed. -- `Question` — migrated 2026-04-11. Callers in `server/routes/instance/question.ts` and test converted; facade removed. -- `Truncate` — migrated 2026-04-11. Caller in `tool/tool.ts` and test converted; facade removed. - -## Route handler effectification - -Route-handler migration guidance and checklist live in `routes.md`. +- [ ] The code has a single Effect body instead of Promise wrappers around + service calls. +- [ ] Expected failures are typed errors, not thrown exceptions or defects. +- [ ] Layer requirements are explicit. +- [ ] Tests use Effect-aware fixtures and focused layers. +- [ ] Public behavior and wire shapes are preserved unless intentionally + changed. diff --git a/packages/opencode/specs/effect/routes.md b/packages/opencode/specs/effect/routes.md index 3bf7e1b5561..7dcd80ce96b 100644 --- a/packages/opencode/specs/effect/routes.md +++ b/packages/opencode/specs/effect/routes.md @@ -1,64 +1,61 @@ -# Route handler effectification +# HTTP Route Patterns -Practical reference for converting server route handlers in `packages/opencode` to a single `AppRuntime.runPromise(Effect.gen(...))` body. +Current guidance for `packages/opencode/src/server/routes/instance/httpapi`. -## Goal +## Handler Shape -Route handlers should wrap their entire body in a single `AppRuntime.runPromise(Effect.gen(...))` call, yielding services from context rather than calling facades one-by-one. - -This eliminates multiple `runPromise` round-trips and lets handlers compose naturally. +Use `HttpApiBuilder.group(...)` for normal JSON and streaming HTTP API +endpoints. Yield stable services once while building the handler layer, +then close over those services in endpoint implementations. ```ts -// Before - one facade call per service -;async (c) => { - await SessionRunState.assertNotBusy(id) - await Session.removeMessage({ sessionID: id, messageID }) - return c.json(true) -} - -// After - one Effect.gen, yield services from context -;async (c) => { - await AppRuntime.runPromise( - Effect.gen(function* () { - const state = yield* SessionRunState.Service - const session = yield* Session.Service - yield* state.assertNotBusy(id) - yield* session.removeMessage({ sessionID: id, messageID }) - }), - ) - return c.json(true) -} +export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "session", (handlers) => + Effect.gen(function* () { + const session = yield* Session.Service + + return handlers.handle("list", () => session.list()) + }), +) ``` -## Rules +Use raw `HttpRouter` only for routes that do not fit the request/response +HttpApi model, such as WebSocket upgrades or catch-all fallback routes. + +Do not rebuild stable layers inside request handlers. Provide stable +services at the route/layer boundary and use request-level provisioning +only for request-derived context. + +## Error Boundaries + +Expected service errors should be mapped at the handler boundary to +endpoint-declared public HTTP errors. Keep one-off mappings inline. Extract +small helpers when the same mapping repeats. -- Wrap the whole handler body in one `AppRuntime.runPromise(Effect.gen(...))` call when the handler is service-heavy. -- Yield services from context instead of calling async facades repeatedly. -- When independent service calls can run in parallel, use `Effect.all(..., { concurrency: "unbounded" })`. -- Prefer one composed Effect body over multiple separate `runPromise(...)` calls in the same handler. +Generic middleware should not become a domain-error mapper. It should +handle cross-cutting concerns and final unknown-defect fallback. -## Current route files +Public JSON errors should be explicit schema contracts declared on each +endpoint or group. Built-in `HttpApiError.*` is fine only when its generated +body is intentionally the public wire shape. -Current instance route files live under `src/server/routes/instance`. +Preserve existing `{ name, data }` error bodies until a deliberate breaking +API change. -Files that are already mostly on the intended service-yielding shape: +## OpenAPI Compatibility -- [x] `server/routes/instance/question.ts` — handlers yield `Question.Service` -- [x] `server/routes/instance/provider.ts` — handlers yield `Provider.Service`, `ProviderAuth.Service`, and `Config.Service` -- [x] `server/routes/instance/permission.ts` — handlers yield `Permission.Service` -- [x] `server/routes/instance/mcp.ts` — handlers mostly yield `MCP.Service` -- [x] `server/routes/instance/pty.ts` — handlers yield `Pty.Service` +`public.ts` still owns SDK/OpenAPI compatibility transforms. Shrink those +transforms by tightening source schemas one workaround at a time. -Files still worth tracking here: +When an OpenAPI-visible source schema changes: -- [ ] `server/routes/instance/session.ts` — still the heaviest mixed file; many handlers are composed, but the file still mixes patterns and has direct `Bus.publish(...)` / `Session.list(...)` usage -- [ ] `server/routes/instance/index.ts` — mostly converted, but still has direct `Instance.dispose()` / `Instance.*` reads for `/instance/dispose` and `/path` -- [ ] `server/routes/instance/file.ts` — most handlers yield services, but `/find` still passes `Instance.directory` directly into ripgrep and `/find/symbol` is still stubbed -- [ ] `server/routes/instance/experimental.ts` — mixed state; many handlers are composed, but some still rely on `runRequest(...)` or direct `Instance.project` reads -- [ ] `server/routes/instance/middleware.ts` — still enters the instance via `Instance.provide(...)` -- [ ] `server/routes/global.ts` — still uses `Instance.disposeAll()` and remains partly outside the fully-composed style +- verify the generated SDK diff is intentional +- preserve legacy compatibility unless the PR explicitly changes it +- prefer source-schema fixes over new post-processing rules -## Notes +## Checklist For Route PRs -- Route conversion is now less about facade removal and more about removing the remaining direct `Instance.*` reads, `Instance.provide(...)` boundaries, and small Promise-style bridges inside route files. -- `jsonRequest(...)` / `runRequest(...)` already provide a good intermediate shape for many handlers. The remaining cleanup is mostly consistency work in the heavier files. +- [ ] Stable services are yielded at handler-layer construction. +- [ ] Expected domain errors are translated at the route boundary. +- [ ] Endpoint/group error schemas describe the public body and status. +- [ ] Middleware does not gain new domain-specific name checks. +- [ ] Raw routes are used only when HttpApi is the wrong abstraction. diff --git a/packages/opencode/specs/effect/schema.md b/packages/opencode/specs/effect/schema.md index e20605c3bc3..12ac267048d 100644 --- a/packages/opencode/specs/effect/schema.md +++ b/packages/opencode/specs/effect/schema.md @@ -1,49 +1,34 @@ -# Schema migration +# Schema Migration -Practical reference for migrating data types in `packages/opencode` from -Zod-first definitions to Effect Schema with Zod compatibility shims. +Use Effect Schema as the source of truth for domain models, DTOs, IDs, +inputs, outputs, and typed errors. -## Goal +This is guidance, not an inventory. Do not use this file to track which +schema modules are complete; verify current state with `git grep` before +starting a migration. -Use Effect Schema as the source of truth for domain models, IDs, inputs, -outputs, and typed errors. Keep Zod available at existing HTTP, tool, and -compatibility boundaries by exposing a `.zod` static derived from the Effect -schema via `@opencode-ai/core/effect-zod`. +## Preferred Shapes -The long-term driver is `specs/effect/http-api.md` — once the HTTP server -moves to `@effect/platform`, every Schema-first DTO can flow through -`HttpApi` / `HttpRouter` without a zod translation layer, and the entire -`effect-zod` walker plus every `.zod` static can be deleted. - -## Preferred shapes - -### Data objects - -Use `Schema.Class` for structured data. +Use `Schema.Class` for exported data objects with a clear domain identity: ```ts export class Info extends Schema.Class("Foo.Info")({ id: FooID, name: Schema.String, enabled: Schema.Boolean, -}) { - static readonly zod = zod(Info) -} +}) {} ``` -If the class cannot reference itself cleanly during initialization, use the -two-step `withStatics` pattern: +Use `Schema.Struct` for local shapes and simple nested objects: ```ts -export const Info = Schema.Struct({ +const Payload = Schema.Struct({ id: FooID, - name: Schema.String, -}).pipe(withStatics((s) => ({ zod: zod(s) }))) + value: Schema.String, +}) ``` -### Errors - -Use `Schema.TaggedErrorClass` for domain errors. +Use `Schema.TaggedErrorClass` for expected domain errors: ```ts export class NotFoundError extends Schema.TaggedErrorClass()("FooNotFoundError", { @@ -51,349 +36,53 @@ export class NotFoundError extends Schema.TaggedErrorClass()("Foo }) {} ``` -### IDs and branded leaf types - -Keep branded/schema-backed IDs as Effect schemas and expose -`static readonly zod` for compatibility when callers still expect Zod. - -### Refinements - -Reuse named refinements instead of re-spelling `z.number().int().positive()` -in every schema. The `effect-zod` walker translates the Effect versions into -the corresponding zod methods, so JSON Schema output (`type: integer`, -`exclusiveMinimum`, `pattern`, `format: uuid`, …) is preserved. - -```ts -const PositiveInt = Schema.Number.check(Schema.isInt()).check(Schema.isGreaterThan(0)) -const NonNegativeInt = Schema.Number.check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) -const HexColor = Schema.String.check(Schema.isPattern(/^#[0-9a-fA-F]{6}$/)) -``` - -See `test/util/effect-zod.test.ts` for the full set of translated checks. - -## Compatibility rule - -During migration, route validators, tool parameters, and any existing -Zod-based boundary should consume the derived `.zod` schema instead of -maintaining a second hand-written Zod schema. - -The default should be: - -- Effect Schema owns the type -- `.zod` exists only as a compatibility surface -- new domain models should not start Zod-first unless there is a concrete - boundary-specific need - -## When Zod can stay - -It is fine to keep a Zod-native schema temporarily when: - -- the type is only used at an HTTP or tool boundary and is not reused elsewhere -- the validator depends on Zod-only transforms or behavior not yet covered by `zod()` -- the migration would force unrelated churn across a large call graph - -When this happens, prefer leaving a short note or TODO rather than silently -creating a parallel schema source of truth. - -## Escape hatches - -The walker in `@opencode-ai/core/effect-zod` exposes two explicit escape hatches for -cases the pure-Schema path cannot express. Each one stays in the codebase -only as long as its upstream or local dependency requires it — inline -comments document when each can be deleted. - -### `ZodOverride` annotation - -Replaces the entire derivation with a hand-crafted zod schema. Used when: - -- the target carries external `$ref` metadata (e.g. - `config/model-id.ts` points at `https://models.dev/...`) -- the target is a zod-only schema that cannot yet be expressed as Schema - (e.g. `ConfigAgent.Info`, `Log.Level`) - -### Local `DeepMutable` in `config/config.ts` - -`Schema.Struct` produces `readonly` types. Some consumer code (notably the -`Config` service) mutates `Info` objects directly, so a readonly-stripping -utility is needed when casting the derived zod schema's output type. - -`Types.DeepMutable` from effect-smol would be a drop-in, but it widens -`unknown` to `{}` in the fallback branch — a bug that affects any schema -using `Schema.Record(String, Schema.Unknown)`. - -Tracked upstream as `effect:core/x228my`: "Types.DeepMutable widens unknown -to `{}`." Once that lands, the local `DeepMutable` copy can be deleted and -`Types.DeepMutable` used directly. +Use branded schema-backed IDs for single-value domain identifiers. -## Ordering +## Boundary Rule -Migrate in this order: +Effect Schema should own the type. Boundaries should consume Effect Schema +directly or use narrow boundary-specific helpers. Avoid reintroducing a +generic Effect Schema -> Zod bridge. -1. Shared leaf models and `schema.ts` files -2. Exported `Info`, `Input`, `Output`, and DTO types -3. Tagged domain errors -4. Service-local internal models -5. Route and tool boundary validators that can switch to `.zod` +Current intentional boundaries: -This keeps shared types canonical first and makes boundary updates mostly -mechanical. +- Public plugin tools still expose Zod through `tool.schema = z`. +- Tool parameters use tool-specific JSON Schema helpers. +- Public config and TUI schema generation goes through the schema script. +- AI SDK object generation uses Standard Schema / JSON Schema helpers. -## Progress tracker +When Zod must stay temporarily, leave a short note explaining the boundary +or compatibility reason. -### `src/config/` ✅ complete +## Refinements -All of `packages/opencode/src/config/` has been migrated. Files that still -import `z` do so only for local `ZodOverride` bridges or for `z.ZodType` -type annotations — the `export const ` values are all Effect -Schema at source. +Reuse named refinements instead of re-spelling constraints: -A file is considered "done" when: - -- its exported schema values (`Info`, `Input`, `Event`, `Definition`, etc.) - are authored as Effect Schema -- any remaining zod is either a derived compat bridge (via `zod()` / - `zodObject()`), a `z.ZodType` type annotation, or a documented - `ZodOverride` escape hatch — never a hand-written parallel source of truth - -Files that meet this bar but still carry a compat bridge are checked off -with an inline note describing the bridge and what unblocks its removal. - -- [x] skills, formatter, console-state, mcp, lsp, permission (leaves), model-id, command, plugin, provider -- [x] server, layout -- [x] keybinds -- [x] permission#Info -- [x] agent -- [x] config.ts root - -### `src/*/schema.ts` leaf modules - -These are the highest-priority next targets. Each is a small, self-contained -schema module with a clear domain. - -- [x] `src/account/schema.ts` -- [x] `src/control-plane/schema.ts` -- [x] `src/permission/schema.ts` -- [x] `src/project/schema.ts` -- [x] `src/provider/schema.ts` -- [x] `src/pty/schema.ts` -- [x] `src/question/schema.ts` -- [x] `src/session/schema.ts` -- [x] `src/storage/schema.ts` -- [x] `src/sync/schema.ts` -- [x] `src/tool/schema.ts` -- [x] `src/util/schema.ts` - -### Session domain - -Major cluster. Message + event types flow through the SSE API and every SDK -output, so byte-identical SDK surface is critical. - -Suggested order for this cluster, starting from the leaves that `session.ts` -and the SSE/event surface depend on: - -1. `src/session/schema.ts` ✅ already migrated -2. `src/provider/schema.ts` if `message-v2.ts` still relies on zod-first IDs -3. `src/lsp/*` schema leaves needed by `LSP.Range` -4. `src/snapshot/*` leaves used by `Snapshot.FileDiff` -5. `src/session/message-v2.ts` -6. `src/session/message.ts` -7. `src/session/prompt.ts` -8. `src/session/revert.ts` -9. `src/session/summary.ts` -10. `src/session/status.ts` -11. `src/session/todo.ts` -12. `src/session/session.ts` -13. `src/session/compaction.ts` - -Dependency sketch: - -```text -session.ts -|- project/schema.ts -|- control-plane/schema.ts -|- permission/schema.ts -|- snapshot/* -|- message-v2.ts -| |- provider/schema.ts -| |- lsp/* -| |- snapshot/* -| |- sync/index.ts -| `- bus/bus-event.ts -|- sync/index.ts -|- bus/bus-event.ts -`- util/update-schema.ts +```ts +const PositiveInt = Schema.Number.check(Schema.isInt()).check(Schema.isGreaterThan(0)) +const NonNegativeInt = Schema.Number.check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) ``` -Working rule for this cluster: - -- migrate reusable leaf schemas and nested payload objects first -- migrate aggregate DTOs like `Session.Info` after their nested pieces exist as - named Schema values -- leave zod-only event/update helpers in place temporarily when converting - them would force unrelated churn across sync/bus boundaries - -`message-v2.ts` first-pass outline: - -1. Schema-backed imports already available - - `SessionID`, `MessageID`, `PartID` - - `ProviderID`, `ModelID` -2. Local leaf objects to extract and migrate first - - output format payloads - - common part bases like `PartBase` - - timestamp/range helper objects like `time.start/end` - - file/source helper objects - - token/cost/model helper objects -3. Part variants built from those leaves - - `SnapshotPart`, `PatchPart`, `TextPart`, `ReasoningPart` - - `FilePart`, `AgentPart`, `CompactionPart`, `SubtaskPart` - - retry/step/tool related parts -4. Higher-level unions and DTOs - - `FilePartSource` - - part unions - - message unions and assistant/user payloads -5. Errors and event payloads last - - `NamedError.create(...)` shapes can stay temporarily if converting them to - `Schema.TaggedErrorClass` would force unrelated churn - - `SyncEvent.define(...)` and `BusEvent.define(...)` payloads can use - derived `.zod` at remaining zod-based HTTP/OpenAPI boundaries - -Possible later tightening after the Schema-first migration is stable: - -- promote repeated opaque strings and timestamp numbers into branded/newtype - leaf schemas where that adds domain value without changing the wire format - -- [x] `src/session/compaction.ts` -- [x] `src/session/message-v2.ts` -- [x] `src/session/message.ts` -- [x] `src/session/prompt.ts` -- [x] `src/session/revert.ts` -- [x] `src/session/session.ts` -- [x] `src/session/status.ts` -- [x] `src/session/summary.ts` -- [x] `src/session/todo.ts` - -### Provider domain - -- [x] `src/provider/auth.ts` -- [x] `src/provider/models.ts` -- [x] `src/provider/provider.ts` - -### Tool schemas - -Each tool declares its parameters via a zod schema. Tools are consumed by -both the in-process runtime and the AI SDK's tool-calling layer, so the -emitted JSON Schema must stay byte-identical. - -- [x] `src/tool/apply_patch.ts` -- [x] `src/tool/bash.ts` -- [x] `src/tool/edit.ts` -- [x] `src/tool/glob.ts` -- [x] `src/tool/grep.ts` -- [x] `src/tool/invalid.ts` -- [x] `src/tool/lsp.ts` -- [x] `src/tool/plan.ts` -- [x] `src/tool/question.ts` -- [x] `src/tool/read.ts` -- [x] `src/tool/registry.ts` -- [x] `src/tool/skill.ts` -- [x] `src/tool/task.ts` -- [x] `src/tool/todo.ts` -- [x] `src/tool/tool.ts` -- [x] `src/tool/webfetch.ts` -- [x] `src/tool/websearch.ts` -- [x] `src/tool/write.ts` - -### HTTP route boundaries - -Every file in `src/server/routes/` uses hono-openapi with zod validators for -route inputs/outputs. Migrating these individually is the last step; most -will switch to `.zod` derived from the Schema-migrated domain types above, -which means touching them is largely mechanical once the domain side is -done. - -- [ ] `src/server/error.ts` -- [x] `src/server/event.ts` -- [x] `src/server/projectors.ts` -- [ ] `src/server/routes/control/index.ts` -- [ ] `src/server/routes/control/workspace.ts` -- [ ] `src/server/routes/global.ts` -- [ ] `src/server/routes/instance/index.ts` -- [ ] `src/server/routes/instance/config.ts` -- [ ] `src/server/routes/instance/event.ts` -- [ ] `src/server/routes/instance/experimental.ts` -- [ ] `src/server/routes/instance/file.ts` -- [ ] `src/server/routes/instance/mcp.ts` -- [ ] `src/server/routes/instance/permission.ts` -- [ ] `src/server/routes/instance/project.ts` -- [ ] `src/server/routes/instance/provider.ts` -- [ ] `src/server/routes/instance/pty.ts` -- [ ] `src/server/routes/instance/question.ts` -- [ ] `src/server/routes/instance/session.ts` -- [ ] `src/server/routes/instance/sync.ts` -- [ ] `src/server/routes/instance/tui.ts` - -The bigger prize for this group is the `@effect/platform` HTTP migration -described in `specs/effect/http-api.md`. Once that lands, every one of -these files changes shape entirely (`HttpApi.endpoint(...)` and friends), -so the Schema-first domain types become a prerequisite rather than a -sibling task. - -### Everything else +Prefer domain-named leaf schemas when the name improves callers or error +messages. Avoid adding brands purely for novelty. -Small / shared / control-plane / CLI. Mostly independent; can be done -piecewise. +## Migration Order -- [ ] `src/acp/agent.ts` -- [ ] `src/agent/agent.ts` -- [x] `src/bus/bus-event.ts` -- [ ] `src/bus/index.ts` -- [ ] `src/cli/cmd/tui/config/tui-migrate.ts` -- [ ] `src/cli/cmd/tui/config/tui-schema.ts` -- [ ] `src/cli/cmd/tui/config/tui.ts` -- [ ] `src/cli/cmd/tui/event.ts` -- [ ] `src/cli/ui.ts` -- [ ] `src/command/index.ts` -- [x] `src/control-plane/adapters/worktree.ts` -- [x] `src/control-plane/types.ts` -- [x] `src/control-plane/workspace.ts` -- [ ] `src/file/index.ts` -- [ ] `src/file/ripgrep.ts` -- [ ] `src/file/watcher.ts` -- [ ] `src/format/index.ts` -- [ ] `src/id/id.ts` -- [ ] `src/ide/index.ts` -- [ ] `src/installation/index.ts` -- [ ] `src/lsp/client.ts` -- [ ] `src/lsp/lsp.ts` -- [ ] `src/mcp/auth.ts` -- [ ] `src/patch/index.ts` -- [ ] `src/plugin/github-copilot/models.ts` -- [ ] `src/project/project.ts` -- [ ] `src/project/vcs.ts` -- [ ] `src/pty/index.ts` -- [ ] `src/skill/index.ts` -- [ ] `src/snapshot/index.ts` -- [ ] `src/storage/db.ts` -- [ ] `src/storage/storage.ts` -- [x] `src/sync/index.ts` — public API (`SyncEvent.define`) is Schema-first; `payloads()` still derives zod for the remaining HTTP/OpenAPI boundary -- [ ] `src/util/fn.ts` -- [ ] `src/util/log.ts` -- [ ] `src/util/update-schema.ts` -- [ ] `src/worktree/index.ts` +For a domain that still has mixed schemas: -### Do-not-migrate +1. Shared leaf models and branded IDs. +2. Exported `Info`, `Input`, `Output`, and event payload types. +3. Expected domain errors. +4. Service-local internal models. +5. HTTP/tool/AI boundary validators. -- `src/util/effect-zod.ts` — the walker itself. Stays zod-importing forever - (it's what emits zod from Schema). Goes away only when the `.zod` - compatibility layer is no longer needed anywhere. +Keep public wire shapes stable unless the PR is explicitly a breaking API +change. -## Notes +## Checklist For A PR -- Use `@opencode-ai/core/effect-zod` for all Schema → Zod conversion. -- Prefer one canonical schema definition. Avoid maintaining parallel Zod and - Effect definitions for the same domain type. -- Keep the migration incremental. Converting the domain model first is more - valuable than converting every boundary in the same change. -- Every migrated file should leave the generated SDK output (`packages/sdk/ -openapi.json` and `packages/sdk/js/src/v2/gen/types.gen.ts`) byte-identical - unless the change is deliberately user-visible. +- [ ] There is one schema source of truth for each migrated type. +- [ ] Remaining Zod is an intentional boundary choice. +- [ ] Public JSON/OpenAPI output is unchanged or intentionally updated. +- [ ] Derived helpers are narrow and boundary-specific. +- [ ] Tests assert behavior, not duplicated schema implementation details. diff --git a/packages/opencode/specs/effect/server-package.md b/packages/opencode/specs/effect/server-package.md index b62ba96ec49..036472337e1 100644 --- a/packages/opencode/specs/effect/server-package.md +++ b/packages/opencode/specs/effect/server-package.md @@ -1,666 +1,58 @@ -# Server package extraction +# Server Package Extraction -Practical reference for extracting a future `packages/server` from the current `packages/opencode` monolith while `packages/core` is still being migrated to Effect. +Practical reference for a future `packages/server` split after the opencode +server moved to the Effect HttpApi backend. -This document is intentionally execution-oriented. +## Current State -It should give an agent enough context to land one incremental PR at a time without needing to rediscover the package strategy, route migration rules, or current constraints. +- The server still lives in `packages/opencode`. +- The runtime and app layer are centralized in `src/effect/app-runtime.ts` and + `src/effect/run-service.ts`. +- The route tree lives under `src/server/routes/instance/httpapi` and is hosted + from `src/server/server.ts`. +- OpenAPI generation is based on the HttpApi contract plus compatibility + translation in `src/server/routes/instance/httpapi/public.ts`. +- There is no standalone `packages/server` workspace yet. -## Goal - -Create `packages/server` as the home for: - -- HTTP contract definitions -- HTTP handler implementations -- OpenAPI generation -- eventual embeddable server APIs for Node apps - -Do this without blocking on the full `packages/core` extraction. - -## Future state +## Future State Target package layout: -- `packages/core` - all opencode services, Effect-first source of truth -- `packages/server` - opencode server, with separate contract and implementation, still producing `openapi.json` -- `packages/cli` - TUI + CLI entrypoints -- `packages/sdk` - generated from the server OpenAPI spec, may add higher-level wrappers -- `packages/plugin` - generated or semi-hand-rolled non-Effect package built from core plugin definitions - -Desired user stories: - -- import from `core` and build a custom agent or app-specific runtime -- import from `server` and embed the full opencode server into an existing Node app -- spawn the CLI and talk to the server through that boundary - -## Current state - -Everything still lives in `packages/opencode`. - -Important current facts: - -- there is no `packages/core` or `packages/cli` workspace yet -- there is no `packages/server` workspace yet on this branch -- the main host server uses Effect `HttpApi` in `src/server/server.ts` -- current OpenAPI generation uses Effect `HttpApi` through `Server.openapi()` and `cli/cmd/generate.ts` -- the Effect runtime and app layer are centralized in `src/effect/app-runtime.ts` and `src/effect/run-service.ts` -- Effect `HttpApi` routes live under `src/server/routes/instance/httpapi/*` - -This means the package split should start from an extraction path, not from greenfield package ownership. - -## Structural reference - -Use `anomalyco/opentunnel` as the structural reference for `packages/server`. - -The important pattern there is: - -- `packages/core` owns services and domain schemas -- `packages/server/src/definition/*` owns pure `HttpApi` contracts -- `packages/server/src/api/*` owns `HttpApiBuilder.group(...)` implementations and server-side middleware wiring -- `packages/server/src/index.ts` becomes the composition root only after the server package really owns runtime hosting - -Relevant `opentunnel` files: - -- `packages/server/src/definition/index.ts` -- `packages/server/src/definition/tunnel.ts` -- `packages/server/src/api/index.ts` -- `packages/server/src/api/tunnel.ts` -- `packages/server/src/api/client.ts` -- `packages/server/src/index.ts` - -The intended direction here is the same, but the current `opencode` package split is earlier in the migration. - -That means: - -- we should follow the same `definition` and `api` naming -- we should keep contract and implementation as separate modules from the start -- we should postpone the runtime composition root until `packages/core` exists enough to support it cleanly - -## Key decision - -Start `packages/server` as a contract and implementation package only. - -Do not make it the runtime host yet. - -Why: - -- `packages/core` does not exist yet -- the current server host still lives in `packages/opencode` -- moving host ownership immediately would force a large package and runtime shuffle while Effect service extraction is still in flight -- if `packages/server` imports services from `packages/opencode` while `packages/opencode` imports `packages/server` to host routes, we create a package cycle immediately - -Short version: - -1. create `packages/server` -2. move pure `HttpApi` contracts there -3. move handler factories there -4. keep `packages/opencode` as the temporary Hono host -5. merge `packages/server` OpenAPI with the legacy Hono OpenAPI during the transition -6. move server hosting later, after `packages/core` exists enough - -## Dependency rule - -Phase 1 rule: - -- `packages/server` must not import from `packages/opencode` - -Allowed in phase 1: - -- `packages/opencode` imports `packages/server` -- `packages/server` accepts host-provided services, layers, or callbacks as inputs -- `packages/server` may temporarily own transport-local placeholder schemas when a canonical shared schema does not exist yet - -Future rule after `packages/core` exists: - -- `packages/server` imports from `packages/core` -- `packages/cli` imports from `packages/server` and `packages/core` -- `packages/opencode` shrinks or disappears as package responsibilities are fully split - -## HttpApi model - -Use Effect v4 `HttpApi` as the source of truth for migrated HTTP routes. - -Important properties from the current `effect` / `effect-smol` model: - -- `HttpApi`, `HttpApiGroup`, and `HttpApiEndpoint` are pure contract definitions -- handlers are implemented separately with `HttpApiBuilder.group(...)` -- OpenAPI can be generated from the contract alone -- auth and middleware can later be modeled with `HttpApiMiddleware.Service` -- SSE and websocket routes are not good first-wave `HttpApi` targets - -This package split should preserve that separation explicitly. - -Default shape for migrated routes: - -- contract lives in `packages/server/src/definition/*` -- implementation lives in `packages/server/src/api/*` -- host mounting stays outside for now - -## OpenAPI rule - -During the transition there is still one spec artifact. - -Default rule: - -- `packages/server` generates OpenAPI from `HttpApi` contract -- `packages/opencode` keeps generating legacy OpenAPI from Hono routes -- the temporary exported server spec is a merged document -- `packages/sdk` continues consuming one `openapi.json` - -Merge safety rules: - -- fail on duplicate `path + method` -- fail on duplicate `operationId` -- prefer explicit summary, description, and operation ids on all new `HttpApi` endpoints - -Practical implication: - -- do not make the SDK consume two specs -- do not switch SDK generation to `packages/server` only until enough of the route surface has moved - -## Package shape - -Minimum viable `packages/server`: - -- `src/index.ts` -- `src/definition/index.ts` -- `src/definition/api.ts` -- `src/definition/question.ts` -- `src/api/index.ts` -- `src/api/question.ts` -- `src/openapi.ts` -- `src/bridge/hono.ts` -- `src/types.ts` - -Later additions, once there is enough real contract surface: - -- `src/api/client.ts` -- runtime composition in `src/index.ts` - -Suggested initial exports: - -- `api` -- `openapi` -- `questionApi` -- `makeQuestionHandler` - -Phase 1 responsibilities: - -- own pure API contracts -- own handler factories for migrated slices -- own contract-generated OpenAPI -- expose host adapters needed by `packages/opencode` - -Phase 1 non-goals: - -- do not own `listen()` -- do not own adapter selection -- do not own global server middleware -- do not own websocket or SSE transport -- do not own process bootstrapping for CLI entrypoints - -## Current source inventory - -These files matter for the first phase. - -Current host and route composition: - -- `src/server/server.ts` -- `src/server/control/index.ts` -- `src/server/routes/instance/index.ts` -- `src/server/middleware.ts` -- `src/server/adapter.bun.ts` -- `src/server/adapter.node.ts` - -Current bridged `HttpApi` slices: - -- `src/server/routes/instance/httpapi/question.ts` -- `src/server/routes/instance/httpapi/permission.ts` -- `src/server/routes/instance/httpapi/provider.ts` -- `src/server/routes/instance/httpapi/config.ts` -- `src/server/routes/instance/httpapi/project.ts` -- `src/server/routes/instance/httpapi/server.ts` - -Current OpenAPI flow: - -- `src/server/server.ts` via `Server.openapi()` -- `src/cli/cmd/generate.ts` -- `packages/sdk/js/script/build.ts` - -Current runtime and service layer: - -- `src/effect/app-runtime.ts` -- `src/effect/run-service.ts` - -## Ownership rules - -Move first into `packages/server`: - -- the experimental `question` `HttpApi` slice -- future `provider` and `config` JSON read slices -- any new `HttpApi` route groups -- transport-local OpenAPI generation for migrated routes - -Keep in `packages/opencode` for now: - -- `src/server/server.ts` -- `src/server/control/index.ts` -- `src/server/routes/**/*.ts` -- `src/server/middleware.ts` -- `src/server/adapter.*.ts` -- `src/effect/app-runtime.ts` -- `src/effect/run-service.ts` -- all Effect services until they move to `packages/core` - -## Placeholder schema rule - -`packages/core` is allowed to lag behind. - -Until shared canonical schemas move to `packages/core`: - -- prefer importing existing Effect Schema DTOs from current locations when practical -- if a route only needs a transport-local type and moving the canonical schema would create unrelated churn, allow a temporary server-local placeholder schema -- if a placeholder is introduced, leave a short note so it does not become permanent - -The default rule from `schema.md` still applies: - -- Effect Schema owns the type -- `.zod` is compatibility only -- avoid parallel hand-written Zod and Effect definitions for the same migrated route shape - -## Host boundary rule - -Until host ownership moves: - -- auth stays at the outer Hono app level -- compression stays at the outer Hono app level -- CORS stays at the outer Hono app level -- instance and workspace lookup stay at the current middleware layer -- `packages/server` handlers should assume the host already provided the right request context -- do not redesign host middleware just to land the package split - -This matches the current guidance in `http-api.md`: - -- keep auth outside the first parallel `HttpApi` slices -- keep instance lookup outside the first parallel `HttpApi` slices -- keep the first migrations transport-focused and semantics-preserving - -## Route selection rules - -Good early migration targets: - -- `question` -- `provider` auth read endpoint -- `config` providers read endpoint -- small read-only instance routes - -Bad early migration targets: - -- `session` -- `event` -- `pty` -- most `global` streaming or process-heavy routes -- anything requiring websocket upgrade handling -- anything that mixes many mutations and streaming in one file - -## First vertical slice - -The first slice for the package split is still the existing `question` `HttpApi` group. - -Why `question` first: - -- it already exists as an experimental `HttpApi` slice -- it already follows the desired contract and implementation split in one file -- it is already mounted through the current Hono host -- it is JSON-only -- it has low blast radius - -Use the first slice to prove: - -- package boundary -- contract and implementation split -- host mounting from `packages/opencode` -- merged OpenAPI output -- test ergonomics for future slices - -Do not broaden scope in the first slice. - -## Incremental migration order - -Use small PRs. - -Each PR should be easy to review, easy to revert, and should not mix extraction work with unrelated service refactors. - -### PR 1. Create `packages/server` - -Scope: - -- add the new workspace package -- add package manifest and tsconfig -- add empty `src/index.ts`, `src/definition/api.ts`, `src/definition/index.ts`, `src/api/index.ts`, `src/openapi.ts`, and supporting scaffolding - -Rules: - -- no production behavior changes -- no host server changes yet -- no imports from `packages/opencode` inside `packages/server` -- prefer `opentunnel`-style naming from the start: `definition` for contracts, `api` for implementations - -Done means: - -- `packages/server` typechecks -- the workspace can import it -- the package boundary is in place for follow-up PRs - -### PR 2. Move the experimental question contract - -Scope: - -- extract the pure `HttpApi` contract from `src/server/routes/instance/httpapi/question.ts` -- place it in `packages/server/src/definition/question.ts` -- aggregate it in `packages/server/src/definition/api.ts` -- generate OpenAPI in `packages/server/src/openapi.ts` - -Rules: - -- contract only in this PR -- no handler movement yet if that keeps the diff simpler -- keep operation ids and docs metadata stable - -Done means: - -- question contract lives in `packages/server` -- OpenAPI can be generated from contract alone -- no runtime behavior changes yet - -### PR 3. Move the experimental question handler factory - -Scope: - -- extract the question `HttpApiBuilder.group(...)` implementation into `packages/server/src/api/question.ts` -- expose it as a factory that accepts host-provided dependencies or wiring -- add a small Hono bridge in `packages/server/src/bridge/hono.ts` if needed - -Rules: - -- `packages/server` must still not import from `packages/opencode` -- handler code should stay thin and service-delegating -- do not redesign the question service itself in this PR - -Done means: - -- `packages/server` can produce the experimental question handler -- the package still stays cycle-free - -### PR 4. Mount `packages/server` question from `packages/opencode` - -Scope: - -- replace local experimental question route wiring in `packages/opencode` -- keep the same mount path: -- `/question` -- `/question/:requestID/reply` -- `/question/:requestID/reject` - -Rules: - -- no behavior change -- preserve existing docs path -- preserve current request and response shapes - -Done means: - -- existing question `HttpApi` test still passes -- runtime behavior is unchanged -- the current host server is now consuming `packages/server` - -### PR 5. Merge legacy and contract OpenAPI - -Scope: - -- keep `Server.openapi()` as the temporary spec entrypoint -- generate legacy Hono spec -- generate `packages/server` contract spec -- merge them into one document -- keep `cli/cmd/generate.ts` and `packages/sdk/js/script/build.ts` consuming one spec - -Rules: - -- fail loudly on duplicate `path + method` -- fail loudly on duplicate `operationId` -- do not silently overwrite one source with the other - -Done means: - -- one merged spec is produced -- migrated question paths can come from `packages/server` -- existing SDK generation path still works - -### PR 6. Add merged OpenAPI coverage - -Scope: - -- add one test for merged OpenAPI -- assert both a legacy Hono route and a migrated `HttpApi` route exist - -Rules: - -- test the merged document, not just the `packages/server` contract spec in isolation -- pick one stable legacy route and one stable migrated route - -Done means: - -- the merged-spec path is covered -- future route migrations have a guardrail - -### PR 7. Migrate `GET /provider/auth` - -Scope: - -- add `GET /provider/auth` as the next `HttpApi` slice in `packages/server` -- mount it in parallel from `packages/opencode` - -Why this route: - -- JSON-only -- simple service delegation -- small response shape -- already listed as the best next `provider` candidate in `http-api.md` - -Done means: - -- route works through the current host -- route appears in merged OpenAPI -- no semantic change to provider auth behavior - -### PR 8. Migrate `GET /config/providers` - -Scope: - -- add `GET /config/providers` as a `HttpApi` slice in `packages/server` -- mount it in parallel from `packages/opencode` - -Why this route: - -- JSON-only -- read-only -- low transport complexity -- already listed as the best next `config` candidate in `http-api.md` - -Done means: - -- route works unchanged -- route appears in merged OpenAPI - -### PR 9+. Migrate small read-only instance routes - -Candidate order: - -1. `GET /path` -2. `GET /vcs` -3. `GET /vcs/diff` -4. `GET /command` -5. `GET /agent` -6. `GET /skill` - -Rules: - -- one or two endpoints per PR -- prefer read-only routes first -- keep outer middleware unchanged -- keep business logic in the existing service layer - -Done means for each PR: - -- contract lives in `packages/server` -- handler lives in `packages/server` -- route is mounted from the current host -- route appears in merged OpenAPI -- behavior remains unchanged - -### Later PR. Move host ownership into `packages/server` - -Only start this after there is enough `packages/core` surface to depend on directly. - -Scope: - -- move server composition into `packages/server` -- add embeddable APIs such as `createServer(...)`, `listen(...)`, or `createApp(...)` -- move adapter selection and server startup out of `packages/opencode` - -Rules: - -- do not start this while `packages/server` still depends on `packages/opencode` -- do not mix this with route migration PRs - -Done means: - -- `packages/server` can be embedded in another Node app -- `packages/cli` can depend on `packages/server` -- host logic no longer lives in `packages/opencode` - -## PR sizing rule - -Every migration PR should satisfy all of these: - -- one route group or one to two endpoints -- no unrelated service refactor -- no auth redesign -- no middleware redesign -- OpenAPI updated -- at least one route test or spec test added or updated - -## Done means for a migrated route group - -A route group migration is complete only when: - -1. the `HttpApi` contract lives in `packages/server` -2. handler implementation lives in `packages/server` -3. the route is mounted from the current host in `packages/opencode` -4. the route appears in merged OpenAPI -5. request and response schemas are Effect Schema-first or clearly temporary placeholders -6. existing behavior remains unchanged -7. the route has straightforward test coverage - -## Validation expectations - -For package-split PRs, validate the smallest useful thing. - -Typical validation for the first waves: - -- `bun typecheck` in the touched package directory or directories -- the relevant server / route coverage for the migrated slice -- merged OpenAPI coverage if the PR touches spec generation - -Do not run tests from repo root. - -## Main risks - -### Package cycle - -This is the biggest risk. - -Bad state: - -- `packages/server` imports services or runtime from `packages/opencode` -- `packages/opencode` imports route definitions or handlers from `packages/server` - -Avoid by: - -- keeping phase-1 `packages/server` free of `packages/opencode` imports -- using factories and host-provided wiring instead of direct service imports - -### Spec drift - -During the transition there are two route-definition sources. - -Avoid by: - -- one merged spec -- collision checks -- explicit `operationId`s -- merged OpenAPI tests - -### Middleware mismatch - -Current auth, compression, CORS, and instance selection are Hono-centered. - -Avoid by: - -- leaving them where they are during the first wave -- not trying to solve `HttpApiMiddleware.Service` globally in the package-split PRs - -### Core lag - -`packages/core` will not be ready everywhere. - -Avoid by: - -- allowing small transport-local placeholder schemas where necessary -- keeping those placeholders clearly temporary -- not blocking the server extraction on full schema movement - -### Scope creep - -The first vertical slice is easy to overload. - -Avoid by: +- `packages/core` - shared domain services and schemas +- `packages/server` - HTTP contracts, handlers, OpenAPI generation, and an + embeddable server API +- `packages/cli` - TUI and CLI entrypoints +- `packages/sdk` - generated from the server OpenAPI spec +- `packages/plugin` - plugin authoring surface -- proving the package boundary first -- not mixing package creation, route migration, host redesign, and core extraction in the same change +## Extraction Rule -## Non-goals for the first wave +Do not create a package cycle. -- do not replace all Hono routes at once -- do not migrate SSE or websocket routes first -- do not redesign auth -- do not redesign instance lookup -- do not wait for full `packages/core` before starting `packages/server` -- do not change SDK generation to consume multiple specs +Until enough shared service code lives outside `packages/opencode`, a future +`packages/server` should either: -## Checklist +- own pure HttpApi contracts only, or +- accept host-provided services/layers/callbacks from `packages/opencode` -- [x] create `packages/server` -- [x] add package-level exports for contract and OpenAPI -- [ ] extract `question` contract into `packages/server` -- [ ] extract `question` handler factory into `packages/server` -- [ ] mount `question` from `packages/opencode` -- [ ] merge legacy and contract OpenAPI into one document -- [ ] add merged-spec coverage -- [ ] migrate `GET /provider/auth` -- [ ] migrate `GET /config/providers` -- [ ] migrate small read-only instance routes one or two at a time -- [ ] move host ownership into `packages/server` only after `packages/core` is ready enough -- [ ] split `packages/cli` after server and core boundaries are stable +It should not import `packages/opencode` services while `packages/opencode` +imports it to host routes. -## Rule of thumb +## Suggested PR Sequence -The fastest correct path is: +1. Keep shrinking OpenAPI compatibility shims in `httpapi/public.ts`. +2. Move stable domain schemas into shared packages only when they no longer + depend on opencode-local runtime modules. +3. Extract pure HttpApi contract modules into `packages/server` once the contract + can compile without importing `packages/opencode` implementation details. +4. Extract handler factories after their service dependencies can be supplied by + a host layer instead of imported directly. +5. Move server hosting last, after package ownership is clear. -1. establish `packages/server` as the contract-first boundary -2. keep `packages/opencode` as the temporary host -3. migrate a few safe JSON routes -4. keep one merged OpenAPI document -5. move actual host ownership only after `packages/core` can support it cleanly +## Non-Goals -If a proposed PR would make `packages/server` import from `packages/opencode`, stop and restructure the boundary first. +- Do not revive the old dual-backend migration shape. +- Do not split server hosting before service dependencies have a clean package + boundary. +- Do not switch SDK generation to a new package until generated output is known + to remain compatible. diff --git a/packages/opencode/specs/effect/todo.md b/packages/opencode/specs/effect/todo.md new file mode 100644 index 00000000000..68bcd0d7177 --- /dev/null +++ b/packages/opencode/specs/effect/todo.md @@ -0,0 +1,304 @@ +# Effect TODO + +Short roadmap for Effect cleanup in `packages/opencode`. + +Current patterns and examples live in [`guide.md`](./guide.md). Error +boundary migration details live in +[`error-boundaries-plan.md`](./error-boundaries-plan.md). Test migration rules live in +[`test/EFFECT_TEST_MIGRATION.md`](../../test/EFFECT_TEST_MIGRATION.md). +Older deep-dive notes in this directory may still be useful, but treat +this roadmap and the guide as the current entry points. + +This is a planning map, not a verified inventory. Before starting a task, +re-run a targeted `git grep` from current `dev` and update this file if +the inventory changed. + +## Priorities + +```text +P0 ERR + RENDER + HTTP + Make expected failures typed, render them well, and stop relying on + generic HTTP error guesswork. + +P1 TEST + Convert touched tests to the ideal Effect test patterns from the guide. + +P2 RF + Move mutable runtime flags into typed runtime/config services. + +P3 GLOBAL + Make global paths explicit and remove import-time side effects. + +P4 INST + BRIDGE + Remove ambient Instance coupling while keeping Promise/callback interop. + +P5 PROC + FS + Replace raw process/filesystem edges with typed Effect services. + +P6 OA + Shrink OpenAPI compatibility shims as source schemas improve. +``` + +## Work Paths + +- `ERR` Typed errors — replace legacy `NamedError.create(...)` and + `Effect.die(...)` for expected service failures with + `Schema.TaggedErrorClass` errors on the Effect error channel. + Shrinks: [`NamedError`](../../../core/src/util/error.ts) usage. +- `RENDER` User-visible error rendering — preserve structured typed-error + details at CLI, HTTP, and tool boundaries. + Shrinks: opaque `Error: Name` rendering. +- `HTTP` HTTP route cleanup — make route errors explicit instead of + relying on generic middleware to guess status/body from error names. + Shrinks: [`middleware/error.ts`](../../src/server/routes/instance/httpapi/middleware/error.ts) + and route-level compatibility shims. +- `TEST` Effect test migration — use `testEffect`, `it.live`, and + `it.instance` with explicit layers. + Shrinks: Promise-style tests, sleeps, mutable global test flags. +- `RF` RuntimeFlags / Flag deletion — move mutable + [`Flag`](../../../core/src/flag/flag.ts) reads into typed runtime/config + services. + Shrinks: [`flag.ts`](../../../core/src/flag/flag.ts), + [`test/fixture/flag.ts`](../../test/fixture/flag.ts). +- `GLOBAL` Global paths / import side effects — make global path state + explicit and testable instead of mutable module state. + Shrinks: [`global.ts`](../../../core/src/global.ts) import-time side + effects, mutable `Global.Path` overrides, and its `Flag` dependency. +- `INST` Instance shim — remove ambient `Instance` usage and old ALS + access patterns. + Shrinks: [`src/project/instance.ts`](../../src/project/instance.ts). +- `BRIDGE` Promise/callback interop — keep bridge helpers, but reduce + legacy ALS coupling. + Shrinks: [`src/effect/bridge.ts`](../../src/effect/bridge.ts) + dependency on [`project/instance.ts`](../../src/project/instance.ts). +- `PROC` AppProcess migration — prefer `AppProcess.Service` over raw + process wrappers. + Shrinks: direct spawn callsites and legacy process helpers. +- `FS` AppFileSystem migration — prefer `AppFileSystem.Service` over raw + filesystem APIs. + Shrinks: direct `fs` / `Bun.file` service callsites where inappropriate. +- `RT` Runtime/facade cleanup — remove service-local `makeRuntime` + facades when not intentional. + Shrinks: async facade exports around services and + [`run-service.ts`](../../src/effect/run-service.ts) usage. +- `OA` OpenAPI compatibility — tighten source schemas instead of + post-processing generated OpenAPI. + Shrinks: schema workaround blocks in + [`public.ts`](../../src/server/routes/instance/httpapi/public.ts). + +## P0: Errors, Rendering, And HTTP + +This should be the next big cleanup theme. The codebase is moving toward +typed Effect failures, but the user-facing boundaries still leak old +shapes and sometimes collapse rich errors into opaque strings. + +### Problems + +- Some expected service failures still use `NamedError.create(...)` or + collapse to `Effect.die(...)`. The storage/worktree/provider-auth + conversions are done; an inventory sweep is needed for the rest. +- HTTP error middleware still guesses status codes from error names — + some entries (e.g. storage `NotFound`, provider auth) can now be + removed, but the middleware overall has not shrunk. +- Route handlers and route groups do not consistently declare the public + error body they intend to expose. +- Repeated route error translations do not yet have a clear home: some + should stay inline, some deserve tiny shared mapper helpers. + +### Target Shape + +- Services define expected failures with `Schema.TaggedErrorClass`. +- Services export an `Error` union and include it in method return types. +- Expected failures stay on the Effect error channel. +- `Effect.die(...)` is reserved for defects: bugs, impossible states, + violated invariants, or final unknown-boundary fallbacks. +- Inside `Effect.gen` / `Effect.fn`, use `yield* new MyError(...)` for + direct expected failures. +- Domain services do not import HTTP status codes, `HttpApiError`, or + route-specific error schemas. +- HTTP route groups make their public error contracts obvious. +- Handlers map service errors to declared HTTP errors at the boundary. +- Shared mapper helpers are only for repeated translations, not a giant + central registry of every domain error. +- Generic HTTP middleware should shrink; it should not accumulate more + name-based domain knowledge. + +### Recently completed + +- [x] `RENDER-1` CLI tagged config error rendering (#27256, tests #27257). +- [x] `ERR-1` [`storage/storage.ts`](../../src/storage/storage.ts) typed + `NotFoundError` (#27265) and removal of the server defect fallback + (#27287). +- [x] `ERR-2` [`worktree/index.ts`](../../src/worktree/index.ts) typed + errors (#27296). +- [x] `ERR-3` [`provider/auth.ts`](../../src/provider/auth.ts) typed + validation/oauth errors (#27301). +- [x] `HTTP-1` Unknown-500 details no longer leaked (#27251); follow-up + to stop exposing named defects (#27471). +- [x] Session message reads typed and made effectful (#27269, #27275, + #27280, #27291). +- [x] Session HTTP error contracts tightened (#27308); busy-session + mapping centralized (#27375, #27473). +- [x] Provider init (#27484) and LSP init (#27494) errors typed. + +### First PR Candidates + +- [ ] `HTTP-2` Audit one route group for explicit error contracts and + decide which mappings stay inline vs. shared helper. +- [ ] `ERR-4` Sweep remaining `NamedError.create(...)` and + `Effect.die(...)` callsites for expected failures — re-run `git +grep` to build a current inventory. +- [ ] `RENDER-2` Audit CLI and TUI surfaces for any remaining opaque + `Error: Name` rendering of typed errors. + +## P1: Tests + +When touching tests, migrate them toward the ideal patterns in +[`test/EFFECT_TEST_MIGRATION.md`](../../test/EFFECT_TEST_MIGRATION.md): + +- Use `testEffect(...)` with explicit layers. +- Prefer `it.instance(...)` for service tests that need an instance. +- Prefer `it.live(...)` for real timers, filesystem mtimes, child + processes, git, locks, or other live integration behavior. +- Avoid sleeps; wait on real events or deterministic state transitions. +- Do not mutate `process.env` or mutable globals after layers are built. +- Use explicit layer variants, such as `RuntimeFlags.layer(...)`, for + behavior changes. + +## P2: RuntimeFlags / Flag Deletion + +Recently completed: + +- [x] Plugin/pure-mode flags moved to RuntimeFlags. +- [x] Tool visibility flags moved to RuntimeFlags. +- [x] Built-in websearch provider selection uses the same runtime flags as + tool visibility. +- [x] Removed global default-plugin disabling from test preload. +- [x] `RF-1` Scout reads routed through runtime flags (#27318). +- [x] `RF-2` Plan-mode prompt read routed through runtime flags (#27320). +- [x] `RF-3` Event-system reads routed through runtime flags (#27323). +- [x] `RF-4` Workspaces reads routed through runtime flags for session + (#27335), sync (#27336), and control-plane (#27337). +- [x] LLM client (#27368) and installation client (#27369) routed + through runtime flags. +- [x] TUI plugin runtime flags simplified (#27506). +- [x] Background-subagents flag moved to RuntimeFlags, then removed + (`refactor(task): use runtime flag for background subagents`, + `refactor(flags): remove background subagents flag`). + +Remaining cleanup: + +- [ ] Sweep lingering `Flag.*` reads — many CLI/TUI/config/observability + callsites still import [`flag.ts`](../../../core/src/flag/flag.ts). + Decide per-callsite whether to route through RuntimeFlags, accept + as legitimate env/config boundary, or migrate to typed `Config`. +- [ ] Delete [`test/fixture/flag.ts`](../../test/fixture/flag.ts) once + tests no longer mutate `Flag`. +- [ ] Delete [`flag.ts`](../../../core/src/flag/flag.ts) once no packages + import it. + +## P3: Global Paths + +[`global.ts`](../../../core/src/global.ts) is real connective tissue, not +just cosmetic ugliness. It currently mixes path calculation, import-time +directory creation, `Flock` setup, mutable exported `Path` state, and a +`Flag` dependency. + +Problems to reduce: + +- Importing the module creates directories. +- Tests override `Global.Path` by mutating exported module state. +- Most callers use `Global.Path` directly instead of the Effect service. +- `Global.make()` still reads mutable `Flag.KILO_CONFIG_DIR`. + +Next PR candidates: + +- [ ] Replace mutable `Global.Path` test overrides with explicit test + layers or scoped helpers. +- [ ] Move directory creation and `Flock` setup behind an explicit init + boundary where possible. +- [ ] Remove the `Flag` dependency from global path resolution. + +## P4: Instance And Bridge + +[`project/instance.ts`](../../src/project/instance.ts) is the deletion +target. [`effect/bridge.ts`](../../src/effect/bridge.ts) is not a near-term +deletion target; Promise/callback interop will continue to exist. + +Goal: + +- Keep a sanctioned bridge for Promise/callback boundaries. +- Reduce bridge dependence on legacy `Instance.restore` / `Instance.current`. +- Move callers toward `InstanceRef`, `WorkspaceRef`, `InstanceState`, or + explicit context where practical. +- Delete `project/instance.ts` only after ambient Instance coupling is gone. + +Important distinction: + +- `InstanceState.context`, `InstanceState.directory`, and + `InstanceState.workspaceID` are acceptable inside normal Effect service + code when `InstanceRef` / `WorkspaceRef` are provided by the runtime. +- The deletion blockers are the fallback and callback paths that rely on + ambient ALS: direct `Instance.*` reads, `InstanceState.bind(...)`, + `AppRuntime.runPromise(...)` re-entry from plain JS, and bridge restore + code that installs legacy ALS before invoking callbacks. + +Current bottom-up inventory from `dev`: + +- Direct `Instance.*` value readers: + [`tool/repo_overview.ts`](../../src/tool/repo_overview.ts), + [`control-plane/adapters/worktree.ts`](../../src/control-plane/adapters/worktree.ts), + [`cli/bootstrap.ts`](../../src/cli/bootstrap.ts). +- `InstanceState.bind(...)` callback boundaries: + [`file/watcher.ts`](../../src/file/watcher.ts) native watcher callback, + [`storage/db.ts`](../../src/storage/db.ts) transaction/effect callbacks, + [`session/llm.ts`](../../src/session/llm.ts) workflow approval callback. +- `AppRuntime.runPromise(...)` / re-entry from plain JS: + [`project/with-instance.ts`](../../src/project/with-instance.ts), + [`project/instance-runtime.ts`](../../src/project/instance-runtime.ts), + [`control-plane/adapters/worktree.ts`](../../src/control-plane/adapters/worktree.ts), + [`cli/effect-cmd.ts`](../../src/cli/effect-cmd.ts), plus global/non-instance + callsites such as CLI upgrade and ACP agent defaults. +- Intentional bridge users to classify, not delete blindly: + workspace adapters in [`control-plane/workspace.ts`](../../src/control-plane/workspace.ts), + MCP, command execution, plugins, pty lifecycle, bus scope cleanup, task + cancellation, and HTTP lifecycle reload/dispose paths. +- Core fallback layer to shrink last: + [`effect/run-service.ts`](../../src/effect/run-service.ts), + [`effect/bridge.ts`](../../src/effect/bridge.ts), and + [`effect/instance-state.ts`](../../src/effect/instance-state.ts). + +Recommended PR order: + +- [ ] `INST-1` Remove direct `Instance.*` value readers. Start with + `repo_overview`, `worktree` adapter, and `cli/bootstrap`; pass context + explicitly or obtain it from an Effect boundary. +- [ ] `INST-2` Move type-only `InstanceContext` imports from + [`project/instance.ts`](../../src/project/instance.ts) to + [`project/instance-context.ts`](../../src/project/instance-context.ts). +- [ ] `INST-3` Audit each `InstanceState.bind(...)` callback from the inside + out: list what the callback calls (`Bus.publish`, database effects, + permission/session services), then replace ambient capture with explicit + `InstanceRef` / `WorkspaceRef` provision or an `EffectBridge` call. +- [ ] `INST-4` Classify `AppRuntime.runPromise(...)` callsites as global, + instance-scoped with explicit refs, or bridge-required. Eliminate the + instance-scoped callsites that rely on `run-service.attach()` falling + back to `Instance.current`. +- [ ] `INST-5` After consumers are explicit, remove `Instance.current` fallback + from `InstanceState.context` and `run-service.attach()`. +- [ ] `INST-6` Move any remaining `restore` / `bind` compatibility helpers to + the boundary that still needs them, then delete + [`project/instance.ts`](../../src/project/instance.ts). + +## Lower Priority Tracks + +- `PROC` / `FS` — continue AppProcess and AppFileSystem migrations as + focused PRs when touching relevant files. +- `RT` — remove service-local runtime facades only when they are not an + intentional boundary. +- `OA` — shrink [`public.ts`](../../src/server/routes/instance/httpapi/public.ts) + by tightening source schemas one workaround at a time. +- `fetch` → `HttpClient` — migrate raw fetch callsites when the caller is + already effectful or being effectified. +- `Tools` — remaining tool cleanup is narrow: `webfetch` HTML extraction + and `shell` raw stream/promise edges. diff --git a/packages/opencode/specs/effect/tools.md b/packages/opencode/specs/effect/tools.md index 37a76e94879..b8c851aa3d9 100644 --- a/packages/opencode/specs/effect/tools.md +++ b/packages/opencode/specs/effect/tools.md @@ -67,11 +67,11 @@ Most exported tools are already on the intended Effect-native shape. The remaini Current spot cleanups worth tracking: -- [ ] `read.ts` — still bridges to Node stream / `readline` helpers and Promise-based binary detection +- [x] `read.ts` — streams through `AppFileSystem.Service.stream` with `Stream.splitLines`; the legacy Node stream / `readline` helper is gone - [ ] `bash.ts` — already uses Effect child-process primitives; only keep tracking shell-specific platform bridges and parser/loading details as they come up - [ ] `webfetch.ts` — already uses `HttpClient`; remaining work is limited to smaller boundary helpers like HTML text extraction - [ ] `file/ripgrep.ts` — adjacent to tool migration; still has raw fs/process usage that affects `grep.ts` and file-search routes -- [ ] `patch/index.ts` — adjacent to tool migration; still has raw fs usage behind patch application +- [x] `patch/index.ts` — apply path now returns `Effect` over `AppFileSystem.Service`; the parser and chunk replacer stay pure Notable items that are already effectively on the target path and do not need separate migration bullets right now: @@ -85,6 +85,4 @@ Notable items that are already effectively on the target path and do not need se Current raw fs users that still appear relevant here: -- `tool/read.ts` — `fs.createReadStream`, `readline` - `file/ripgrep.ts` — `fs/promises` -- `patch/index.ts` — `fs`, `fs/promises` diff --git a/packages/opencode/specs/openapi-translation-cleanup.md b/packages/opencode/specs/openapi-translation-cleanup.md index 255c09644f8..5be155d1b86 100644 --- a/packages/opencode/specs/openapi-translation-cleanup.md +++ b/packages/opencode/specs/openapi-translation-cleanup.md @@ -100,7 +100,7 @@ Verification: - Audit `PathParameterSchemas` and `pathParameterSchema()` in `public.ts`. - Check source schemas in files like `packages/opencode/src/session/schema.ts`, `packages/opencode/src/permission/schema.ts`, and pty schema definitions. -- Add or fix `ZodOverride` / OpenAPI-compatible annotations on branded ID schemas so generated path params include the same patterns without `public.ts` overrides. +- Add or fix OpenAPI-compatible annotations on branded ID schemas so generated path params include the same patterns without `public.ts` overrides. - Delete one path override only after generated OpenAPI is unchanged for that param. Concrete first targets: diff --git a/packages/opencode/specs/tui-plugins.md b/packages/opencode/specs/tui-plugins.md index e64cd4e3bd3..f5836426661 100644 --- a/packages/opencode/specs/tui-plugins.md +++ b/packages/opencode/specs/tui-plugins.md @@ -29,6 +29,16 @@ Example: "plugin": ["@acme/opencode-plugin@1.2.3", ["./plugins/demo.tsx", { "label": "demo" }]], "plugin_enabled": { "acme.demo": false + }, + "attention": { + "enabled": true, + "notifications": true, + "sound": true, + "volume": 0.4, + "sound_pack": "opencode.default", + "sounds": { + "error": "/Users/me/sounds/error.mp3" + } } } ``` @@ -45,6 +55,11 @@ Example: - Internal plugins can declare `enabled: false` to be registered but inactive by default; `plugin_enabled` and runtime KV can still enable them by id. - `plugin_enabled` is merged across config layers. - Runtime enable/disable state is also stored in KV under `plugin_enabled`; that KV state overrides config on startup. +- `attention.enabled` defaults to `false`; when `false`, it disables all `api.attention.notify(...)` delivery. +- `attention.notifications` and `attention.sound` independently control terminal-mediated desktop notifications and built-in sounds. +- `attention.volume` sets the default built-in sound volume from `0` to `1`. +- `attention.sound_pack` selects the initial semantic sound pack. Persisted runtime selection in KV can override it. +- `attention.sounds` overrides individual semantic sound slots such as `error`, `done`, or `subagent_done`. - `leader_timeout` is a top-level TUI setting. - `keybinds` is a flat object keyed by command id; values are key binding values (`false`, `"none"`, a key string/object, a binding object, or an array of key strings/objects/binding objects). - `keybinds.leader` sets the key used by `` shortcuts. @@ -212,6 +227,7 @@ That is what makes local config-scoped plugins able to import `@kilocode/plugin/ Top-level API groups exposed to `tui(api, options, meta)`: - `api.app.version` +- `api.attention.notify(input)` - `api.keys.formatSequence(parts)`, `formatBindings(bindings)` - `api.keymap` - `api.route.register(routes)` / `api.route.navigate(name, params?)` / `api.route.current` @@ -246,6 +262,24 @@ Top-level API groups exposed to `tui(api, options, meta)`: - `formatBindings(bindings)` formats binding lists and returns `undefined` when there is nothing to show. - For generic config-to-bindings helpers, import `createBindingLookup` from `@kilocode/plugin/tui`. +### Attention + +- `api.attention.notify({ title?, message, notification?, sound? })` requests user attention while keeping terminal focus, notifications, and audio owned by the host. +- `message` is required; `title` defaults to `"opencode"`; `notification` defaults to enabled with `when: "blurred"`; `sound` defaults to enabled with `when: "always"`. +- `when: "always"` requests delivery regardless of terminal focus state. +- `when: "focused"` only requests delivery after the terminal is known focused; `when: "blurred"` only requests delivery after the terminal is known blurred. +- Example: `notification: { when: "blurred" }, sound: { name: "question", when: "always" }` plays sound while focused but only triggers system notifications when blurred. +- Semantic sound names are `"default"`, `"question"`, `"permission"`, `"error"`, `"done"`, and `"subagent_done"`. +- `sound: true` plays the `"default"` sound; `sound: { name: "question" }` plays a named semantic sound. +- `sound: { volume }` overrides volume for that call; `sound: false` disables sound for that call; `notification: false` disables system notification for that call. +- `api.attention.soundboard.registerPack({ id, name?, sounds })` registers a sound pack and returns a disposer. Relative paths resolve from the plugin root and are cleaned up on plugin deactivation. +- `api.attention.soundboard.activate(id, { persist })` selects the active pack. `persist: true` writes the selected pack id to TUI KV state, not `tui.json`. +- `api.attention.soundboard.current()` and `list()` expose the active/registered packs for plugin UX. +- Config `attention.sounds` overrides active-pack sounds by slot. Failed loads fall back to the active pack and then `opencode.default`. +- The host strips ANSI/control characters and collapses newlines before sending text to the terminal notification API. +- Terminal and OS settings decide whether a requested notification is visibly displayed. +- Prefer privacy-safe messages such as `"A question needs your input"`; avoid full commands, paths, prompts, errors, secrets, or file contents unless the plugin intentionally exposes them. + ### Routes - Reserved route names: `home` and `session`. diff --git a/packages/opencode/specs/v2/notifications.md b/packages/opencode/specs/v2/notifications.md new file mode 100644 index 00000000000..96018f99443 --- /dev/null +++ b/packages/opencode/specs/v2/notifications.md @@ -0,0 +1,13 @@ +# TUI Notifications Default + +Problem: + +- v1 defaults `attention.enabled` to `false` +- users can opt in with `attention.enabled = true` +- v2 should make core TUI notifications a default behavior + +## v2 Target + +Flip `attention.enabled` to `true` by default in v2. + +Keep `attention.enabled = false` as the explicit opt-out. diff --git a/packages/opencode/src/acp/agent.ts b/packages/opencode/src/acp/agent.ts index 89444d1bc72..683bc7df554 100644 --- a/packages/opencode/src/acp/agent.ts +++ b/packages/opencode/src/acp/agent.ts @@ -50,7 +50,15 @@ import { ConfigMCP } from "@/config/mcp" import { Todo } from "@/session/todo" import { Result, Schema } from "effect" import { LoadAPIKeyError } from "ai" -import type { AssistantMessage, Event, KiloClient, SessionMessageResponse, ToolPart } from "@kilocode/sdk/v2" +import type { + AssistantMessage, + Event, + GlobalEvent, + KiloClient, + SessionMessageResponse, + SyncEventMessagePartUpdated, + ToolPart, +} from "@kilocode/sdk/v2" import { applyPatch } from "diff" import { InstallationVersion } from "@opencode-ai/core/installation/version" @@ -59,6 +67,12 @@ import { ShellID } from "@/tool/shell/id" type ModeOption = { id: string; name: string; description?: string } type ModelOption = { modelId: string; name: string } +type MessagePartUpdated = { + id: string + type: "message.part.updated" + properties: SyncEventMessagePartUpdated["data"] +} +type AgentEvent = Event | MessagePartUpdated const decodeTodos = Schema.decodeUnknownResult(Schema.fromJsonString(Schema.Array(Todo.Info))) const DEFAULT_VARIANT_VALUE = "default" @@ -185,14 +199,21 @@ export class Agent implements ACPAgent { if (this.eventAbort.signal.aborted) return const payload = event?.payload if (!payload) continue - await this.handleEvent(payload as Event).catch((error) => { + await this.handleEvent(payload).catch((error) => { log.error("failed to handle event", { error, type: payload.type }) }) } } } - private async handleEvent(event: Event) { + private async handleEvent(payload: GlobalEvent["payload"]) { + const event: AgentEvent | undefined = (() => { + if (payload.type !== "sync") return payload + if (payload.name !== "message.part.updated.1") return undefined + return { id: payload.id, type: "message.part.updated", properties: payload.data } + })() + if (!event) return + switch (event.type) { case "permission.asked": { const permission = event.properties @@ -1098,8 +1119,8 @@ export class Agent implements ACPAgent { const currentModeId = await (async () => { if (!availableModes.length) return undefined - const defaultAgentName = await AppRuntime.runPromise(AgentModule.Service.use((svc) => svc.defaultAgent())) - const resolvedModeId = availableModes.find((mode) => mode.name === defaultAgentName)?.id ?? availableModes[0].id + const defaultAgent = await AppRuntime.runPromise(AgentModule.Service.use((svc) => svc.defaultInfo())) + const resolvedModeId = availableModes.find((mode) => mode.name === defaultAgent.name)?.id ?? availableModes[0].id this.sessionManager.setMode(sessionId, resolvedModeId) return resolvedModeId })() @@ -1332,7 +1353,8 @@ export class Agent implements ACPAgent { if (!current) { this.sessionManager.setModel(session.id, model) } - const agent = session.modeId ?? (await AppRuntime.runPromise(AgentModule.Service.use((svc) => svc.defaultAgent()))) + const agent = + session.modeId ?? (await AppRuntime.runPromise(AgentModule.Service.use((svc) => svc.defaultInfo()))).name const parts: Array< | { type: "text"; text: string; synthetic?: boolean; ignored?: boolean } diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts index 067273e9038..aff15c1655d 100644 --- a/packages/opencode/src/agent/agent.ts +++ b/packages/opencode/src/agent/agent.ts @@ -1,5 +1,4 @@ import { Config } from "@/config/config" -import z from "zod" import { Provider } from "@/provider/provider" import { ModelID, ProviderID } from "../provider/schema" import { generateObject, streamObject, type ModelMessage } from "ai" @@ -17,16 +16,15 @@ import { Permission } from "@/permission" import { mergeDeep, pipe, sortBy, values } from "remeda" import { Global } from "@opencode-ai/core/global" import { KilocodePaths } from "@/kilocode/paths" // kilocode_change -import { Flag } from "@opencode-ai/core/flag/flag" import path from "path" import { Plugin } from "@/plugin" import { Skill } from "../skill" import { Effect, Context, Layer, Schema } from "effect" import { InstanceState } from "@/effect/instance-state" -import { zod } from "@opencode-ai/core/effect-zod" -import { withStatics, type DeepMutable } from "@opencode-ai/core/schema" -import { Reference } from "@/reference/reference" +import { type DeepMutable } from "@opencode-ai/core/schema" import * as KiloAgent from "@/kilocode/agent" // kilocode_change +import { RuntimeFlags } from "@/effect/runtime-flags" +import { Reference } from "@/reference/reference" // kilocode_change export const Info = Schema.Struct({ name: Schema.String, @@ -50,23 +48,31 @@ export const Info = Schema.Struct({ prompt: Schema.optional(Schema.String), options: Schema.Record(Schema.String, Schema.Unknown), steps: Schema.optional(Schema.Finite), -}) - .annotate({ identifier: "Agent" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "Agent" }) export type Info = DeepMutable> +const GeneratedAgent = Schema.Struct({ + identifier: Schema.String, + whenToUse: Schema.String, + systemPrompt: Schema.String, +}) + export interface Interface { readonly get: (agent: string) => Effect.Effect readonly list: () => Effect.Effect + readonly defaultInfo: () => Effect.Effect readonly defaultAgent: () => Effect.Effect readonly generate: (input: { description: string model?: { providerID: ProviderID; modelID: ModelID } - }) => Effect.Effect<{ - identifier: string - whenToUse: string - systemPrompt: string - }> + }) => Effect.Effect< + { + identifier: string + whenToUse: string + systemPrompt: string + }, + Provider.ModelNotFoundError + > } type State = Omit & { version: string } // kilocode_change @@ -81,6 +87,7 @@ export const layer = Layer.effect( const plugin = yield* Plugin.Service const skill = yield* Skill.Service const provider = yield* Provider.Service + const flags = yield* RuntimeFlags.Service const state = yield* InstanceState.make( Effect.fn("Agent.state")(function* (ctx) { @@ -207,7 +214,7 @@ export const layer = Layer.effect( mode: "subagent", native: true, }, - ...(Flag.KILO_EXPERIMENTAL_SCOUT + ...(flags.experimentalScout ? { scout: { name: "scout", @@ -219,7 +226,7 @@ export const layer = Layer.effect( glob: "allow", webfetch: "allow", websearch: "allow", - codesearch: "allow", + codesearch: "allow", // kilocode_change read: "allow", repo_clone: "allow", repo_overview: "allow", @@ -246,10 +253,10 @@ export const layer = Layer.effect( prompt: PROMPT_COMPACTION, permission: Permission.merge( defaults, + user, Permission.fromConfig({ "*": "deny", }), - user, ), options: {}, }, @@ -262,10 +269,10 @@ export const layer = Layer.effect( temperature: 0.5, permission: Permission.merge( defaults, + user, Permission.fromConfig({ "*": "deny", }), - user, ), prompt: PROMPT_TITLE, }, @@ -277,10 +284,10 @@ export const layer = Layer.effect( hidden: true, permission: Permission.merge( defaults, + user, Permission.fromConfig({ "*": "deny", }), - user, ), prompt: PROMPT_SUMMARY, }, @@ -357,7 +364,7 @@ export const layer = Layer.effect( return `Invalid Scout reference for repository ${reference.repository}` } - if (Flag.KILO_EXPERIMENTAL_SCOUT) { + if (flags.experimentalScout) { const resolvedReferences = Reference.resolveAll({ references: cfg.reference ?? {}, directory: ctx.directory, @@ -423,7 +430,7 @@ export const layer = Layer.effect( ) }) - const defaultAgent = Effect.fnUntraced(function* () { + const defaultInfo = Effect.fnUntraced(function* () { const c = yield* config.get() if (c.default_agent) { // kilocode_change start @@ -433,21 +440,26 @@ export const layer = Layer.effect( if (!agent) throw new Error(`default agent "${c.default_agent}" not found`) if (agent.mode === "subagent") throw new Error(`default agent "${c.default_agent}" is a subagent`) if (agent.hidden === true) throw new Error(`default agent "${c.default_agent}" is hidden`) - return agent.name + return agent } // kilocode_change start - prefer "code" as default agent (key order changes after rename from "build") const code = agents.code - if (code && code.mode !== "subagent" && code.hidden !== true) return code.name + if (code && code.mode !== "subagent" && code.hidden !== true) return code // kilocode_change end const visible = Object.values(agents).find((a) => a.mode !== "subagent" && a.hidden !== true) if (!visible) throw new Error("no primary visible agent found") - return visible.name + return visible + }) + + const defaultAgent = Effect.fnUntraced(function* () { + return (yield* defaultInfo()).name }) return { version: KiloAgent.cacheKey(cfg), // kilocode_change get, list, + defaultInfo, defaultAgent, } satisfies State }), @@ -470,6 +482,9 @@ export const layer = Layer.effect( list: Effect.fn("Agent.list")(function* () { return yield* current((s) => s.list()) // kilocode_change }), + defaultInfo: Effect.fn("Agent.defaultInfo")(function* () { + return yield* current((s) => s.defaultInfo()) // kilocode_change + }), defaultAgent: Effect.fn("Agent.defaultAgent")(function* () { return yield* current((s) => s.defaultAgent()) // kilocode_change }), @@ -510,11 +525,10 @@ export const layer = Layer.effect( }, ], model: language, - schema: z.object({ - identifier: z.string(), - whenToUse: z.string(), - systemPrompt: z.string(), - }), + schema: Object.assign( + Schema.toStandardSchemaV1(GeneratedAgent), + Schema.toStandardJSONSchemaV1(GeneratedAgent), + ), } satisfies Parameters[0] if (isOpenaiOauth) { @@ -546,6 +560,7 @@ export const defaultLayer = layer.pipe( Layer.provide(Auth.defaultLayer), Layer.provide(Config.defaultLayer), Layer.provide(Skill.defaultLayer), + Layer.provide(RuntimeFlags.defaultLayer), ) export * as Agent from "./agent" diff --git a/packages/opencode/src/agent/subagent-permissions.ts b/packages/opencode/src/agent/subagent-permissions.ts index 1174ec31ade..051f42e37bb 100644 --- a/packages/opencode/src/agent/subagent-permissions.ts +++ b/packages/opencode/src/agent/subagent-permissions.ts @@ -5,10 +5,10 @@ import type { Agent } from "./agent" * Build the `permission` ruleset for a subagent's session when it's spawned * via the task tool. Combines: * - * 1. The parent **agent's** deny rules — Plan Mode and other agent-level - * restrictions live on the agent ruleset, not on the session, so a + * 1. The parent **agent's** edit-class deny rules — Plan Mode's file-edit + * restriction lives on the agent ruleset, not on the session, so a * subagent that only inherited the parent SESSION's permission would - * silently bypass them. (#26514) + * silently bypass it. (#26514) * 2. The parent **session's** deny rules and external_directory rules — * same forwarding the original code already did. * 3. Default `todowrite` and `task` denies if the subagent's own ruleset @@ -21,7 +21,8 @@ export function deriveSubagentSessionPermission(input: { }): Permission.Ruleset { const canTask = input.subagent.permission.some((rule) => rule.permission === "task") const canTodo = input.subagent.permission.some((rule) => rule.permission === "todowrite") - const parentAgentDenies = input.parentAgent?.permission.filter((rule) => rule.action === "deny") ?? [] + const parentAgentDenies = + input.parentAgent?.permission.filter((rule) => rule.action === "deny" && rule.permission === "edit") ?? [] return [ ...parentAgentDenies, ...input.parentSessionPermission.filter( diff --git a/packages/opencode/src/audio.d.ts b/packages/opencode/src/audio.d.ts index c7c947450dc..7b99d097a33 100644 --- a/packages/opencode/src/audio.d.ts +++ b/packages/opencode/src/audio.d.ts @@ -3,6 +3,11 @@ declare module "*.wav" { export default file } +declare module "*.mp3" { + const file: string + export default file +} + declare module "*.wasm" { const file: string export default file diff --git a/packages/opencode/src/auth/index.ts b/packages/opencode/src/auth/index.ts index 55803660ca1..9779f7202cf 100644 --- a/packages/opencode/src/auth/index.ts +++ b/packages/opencode/src/auth/index.ts @@ -1,6 +1,5 @@ import path from "path" import { Effect, Layer, Record, Result, Schema, Context } from "effect" -import { zod } from "@opencode-ai/core/effect-zod" import { NonNegativeInt } from "@opencode-ai/core/schema" import { Global } from "@opencode-ai/core/global" import { AppFileSystem } from "@opencode-ai/core/filesystem" @@ -33,9 +32,8 @@ export class WellKnown extends Schema.Class("WellKnownAuth")({ token: Schema.String, }) {} -const _Info = Schema.Union([Oauth, Api, WellKnown]).annotate({ discriminator: "type", identifier: "Auth" }) -export const Info = Object.assign(_Info, { zod: zod(_Info) }) -export type Info = Schema.Schema.Type +export const Info = Schema.Union([Oauth, Api, WellKnown]).annotate({ discriminator: "type", identifier: "Auth" }) +export type Info = Schema.Schema.Type export class AuthError extends Schema.TaggedErrorClass()("AuthError", { message: Schema.String, diff --git a/packages/opencode/src/background/job.ts b/packages/opencode/src/background/job.ts new file mode 100644 index 00000000000..3ea228f048c --- /dev/null +++ b/packages/opencode/src/background/job.ts @@ -0,0 +1,200 @@ +import { InstanceState } from "@/effect/instance-state" +import { Identifier } from "@/id/id" +import { Cause, Clock, Context, Deferred, Effect, Fiber, Layer, Scope, SynchronizedRef } from "effect" + +export type Status = "running" | "completed" | "error" | "cancelled" + +export type Info = { + id: string + type: string + title?: string + status: Status + started_at: number + completed_at?: number + output?: string + error?: string + metadata?: Record +} + +type Active = { + info: Info + done: Deferred.Deferred + fiber?: Fiber.Fiber +} + +type State = { + jobs: SynchronizedRef.SynchronizedRef> + scope: Scope.Scope +} + +type FinishResult = { + info?: Info + done?: Deferred.Deferred +} + +export type StartInput = { + id?: string + type: string + title?: string + metadata?: Record + run: Effect.Effect +} + +export type WaitInput = { + id: string + timeout?: number +} + +export type WaitResult = { + info?: Info + timedOut: boolean +} + +export interface Interface { + readonly list: () => Effect.Effect + readonly get: (id: string) => Effect.Effect + readonly start: (input: StartInput) => Effect.Effect + readonly wait: (input: WaitInput) => Effect.Effect + readonly cancel: (id: string) => Effect.Effect +} + +export class Service extends Context.Service()("@opencode/BackgroundJob") {} + +function snapshot(job: Active): Info { + return { + ...job.info, + ...(job.info.metadata ? { metadata: { ...job.info.metadata } } : {}), + } +} + +function errorText(error: unknown) { + if (error instanceof Error) return error.message + return String(error) +} + +export const layer = Layer.effect( + Service, + Effect.gen(function* () { + const state = yield* InstanceState.make( + Effect.fn("BackgroundJob.state")(function* () { + return { + jobs: yield* SynchronizedRef.make(new Map()), + scope: yield* Scope.Scope, + } + }), + ) + + const finish = Effect.fn("BackgroundJob.finish")(function* ( + id: string, + status: Exclude, + data?: { output?: string; error?: string }, + ) { + const completed_at = yield* Clock.currentTimeMillis + const result = yield* SynchronizedRef.modify( + (yield* InstanceState.get(state)).jobs, + (jobs): readonly [FinishResult, Map] => { + const job = jobs.get(id) + if (!job) return [{}, jobs] + if (job.info.status !== "running") return [{ info: snapshot(job) }, jobs] + const next = { + ...job, + fiber: undefined, + info: { + ...job.info, + status, + completed_at, + ...(data?.output !== undefined ? { output: data.output } : {}), + ...(data?.error !== undefined ? { error: data.error } : {}), + }, + } + return [{ info: snapshot(next), done: job.done }, new Map(jobs).set(id, next)] + }, + ) + if (result.info && result.done) yield* Deferred.succeed(result.done, result.info).pipe(Effect.ignore) + return result.info + }) + + const list: Interface["list"] = Effect.fn("BackgroundJob.list")(function* () { + return Array.from((yield* SynchronizedRef.get((yield* InstanceState.get(state)).jobs)).values()) + .map(snapshot) + .toSorted((a, b) => a.started_at - b.started_at) + }) + + const get: Interface["get"] = Effect.fn("BackgroundJob.get")(function* (id) { + const job = (yield* SynchronizedRef.get((yield* InstanceState.get(state)).jobs)).get(id) + if (!job) return + return snapshot(job) + }) + + const start: Interface["start"] = Effect.fn("BackgroundJob.start")(function* (input) { + return yield* Effect.uninterruptibleMask((restore) => + Effect.gen(function* () { + const s = yield* InstanceState.get(state) + const id = input.id ?? Identifier.ascending("job") + const started_at = yield* Clock.currentTimeMillis + const done = yield* Deferred.make() + return yield* SynchronizedRef.modifyEffect( + s.jobs, + Effect.fnUntraced(function* (jobs) { + const existing = jobs.get(id) + if (existing?.info.status === "running") return [snapshot(existing), jobs] as const + const fiber = yield* restore(input.run).pipe( + Effect.matchCauseEffect({ + onSuccess: (output) => finish(id, "completed", { output }), + onFailure: (cause) => + finish(id, Cause.hasInterruptsOnly(cause) ? "cancelled" : "error", { + error: errorText(Cause.squash(cause)), + }), + }), + Effect.asVoid, + Effect.forkIn(s.scope, { startImmediately: true }), + ) + const job = { + info: { + id, + type: input.type, + title: input.title, + status: "running" as const, + started_at, + metadata: input.metadata, + }, + done, + fiber, + } + return [snapshot(job), new Map(jobs).set(id, job)] as const + }), + ) + }), + ) + }) + + const wait: Interface["wait"] = Effect.fn("BackgroundJob.wait")(function* (input) { + const job = (yield* SynchronizedRef.get((yield* InstanceState.get(state)).jobs)).get(input.id) + if (!job) return { timedOut: false } + if (job.info.status !== "running") return { info: snapshot(job), timedOut: false } + if (input.timeout === undefined) return { info: yield* Deferred.await(job.done), timedOut: false } + if (input.timeout <= 0) return { info: snapshot(job), timedOut: true } + const info = yield* Deferred.await(job.done).pipe(Effect.timeoutOption(input.timeout)) + if (info._tag === "Some") return { info: info.value, timedOut: false } + return { info: snapshot(job), timedOut: true } + }) + + const cancel: Interface["cancel"] = Effect.fn("BackgroundJob.cancel")(function* (id) { + const job = (yield* SynchronizedRef.get((yield* InstanceState.get(state)).jobs)).get(id) + if (!job) return + if (job.info.status !== "running") return snapshot(job) + if (job.fiber) { + yield* Fiber.interrupt(job.fiber).pipe(Effect.ignore) + yield* Fiber.await(job.fiber).pipe(Effect.ignore) + } + const info = yield* finish(id, "cancelled") + return info + }) + + return Service.of({ list, get, start, wait, cancel }) + }), +) + +export const defaultLayer = layer + +export * as BackgroundJob from "./job" diff --git a/packages/opencode/src/cli/cmd/db.ts b/packages/opencode/src/cli/cmd/db.ts index 90a0adb6f3d..473dfd974d5 100644 --- a/packages/opencode/src/cli/cmd/db.ts +++ b/packages/opencode/src/cli/cmd/db.ts @@ -28,7 +28,7 @@ const QueryCommand = cmd({ handler: async (args: { query?: string; format: string }) => { const query = args.query as string | undefined if (query) { - const db = new BunDatabase(Database.Path, { readonly: true }) + const db = new BunDatabase(Database.getPath(), { readonly: true }) try { const result = db.query(query).all() as Record[] if (args.format === "json") { @@ -47,7 +47,7 @@ const QueryCommand = cmd({ db.close() return } - const child = spawn("sqlite3", [Database.Path], { + const child = spawn("sqlite3", [Database.getPath()], { stdio: "inherit", windowsHide: true, // kilocode_change - prevent CMD window flash on Windows }) @@ -59,7 +59,7 @@ const PathCommand = cmd({ command: "path", describe: "print the database path", handler: () => { - console.log(Database.Path) + console.log(Database.getPath()) }, }) @@ -67,7 +67,7 @@ const MigrateCommand = cmd({ command: "migrate", describe: "migrate JSON data to SQLite (merges with existing data)", handler: async () => { - const sqlite = new BunDatabase(Database.Path) + const sqlite = new BunDatabase(Database.getPath()) const tty = process.stderr.isTTY const width = 36 const orange = "\x1b[38;5;214m" diff --git a/packages/opencode/src/cli/cmd/debug/index.ts b/packages/opencode/src/cli/cmd/debug/index.ts index 91c647cc7ee..d160a42d087 100644 --- a/packages/opencode/src/cli/cmd/debug/index.ts +++ b/packages/opencode/src/cli/cmd/debug/index.ts @@ -16,6 +16,7 @@ import { SkillCommand } from "./skill" import { SnapshotCommand } from "./snapshot" import { AgentCommand } from "./agent" import { StartupCommand } from "./startup" +import { V2Command } from "./v2" export const DebugCommand = cmd({ command: "debug", @@ -31,6 +32,7 @@ export const DebugCommand = cmd({ .command(SnapshotCommand) .command(StartupCommand) .command(AgentCommand) + .command(V2Command) .command(InfoCommand) .command(PathsCommand) .command(WaitCommand) diff --git a/packages/opencode/src/cli/cmd/debug/v2.ts b/packages/opencode/src/cli/cmd/debug/v2.ts new file mode 100644 index 00000000000..507f82cdf53 --- /dev/null +++ b/packages/opencode/src/cli/cmd/debug/v2.ts @@ -0,0 +1,46 @@ +import { EOL } from "os" +import { Effect, Layer, Option } from "effect" +import { Catalog } from "@opencode-ai/core/catalog" +import { InstanceServiceMap } from "@opencode-ai/core/instance-layer" +import { PluginBoot } from "@opencode-ai/core/plugin/boot" +import { effectCmd } from "../../effect-cmd" + +const Runtime = Layer.mergeAll(InstanceServiceMap.layer) + +export const V2Command = effectCmd({ + command: "v2", + describe: "debug v2 catalog and built-in plugins", + instance: false, + handler: Effect.fn("Cli.debug.v2")( + function* () { + yield* PluginBoot.Service.use((service) => service.wait()) + const catalog = yield* Catalog.Service + const providers = (yield* catalog.provider.available()).sort((a, b) => a.id.localeCompare(b.id)) + const all = (yield* catalog.provider.all()).sort((a, b) => a.id.localeCompare(b.id)) + const result = { + providers, + default: catalog.model + .default() + .pipe(Effect.map(Option.map((item) => item.id)), Effect.map(Option.getOrUndefined)), + small: Object.fromEntries( + yield* Effect.all( + all.map((provider) => + Effect.map( + catalog.model.small(provider.id), + (model) => [provider.id, Option.getOrUndefined(Option.map(model, (item) => item.id))] as const, + ), + ), + { concurrency: "unbounded" }, + ), + ), + } + process.stdout.write(JSON.stringify(result, null, 2) + EOL) + }, + Effect.provide( + InstanceServiceMap.get({ + directory: process.cwd(), + }), + ), + Effect.provide(Runtime), + ), +}) diff --git a/packages/opencode/src/cli/cmd/github.ts b/packages/opencode/src/cli/cmd/github.ts index 3e79455d987..2cdffebc52c 100644 --- a/packages/opencode/src/cli/cmd/github.ts +++ b/packages/opencode/src/cli/cmd/github.ts @@ -19,7 +19,7 @@ import type { import { UI } from "../ui" import { cmd } from "./cmd" import { effectCmd } from "../effect-cmd" -import { ModelsDev } from "@/provider/models" +import { ModelsDev } from "@opencode-ai/core/models" import { InstanceRef } from "@/effect/instance-ref" import { SessionShare } from "@/share/session" import { Session } from "@/session/session" diff --git a/packages/opencode/src/cli/cmd/import.ts b/packages/opencode/src/cli/cmd/import.ts index 03e918e75c4..83de9efef51 100644 --- a/packages/opencode/src/cli/cmd/import.ts +++ b/packages/opencode/src/cli/cmd/import.ts @@ -6,7 +6,7 @@ import { Database } from "@/storage/db" import { SessionTable, MessageTable, PartTable } from "../../session/session.sql" import { InstanceRef } from "@/effect/instance-ref" import { EOL } from "os" -import { Filesystem } from "@/util/filesystem" +import { AppFileSystem } from "@opencode-ai/core/filesystem" import { Effect, Schema } from "effect" import * as Log from "@opencode-ai/core/util/log" // kilocode_change @@ -136,6 +136,8 @@ export const ImportCommand = effectCmd({ }) const runImport = Effect.fn("Cli.import.body")(function* (file: string, projectID: string) { + const fs = yield* AppFileSystem.Service + let exportData: ExportData | undefined const isUrl = file.startsWith("http://") || file.startsWith("https://") @@ -178,9 +180,9 @@ const runImport = Effect.fn("Cli.import.body")(function* (file: string, projectI exportData = data // kilocode_change end } else { - exportData = yield* Effect.promise(() => - Filesystem.readJson>(file).catch(() => undefined), - ) + exportData = (yield* fs.readJson(file).pipe(Effect.orElseSucceed(() => undefined))) as + | NonNullable + | undefined if (!exportData) { process.stdout.write(`File not found: ${file}`) process.stdout.write(EOL) diff --git a/packages/opencode/src/cli/cmd/models.ts b/packages/opencode/src/cli/cmd/models.ts index 556e63354fc..f4fa3e8bca2 100644 --- a/packages/opencode/src/cli/cmd/models.ts +++ b/packages/opencode/src/cli/cmd/models.ts @@ -2,7 +2,7 @@ import { EOL } from "os" import { Effect } from "effect" import { Provider } from "@/provider/provider" import { ProviderID } from "../../provider/schema" -import { ModelsDev } from "@/provider/models" +import { ModelsDev } from "@opencode-ai/core/models" import { effectCmd, fail } from "../effect-cmd" import { UI } from "../ui" diff --git a/packages/opencode/src/cli/cmd/prompt-display.ts b/packages/opencode/src/cli/cmd/prompt-display.ts new file mode 100644 index 00000000000..7ec4bc0af55 --- /dev/null +++ b/packages/opencode/src/cli/cmd/prompt-display.ts @@ -0,0 +1,39 @@ +const graphemes = new Intl.Segmenter(undefined, { granularity: "grapheme" }) + +function displayOffsetIndex(value: string, offset: number) { + if (offset <= 0) return 0 + + let width = 0 + for (const part of graphemes.segment(value)) { + const next = width + Bun.stringWidth(part.segment) + if (next > offset) return part.index + width = next + } + + return value.length +} + +export function displaySlice(value: string, start = 0, end = Bun.stringWidth(value)) { + return value.slice(displayOffsetIndex(value, start), displayOffsetIndex(value, end)) +} + +export function displayCharAt(value: string, offset: number) { + let width = 0 + for (const part of graphemes.segment(value)) { + const next = width + Bun.stringWidth(part.segment) + if (offset === width || offset < next) return part.segment + width = next + } +} + +export function mentionTriggerIndex(value: string, offset = Bun.stringWidth(value)) { + const text = displaySlice(value, 0, offset) + const index = text.lastIndexOf("@") + if (index === -1) return + + const before = index === 0 ? undefined : text[index - 1] + const query = text.slice(index) + if ((before === undefined || /\s/.test(before)) && !/\s/.test(query)) { + return Bun.stringWidth(text.slice(0, index)) + } +} diff --git a/packages/opencode/src/cli/cmd/providers.ts b/packages/opencode/src/cli/cmd/providers.ts index 2ce81da0677..00341e19122 100644 --- a/packages/opencode/src/cli/cmd/providers.ts +++ b/packages/opencode/src/cli/cmd/providers.ts @@ -3,7 +3,7 @@ import { cmd } from "./cmd" import { CliError, effectCmd, fail } from "../effect-cmd" import { UI } from "../ui" import * as Prompt from "../effect/prompt" -import { ModelsDev } from "@/provider/models" +import { ModelsDev } from "@opencode-ai/core/models" import { map, pipe, sortBy, values } from "remeda" import path from "path" @@ -124,6 +124,7 @@ const handlePluginAuth = Effect.fn("Cli.providers.pluginAuth")(function* ( yield* put(saveProvider, { type: "api", key: result.key, + ...(result.metadata ? { metadata: result.metadata } : {}), }) } yield* spinner.stop("Login successful") @@ -156,6 +157,7 @@ const handlePluginAuth = Effect.fn("Cli.providers.pluginAuth")(function* ( yield* put(saveProvider, { type: "api", key: result.key, + ...(result.metadata ? { metadata: result.metadata } : {}), }) } yield* Prompt.log.success("Login successful") @@ -191,10 +193,11 @@ const handlePluginAuth = Effect.fn("Cli.providers.pluginAuth")(function* ( } if (result.type === "success") { const saveProvider = result.provider ?? provider + const merged = { ...(metadata.metadata ?? {}), ...(result.metadata ?? {}) } yield* put(saveProvider, { type: "api", key: result.key ?? apiKey, - ...metadata, + ...(Object.keys(merged).length ? { metadata: merged } : {}), }) yield* Prompt.log.success("Login successful") } diff --git a/packages/opencode/src/cli/cmd/run.ts b/packages/opencode/src/cli/cmd/run.ts index aa19ea27f83..eb744e73625 100644 --- a/packages/opencode/src/cli/cmd/run.ts +++ b/packages/opencode/src/cli/cmd/run.ts @@ -19,7 +19,6 @@ import { pathToFileURL } from "url" import { Effect } from "effect" import { UI } from "../ui" import { effectCmd } from "../effect-cmd" -import { Flag } from "@opencode-ai/core/flag/flag" import { ServerAuth } from "@/server/auth" import { buildRunMessage } from "@/kilocode/cli/cmd/run-message" // kilocode_change import { EOL } from "os" @@ -27,7 +26,10 @@ import { Filesystem } from "@/util/filesystem" import { createKiloClient, type KiloClient, type ToolPart } from "@kilocode/sdk/v2" import { Agent } from "@/agent/agent" import { Permission } from "@/permission" +import { RuntimeFlags } from "@/effect/runtime-flags" +import { FormatError, FormatUnknownError } from "../error" import { INTERACTIVE_INPUT_ERROR, resolveInteractiveStdin } from "./run/runtime.stdin" +import { event as normalizeEvent } from "./run/event" import { importCloudSession, validateCloudFork } from "@/kilocode/cloud-session" // kilocode_change import { KiloRunAuto } from "@/kilocode/cli/run-auto" // kilocode_change import { KiloRunDaemon } from "@/kilocode/cli/cmd/run" // kilocode_change @@ -88,6 +90,10 @@ function block(info: Inline, output?: string) { UI.empty() } +function formatRunError(error: unknown) { + return FormatError(error) ?? FormatUnknownError(error) +} + async function tool(part: ToolPart) { try { const { toolInlineInfo } = await import("./run/tool") @@ -249,6 +255,7 @@ export const RunCommand = effectCmd({ }), handler: Effect.fn("Cli.run")(function* (args) { const agentSvc = yield* Agent.Service + const flags = yield* RuntimeFlags.Service yield* Effect.promise(async () => { const rawMessage = [...args.message, ...(args["--"] || [])].join(" ") const thinking = args.interactive ? (args.thinking ?? true) : (args.thinking ?? false) @@ -498,7 +505,7 @@ export const RunCommand = effectCmd({ async function share(sdk: KiloClient, sessionID: string) { const cfg = await sdk.config.get() if (!cfg.data) return - if (cfg.data.share !== "auto" && !Flag.KILO_AUTO_SHARE && !args.share) return + if (cfg.data.share !== "auto" && !flags.autoShare && !args.share) return const res = await sdk.session.share({ sessionID }).catch((error) => { if (error instanceof Error && error.message.includes("disabled")) { UI.println(UI.Style.TEXT_DANGER_BOLD + "! " + error.message) @@ -662,7 +669,10 @@ export const RunCommand = effectCmd({ let retries = 0 // kilocode_change let error: string | undefined - for await (const event of events.stream) { + for await (const payload of events.stream) { + const event = normalizeEvent(payload) + if (!event) continue + if ( event.type === "message.updated" && event.properties.sessionID === sessionID && @@ -822,6 +832,7 @@ export const RunCommand = effectCmd({ } // kilocode_change end } + return error } const cwd = args.attach ? (directory ?? sess.directory ?? (await current(sdk))) : (directory ?? root) const client = args.attach ? attachSDK(cwd) : sdk @@ -839,7 +850,7 @@ export const RunCommand = effectCmd({ }) if (args.command) { - await client.session.command({ + const result = await client.session.command({ sessionID, agent, model: args.model, @@ -847,17 +858,25 @@ export const RunCommand = effectCmd({ arguments: message, variant: args.variant, }) + if (result.error) { + if (!emit("error", { error: result.error })) UI.error(formatRunError(result.error)) + process.exitCode = 1 + } return } const model = pick(args.model) - await client.session.prompt({ + const result = await client.session.prompt({ sessionID, agent, model, variant: args.variant, parts: [...files, { type: "text", text: message }], }) + if (result.error) { + if (!emit("error", { error: result.error })) UI.error(formatRunError(result.error)) + process.exitCode = 1 + } return } diff --git a/packages/opencode/src/cli/cmd/run/demo.ts b/packages/opencode/src/cli/cmd/run/demo.ts index a42f00f87d1..3afb4d58124 100644 --- a/packages/opencode/src/cli/cmd/run/demo.ts +++ b/packages/opencode/src/cli/cmd/run/demo.ts @@ -15,7 +15,8 @@ // Demo mode also handles permission and question replies locally, completing // or failing the synthetic tool parts as appropriate. import path from "path" -import type { Event, ToolPart } from "@kilocode/sdk/v2" +import type { ToolPart } from "@kilocode/sdk/v2" +import type { Event } from "./event" import { createSessionData, reduceSessionData, type SessionData } from "./session-data" import { writeSessionOutput } from "./stream" import type { FooterApi, PermissionReply, QuestionReject, QuestionReply, RunPrompt, StreamCommit } from "./types" diff --git a/packages/opencode/src/cli/cmd/run/event.ts b/packages/opencode/src/cli/cmd/run/event.ts new file mode 100644 index 00000000000..7ab90ef6ffd --- /dev/null +++ b/packages/opencode/src/cli/cmd/run/event.ts @@ -0,0 +1,51 @@ +import type { + Event as SDKEvent, + GlobalEvent, + SyncEventMessagePartRemoved, + SyncEventMessagePartUpdated, + SyncEventMessageRemoved, + SyncEventMessageUpdated, +} from "@kilocode/sdk/v2" + +type MessageUpdated = { + id: string + type: "message.updated" + properties: SyncEventMessageUpdated["data"] +} + +type MessageRemoved = { + id: string + type: "message.removed" + properties: SyncEventMessageRemoved["data"] +} + +type MessagePartUpdated = { + id: string + type: "message.part.updated" + properties: SyncEventMessagePartUpdated["data"] +} + +type MessagePartRemoved = { + id: string + type: "message.part.removed" + properties: SyncEventMessagePartRemoved["data"] +} + +export type Event = SDKEvent | MessageUpdated | MessageRemoved | MessagePartUpdated | MessagePartRemoved + +export function event(payload: GlobalEvent["payload"]): Event | undefined { + if (payload.type !== "sync") return payload + + switch (payload.name) { + case "message.updated.1": + return { id: payload.id, type: "message.updated", properties: payload.data } + case "message.removed.1": + return { id: payload.id, type: "message.removed", properties: payload.data } + case "message.part.updated.1": + return { id: payload.id, type: "message.part.updated", properties: payload.data } + case "message.part.removed.1": + return { id: payload.id, type: "message.part.removed", properties: payload.data } + default: + return undefined + } +} diff --git a/packages/opencode/src/cli/cmd/run/footer.prompt.tsx b/packages/opencode/src/cli/cmd/run/footer.prompt.tsx index ad0e1508e6e..fcde863d014 100644 --- a/packages/opencode/src/cli/cmd/run/footer.prompt.tsx +++ b/packages/opencode/src/cli/cmd/run/footer.prompt.tsx @@ -15,7 +15,10 @@ import { slashDisplay, slashMatches } from "@/kilocode/cli/cmd/command-display" import * as Locale from "@/util/locale" import { createPromptHistory, + displayCharAt, + displaySlice, isExitCommand, + mentionTriggerIndex, isNewCommand, movePromptHistory, promptCycle, @@ -170,7 +173,8 @@ function parseSlashCommand(text: string, commands: RunCommand[] | undefined) { return { type: "pending" as const } } - if (!commands.some((item) => slashMatches(item, head.name))) { // kilocode_change + if (!commands.some((item) => slashMatches(item, head.name))) { + // kilocode_change return { type: "none" as const } } @@ -538,7 +542,7 @@ export function createPromptState(input: PromptInput): PromptState { }) } - const restore = (value: RunPrompt, cursor = value.text.length) => { + const restore = (value: RunPrompt, cursor = Bun.stringWidth(value.text)) => { draft = clonePrompt(value) if (!area || area.isDestroyed) { return @@ -547,7 +551,7 @@ export function createPromptState(input: PromptInput): PromptState { hide() area.setText(value.text) restoreParts(value.parts) - area.cursorOffset = Math.min(cursor, area.plainText.length) + area.cursorOffset = Math.min(cursor, Bun.stringWidth(area.plainText)) scheduleRows() area.focus() } @@ -578,7 +582,7 @@ export function createPromptState(input: PromptInput): PromptState { area.setText(text) clearParts() draft = { text: area.plainText, parts: [] } - area.cursorOffset = Math.min(text.length, area.plainText.length) + area.cursorOffset = Math.min(Bun.stringWidth(text), Bun.stringWidth(area.plainText)) scheduleRows() area.focus() } @@ -611,12 +615,13 @@ export function createPromptState(input: PromptInput): PromptState { } if (visible() && mode() === "mention") { - if (cursor <= at() || /\s/.test(text.slice(at(), cursor))) { + const query = displaySlice(text, at(), cursor) + if (cursor <= at() || /\s/.test(query)) { hide() return } - setQuery(text.slice(at() + 1, cursor)) + setQuery(displaySlice(text, at() + 1, cursor)) return } @@ -624,19 +629,12 @@ export function createPromptState(input: PromptInput): PromptState { return } - const head = text.slice(0, cursor) - const idx = head.lastIndexOf("@") - if (idx === -1) { - return - } - - const before = idx === 0 ? undefined : head[idx - 1] - const tail = head.slice(idx) - if ((before === undefined || /\s/.test(before)) && !/\s/.test(tail)) { + const idx = mentionTriggerIndex(text, cursor) + if (idx !== undefined) { setAt(idx) menu.reset() setMode("mention") - setQuery(head.slice(idx + 1)) + setQuery(displaySlice(text, idx + 1, cursor)) } } @@ -783,7 +781,7 @@ export function createPromptState(input: PromptInput): PromptState { } const cursor = area.cursorOffset - const tail = area.plainText.at(cursor) + const tail = displayCharAt(area.plainText, cursor) const append = "@" + next.value + (tail === " " ? "" : " ") area.cursorOffset = at() const start = area.logicalCursor @@ -942,7 +940,8 @@ export function createPromptState(input: PromptInput): PromptState { } const dir = up ? -1 : 1 - if ((dir === -1 && area.cursorOffset === 0) || (dir === 1 && area.cursorOffset === area.plainText.length)) { + const endOffset = Bun.stringWidth(area.plainText) + if ((dir === -1 && area.cursorOffset === 0) || (dir === 1 && area.cursorOffset === endOffset)) { move(dir, event) return } @@ -956,7 +955,7 @@ export function createPromptState(input: PromptInput): PromptState { ? area.height - 1 : Math.max(0, (area.virtualLineCount ?? 1) - 1) if (dir === 1 && area.visualCursor.visualRow === end) { - area.cursorOffset = area.plainText.length + area.cursorOffset = endOffset } } diff --git a/packages/opencode/src/cli/cmd/run/prompt.shared.ts b/packages/opencode/src/cli/cmd/run/prompt.shared.ts index 1b639e6e7e2..0da787cb3c8 100644 --- a/packages/opencode/src/cli/cmd/run/prompt.shared.ts +++ b/packages/opencode/src/cli/cmd/run/prompt.shared.ts @@ -12,6 +12,7 @@ // The leader-key cycle (promptCycle) uses a two-step pattern: first press // arms the leader, second press within the timeout fires the action. import type { KeyBinding } from "@opentui/core" +export { displayCharAt, displaySlice, mentionTriggerIndex } from "../prompt-display" import { formatBinding, parseBindings } from "./keymap.shared" import type { FooterKeybinds, RunPrompt } from "./types" @@ -275,7 +276,7 @@ export function movePromptHistory(state: PromptHistoryState, dir: -1 | 1, text: return { state, apply: false } } - if (dir === 1 && cursor !== text.length) { + if (dir === 1 && cursor !== Bun.stringWidth(text)) { return { state, apply: false } } @@ -309,7 +310,7 @@ export function movePromptHistory(state: PromptHistoryState, dir: -1 | 1, text: index: null, }, text: state.draft, - cursor: state.draft.length, + cursor: Bun.stringWidth(state.draft), apply: true, } } @@ -320,7 +321,7 @@ export function movePromptHistory(state: PromptHistoryState, dir: -1 | 1, text: index: idx, }, text: state.items[idx].text, - cursor: dir === -1 ? 0 : state.items[idx].text.length, + cursor: dir === -1 ? 0 : Bun.stringWidth(state.items[idx].text), apply: true, } } diff --git a/packages/opencode/src/cli/cmd/run/session-data.ts b/packages/opencode/src/cli/cmd/run/session-data.ts index 2c3a33ec4cf..058bf236edb 100644 --- a/packages/opencode/src/cli/cmd/run/session-data.ts +++ b/packages/opencode/src/cli/cmd/run/session-data.ts @@ -24,7 +24,8 @@ // `data.questions`. The footer shows whichever is first. When a reply // event arrives, the queue entry is removed and the footer falls back // to the next pending request or to the prompt view. -import type { Event, Part, PermissionRequest, QuestionRequest, ToolPart } from "@kilocode/sdk/v2" +import type { Part, PermissionRequest, QuestionRequest, ToolPart } from "@kilocode/sdk/v2" +import type { Event } from "./event" import * as Locale from "@/util/locale" import { toolView } from "./tool" import type { FooterOutput, FooterPatch, FooterView, StreamCommit } from "./types" diff --git a/packages/opencode/src/cli/cmd/run/stream.transport.ts b/packages/opencode/src/cli/cmd/run/stream.transport.ts index a94ebab5220..e9e4c46e8a2 100644 --- a/packages/opencode/src/cli/cmd/run/stream.transport.ts +++ b/packages/opencode/src/cli/cmd/run/stream.transport.ts @@ -15,7 +15,8 @@ // The tick counter prevents stale idle events from resolving the wrong turn. // We also re-check live session status before resolving an idle event so a // delayed idle from an older turn cannot complete a newer busy turn. -import type { Event, GlobalEvent, KiloClient } from "@kilocode/sdk/v2" +import type { GlobalEvent, KiloClient } from "@kilocode/sdk/v2" +import { event as normalizeEvent, type Event } from "./event" import { Context, Deferred, Effect, Exit, Layer, Scope, Stream } from "effect" import { makeRuntime } from "@/effect/run-service" import { @@ -167,12 +168,8 @@ function globalPayloadEvent(value: unknown): Event | undefined { return undefined } - const payload = value.payload - if (payload.type === "sync") { - return undefined - } - - return isEvent(payload) ? payload : undefined + const payload = normalizeEvent(value.payload) + return payload && isEvent(payload) ? payload : undefined } function isMatchingDisposeEvent(value: unknown, directory: string | undefined): boolean { diff --git a/packages/opencode/src/cli/cmd/run/subagent-data.ts b/packages/opencode/src/cli/cmd/run/subagent-data.ts index 31b86042942..7fef1d9e975 100644 --- a/packages/opencode/src/cli/cmd/run/subagent-data.ts +++ b/packages/opencode/src/cli/cmd/run/subagent-data.ts @@ -1,4 +1,5 @@ -import type { Event, Message, Part, PermissionRequest, QuestionRequest, ToolPart } from "@kilocode/sdk/v2" +import type { Message, Part, PermissionRequest, QuestionRequest, ToolPart } from "@kilocode/sdk/v2" +import type { Event } from "./event" import * as Locale from "@/util/locale" import { bootstrapSessionData, diff --git a/packages/opencode/src/cli/cmd/stats.ts b/packages/opencode/src/cli/cmd/stats.ts index c7479901da9..1fdded3676c 100644 --- a/packages/opencode/src/cli/cmd/stats.ts +++ b/packages/opencode/src/cli/cmd/stats.ts @@ -1,6 +1,7 @@ import { Effect } from "effect" import { effectCmd } from "../effect-cmd" import { Session } from "@/session/session" +import { NotFoundError } from "@/storage/storage" import { Database } from "@/storage/db" import { SessionTable } from "../../session/session.sql" import { Project } from "@/project/project" @@ -164,10 +165,13 @@ export const aggregateSessionStats = Effect.fn("Cli.stats.aggregate")(function* filteredSessions, (session) => Effect.gen(function* () { - const messages = yield* svc.messages({ sessionID: session.id }) + const messages = yield* svc + .messages({ sessionID: session.id }) + .pipe(Effect.catchIf(NotFoundError.isInstance, () => Effect.succeed([]))) - let sessionCost = 0 - let sessionTokens = { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } } + let legacyCost = 0 + const legacyTokens = { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } } + const sessionTokens = session.tokens ?? legacyTokens let sessionToolUsage: Record = {} let sessionModelUsage: Record< string, @@ -183,7 +187,7 @@ export const aggregateSessionStats = Effect.fn("Cli.stats.aggregate")(function* // kilocode_change start - count propagated subagent cost once but keep child model stats (#6321) const parts = message.parts.filter((part) => part.type === "step-finish") const cost = parts.length ? parts.reduce((sum, part) => sum + part.cost, 0) : message.info.cost || 0 - if (!session.parentID) sessionCost += message.info.cost || 0 + if (!session.parentID) legacyCost += message.info.cost || 0 // kilocode_change end const modelKey = `${message.info.providerID}/${message.info.modelID}` @@ -198,11 +202,13 @@ export const aggregateSessionStats = Effect.fn("Cli.stats.aggregate")(function* sessionModelUsage[modelKey].cost += cost // kilocode_change if (message.info.tokens) { - sessionTokens.input += message.info.tokens.input || 0 - sessionTokens.output += message.info.tokens.output || 0 - sessionTokens.reasoning += message.info.tokens.reasoning || 0 - sessionTokens.cache.read += message.info.tokens.cache?.read || 0 - sessionTokens.cache.write += message.info.tokens.cache?.write || 0 + if (!session.tokens) { + legacyTokens.input += message.info.tokens.input || 0 + legacyTokens.output += message.info.tokens.output || 0 + legacyTokens.reasoning += message.info.tokens.reasoning || 0 + legacyTokens.cache.read += message.info.tokens.cache?.read || 0 + legacyTokens.cache.write += message.info.tokens.cache?.write || 0 + } sessionModelUsage[modelKey].tokens.input += message.info.tokens.input || 0 sessionModelUsage[modelKey].tokens.output += @@ -221,7 +227,9 @@ export const aggregateSessionStats = Effect.fn("Cli.stats.aggregate")(function* return { messageCount: messages.length, - sessionCost, + // Persisted totals may reflect step costs while parent assistant + // messages include propagated subagent cost. Keep the larger total. + sessionCost: session.parentID ? 0 : Math.max(session.cost ?? 0, legacyCost), sessionTokens, sessionTotalTokens: sessionTokens.input + diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index fb738cc0edd..383a11b3de5 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -2,6 +2,7 @@ import { render, TimeToFirstDraw, useKeyboard, useRenderer, useTerminalDimension import { createDefaultOpenTuiKeymap } from "@opentui/keymap/opentui" import * as Clipboard from "@tui/util/clipboard" import * as Selection from "@tui/util/selection" +import * as TuiAudio from "@tui/util/audio" import { createCliRenderer, MouseButton, TextAttributes, type CliRendererConfig } from "@opentui/core" // kilocode_change import { RouteProvider, useRoute } from "@tui/context/route" import { @@ -68,6 +69,7 @@ import { TuiConfig } from "@/cli/cmd/tui/config/tui" import { TuiPluginRuntime } from "@/cli/cmd/tui/plugin/runtime" import { createTuiApi } from "@/cli/cmd/tui/plugin/api" import type { RouteMap } from "@/cli/cmd/tui/plugin/api" +import { createTuiAttention } from "@/cli/cmd/tui/attention" import { FormatError, FormatUnknownError } from "@/cli/error" import { kitty, resetTerminalState } from "@/kilocode/cli/cmd/tui/util/terminal" // kilocode_change import * as AppExit from "@/kilocode/tui/app-exit" // kilocode_change @@ -81,6 +83,17 @@ const appBindingCommands = [ "command.palette.show", "session.list", "session.new", + "session.cycle_recent", + "session.cycle_recent_reverse", + "session.quick_switch.1", + "session.quick_switch.2", + "session.quick_switch.3", + "session.quick_switch.4", + "session.quick_switch.5", + "session.quick_switch.6", + "session.quick_switch.7", + "session.quick_switch.8", + "session.quick_switch.9", "model.list", "model.cycle_recent", "model.cycle_recent_reverse", @@ -173,10 +186,10 @@ export function tui(input: { unguard?.() resolve() } - const onBeforeExit = async () => { offKeymap() await TuiPluginRuntime.dispose() + TuiAudio.dispose() } process.on("exit", resetTerminalState) // kilocode_change @@ -282,6 +295,7 @@ function App(props: { onSnapshot?: () => Promise }) { routeRev() return routes.get(name)?.at(-1)?.render } + const attention = createTuiAttention({ renderer, config: tuiConfig, kv }) const api = createTuiApi({ tuiConfig, @@ -297,11 +311,13 @@ function App(props: { onSnapshot?: () => Promise }) { theme: themeState, toast, renderer, + attention, }) const [ready, setReady] = createSignal(false) TuiPluginRuntime.init({ api, config: tuiConfig, + dispose: () => attention.dispose(), }) .catch((error) => { console.error("Failed to load TUI plugins", error) @@ -319,7 +335,10 @@ function App(props: { onSnapshot?: () => Promise }) { }, { priority: 1 }, ) - onCleanup(offSelectionKeys) + onCleanup(() => { + offSelectionKeys() + attention.dispose() + }) // Wire up console copy-to-clipboard via opentui's onCopySelection callback renderer.console.onCopySelection = async (text: string) => { @@ -484,6 +503,37 @@ function App(props: { onSnapshot?: () => Promise }) { dialog.clear() }, }, + ...(Flag.KILO_EXPERIMENTAL_SESSION_SWITCHING + ? [ + { + name: "session.cycle_recent", + title: "Cycle to previous recent session", + category: "Session", + hidden: true, + run: () => { + local.session.cycleRecent(1) + }, + }, + { + name: "session.cycle_recent_reverse", + title: "Cycle to next recent session", + category: "Session", + hidden: true, + run: () => { + local.session.cycleRecent(-1) + }, + }, + ...Array.from({ length: 9 }, (_, i) => ({ + name: `session.quick_switch.${i + 1}`, + title: `Switch to session in quick slot ${i + 1}`, + category: "Session", + hidden: true, + run: () => { + local.session.quickSwitch(i + 1) + }, + })), + ] + : []), { name: "model.list", title: "Switch model", @@ -802,7 +852,14 @@ function App(props: { onSnapshot?: () => Promise }) { useBindings(() => ({ enabled: command.matcher, - bindings: tuiConfig.keybinds.gather("app", appBindingCommands), + bindings: tuiConfig.keybinds.gather( + "app", + Flag.KILO_EXPERIMENTAL_SESSION_SWITCHING + ? appBindingCommands + : appBindingCommands.filter( + (c) => !c.startsWith("session.cycle_recent") && !c.startsWith("session.quick_switch"), + ), + ), })) useBindings(() => ({ @@ -832,8 +889,8 @@ function App(props: { onSnapshot?: () => Promise }) { }) }) - event.on("session.deleted", (evt) => { - if (route.data.type === "session" && route.data.sessionID === evt.properties.info.id) { + event.onSync("session.deleted.1", (evt) => { + if (route.data.type === "session" && route.data.sessionID === evt.data.sessionID) { route.navigate({ type: "home" }) toast.show({ variant: "info", diff --git a/packages/opencode/src/cli/cmd/tui/asset/charge.wav b/packages/opencode/src/cli/cmd/tui/asset/charge.wav deleted file mode 100644 index d9597899cd9..00000000000 Binary files a/packages/opencode/src/cli/cmd/tui/asset/charge.wav and /dev/null differ diff --git a/packages/opencode/src/cli/cmd/tui/asset/pulse-a.wav b/packages/opencode/src/cli/cmd/tui/asset/pulse-a.wav deleted file mode 100644 index 2ebb6a38bcf..00000000000 Binary files a/packages/opencode/src/cli/cmd/tui/asset/pulse-a.wav and /dev/null differ diff --git a/packages/opencode/src/cli/cmd/tui/asset/pulse-b.wav b/packages/opencode/src/cli/cmd/tui/asset/pulse-b.wav deleted file mode 100644 index 4e1b59c9646..00000000000 Binary files a/packages/opencode/src/cli/cmd/tui/asset/pulse-b.wav and /dev/null differ diff --git a/packages/opencode/src/cli/cmd/tui/asset/pulse-c.wav b/packages/opencode/src/cli/cmd/tui/asset/pulse-c.wav deleted file mode 100644 index feb56cacdac..00000000000 Binary files a/packages/opencode/src/cli/cmd/tui/asset/pulse-c.wav and /dev/null differ diff --git a/packages/opencode/src/cli/cmd/tui/attention.ts b/packages/opencode/src/cli/cmd/tui/attention.ts new file mode 100644 index 00000000000..3af7b2e64a8 --- /dev/null +++ b/packages/opencode/src/cli/cmd/tui/attention.ts @@ -0,0 +1,262 @@ +import type { + TuiAttention, + TuiAttentionNotifyInput, + TuiAttentionNotifyResult, + TuiAttentionNotifySkipReason, + TuiAttentionWhen, + TuiKV, + TuiAttentionSoundName, + TuiAttentionSoundPack, + TuiAttentionSoundPackInfo, +} from "@kilocode/plugin/tui" +import stripAnsi from "strip-ansi" +import type { TuiConfig } from "./config/tui" +import { isAttentionSoundName } from "./config/tui-schema" +import * as TuiAudio from "@tui/util/audio" +import defaultSoundPath from "@opencode-ai/ui/audio/bip-bop-01.mp3" with { type: "file" } +import questionSoundPath from "@opencode-ai/ui/audio/bip-bop-03.mp3" with { type: "file" } +import permissionSoundPath from "@opencode-ai/ui/audio/staplebops-06.mp3" with { type: "file" } +import errorSoundPath from "@opencode-ai/ui/audio/nope-03.mp3" with { type: "file" } +import doneSoundPath from "@opencode-ai/ui/audio/bip-bop-01.mp3" with { type: "file" } +import subagentDoneSoundPath from "@opencode-ai/ui/audio/yup-01.mp3" with { type: "file" } +import * as Log from "@opencode-ai/core/util/log" + +type FocusState = "unknown" | "focused" | "blurred" + +type AttentionRenderer = { + readonly isDestroyed: boolean + on(event: "focus" | "blur", listener: () => void): unknown + off(event: "focus" | "blur", listener: () => void): unknown + triggerNotification(message: string, title?: string): boolean +} + +type RegisteredSoundPack = TuiAttentionSoundPack & { + builtin: boolean +} + +type TuiAttentionHost = TuiAttention & { + dispose(): void +} + +const log = Log.create({ service: "tui.attention" }) + +const DEFAULT_TITLE = "opencode" +const DEFAULT_PACK_ID = "opencode.default" +const KV_SOUND_PACK = "attention_sound_pack" +const TITLE_LIMIT = 80 +const MESSAGE_LIMIT = 240 +const BUILTIN_PACK: RegisteredSoundPack = { + id: DEFAULT_PACK_ID, + name: "OpenCode Default", + builtin: true, + sounds: { + default: defaultSoundPath, + question: questionSoundPath, + permission: permissionSoundPath, + error: errorSoundPath, + done: doneSoundPath, + subagent_done: subagentDoneSoundPath, + }, +} + +function skipped(reason: TuiAttentionNotifySkipReason): TuiAttentionNotifyResult { + return { + ok: false, + notification: false, + sound: false, + skipped: reason, + } +} + +function normalizeText(input: string | undefined, fallback: string, limit: number) { + const text = stripAnsi(input ?? "") + .replace(/[ \t]*[\r\n]+[ \t]*/g, " ") + .replace(/[\u0000-\u0009\u000B\u000C\u000E-\u001F\u007F-\u009F]/g, "") + .trim() + const normalized = text.length ? text : fallback + return Array.from(normalized).slice(0, limit).join("") +} + +function clampVolume(volume: number) { + if (!Number.isFinite(volume)) return 0 + return Math.min(1, Math.max(0, volume)) +} + +function soundVolume(input: TuiAttentionNotifyInput, config: Pick) { + if (!config.attention.sound) return + if (input.sound === false) return + if (input.sound === undefined) return clampVolume(config.attention.volume) + if (input.sound === true) return clampVolume(config.attention.volume) + return clampVolume(input.sound.volume ?? config.attention.volume) +} + +function normalizePack(pack: TuiAttentionSoundPack): RegisteredSoundPack | undefined { + const id = pack.id.trim() + if (!id) return + return { + id, + name: pack.name?.trim() || undefined, + builtin: false, + sounds: Object.fromEntries( + Object.entries(pack.sounds).filter( + (item): item is [TuiAttentionSoundName, string] => + isAttentionSoundName(item[0]) && typeof item[1] === "string" && item[1].trim().length > 0, + ), + ), + } +} + +function focusSkip(when: TuiAttentionWhen, focus: FocusState) { + if (when === "always") return + if (focus === "unknown") return "focus_unknown" + if (when === "blurred" && focus === "focused") return "focused" + if (when === "focused" && focus === "blurred") return "blurred" +} + +export function createTuiAttention(input: { + renderer: AttentionRenderer + config: Pick + kv?: TuiKV + audio?: Pick +}): TuiAttentionHost { + let focus: FocusState = "unknown" + let disposed = false + let activePackID: string | undefined + const packs = new Map([[BUILTIN_PACK.id, BUILTIN_PACK]]) + const audio = input.audio ?? TuiAudio + + const onFocus = () => { + focus = "focused" + } + const onBlur = () => { + focus = "blurred" + } + + input.renderer.on("focus", onFocus) + input.renderer.on("blur", onBlur) + + function configuredPackID() { + const stored = input.kv?.get(KV_SOUND_PACK, undefined) + return activePackID ?? stored ?? input.config.attention.sound_pack + } + + function currentPack() { + return packs.get(configuredPackID()) ?? BUILTIN_PACK + } + + function soundCandidates(name: TuiAttentionSoundName) { + return [input.config.attention.sounds[name], currentPack().sounds[name], BUILTIN_PACK.sounds[name]].filter( + (item, index, list): item is string => typeof item === "string" && list.indexOf(item) === index, + ) + } + + async function playSound(name: TuiAttentionSoundName, volume: number) { + try { + for (const file of soundCandidates(name)) { + const current = await audio.loadSoundFile(file).catch((error) => { + log.debug("failed to load attention sound", { file, error }) + return null + }) + if (disposed) return false + if (current == null) continue + if (audio.play(current, { volume }) != null) return true + } + return false + } catch (error) { + log.debug("failed to play attention sound", { error }) + return false + } + } + + return { + async notify(request) { + try { + if (!input.config.attention.enabled) return skipped("attention_disabled") + if (disposed || input.renderer.isDestroyed) return skipped("renderer_destroyed") + + const message = normalizeText(request.message, "", MESSAGE_LIMIT) + if (!message) return skipped("empty_message") + + const requestedNotification = typeof request.notification === "object" ? request.notification : undefined + const notificationSkip = focusSkip(requestedNotification?.when ?? "blurred", focus) + const notificationRequested = input.config.attention.notifications && request.notification !== false + const shouldNotify = notificationRequested && !notificationSkip + const notification = shouldNotify + ? (() => { + try { + return input.renderer.triggerNotification( + message, + normalizeText(request.title, DEFAULT_TITLE, TITLE_LIMIT), + ) + } catch (error) { + log.debug("failed to trigger attention notification", { error }) + return false + } + })() + : false + const volume = soundVolume(request, input.config) + const requestedSound = typeof request.sound === "object" ? request.sound : undefined + const soundSkip = volume === undefined ? undefined : focusSkip(requestedSound?.when ?? "always", focus) + const soundName = + requestedSound?.name && isAttentionSoundName(requestedSound.name) ? requestedSound.name : "default" + const sound = volume === undefined || soundSkip ? false : await playSound(soundName, volume) + + if (!notification && !sound) { + if (notificationRequested && notificationSkip) return skipped(notificationSkip) + if (soundSkip) return skipped(soundSkip) + } + + return { + ok: notification || sound, + notification, + sound, + } + } catch (error) { + log.debug("failed to handle attention notification", { error }) + return { + ok: false, + notification: false, + sound: false, + } + } + }, + soundboard: { + registerPack(pack) { + const next = normalizePack(pack) + if (!next) return () => {} + packs.set(next.id, next) + let disposed = false + return () => { + if (disposed) return + disposed = true + if (packs.get(next.id) === next) packs.delete(next.id) + } + }, + activate(id, options) { + const pack = packs.get(id) + if (!pack) return false + activePackID = pack.id + if (options?.persist) input.kv?.set(KV_SOUND_PACK, pack.id) + return true + }, + current() { + return currentPack().id + }, + list(): TuiAttentionSoundPackInfo[] { + const current = currentPack().id + return Array.from(packs.values()).map((pack) => ({ + id: pack.id, + name: pack.name, + active: pack.id === current, + builtin: pack.builtin, + })) + }, + }, + dispose() { + if (disposed) return + disposed = true + input.renderer.off("focus", onFocus) + input.renderer.off("blur", onBlur) + }, + } +} diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx index 5d8898fd9dd..0add96c2027 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx @@ -7,6 +7,7 @@ import { Locale } from "@/util/locale" import { useProject } from "@tui/context/project" import { useTheme } from "../context/theme" import { useSDK } from "../context/sdk" +import { useLocal } from "../context/local" import { Flag } from "@opencode-ai/core/flag/flag" import { DialogSessionRename } from "./dialog-session-rename" import { createDebouncedSignal } from "../util/signal" @@ -26,6 +27,7 @@ export function DialogSessionList() { const project = useProject() const { theme } = useTheme() const sdk = useSDK() + const local = useLocal() const toast = useToast() const [toDelete, setToDelete] = createSignal() const [search, setSearch] = createDebouncedSignal("", 150) @@ -152,7 +154,10 @@ export function DialogSessionList() { const [browseOrder] = createSignal(orderByRecency(sync.data.session)) + const RECENT_LIMIT = 5 + const options = createMemo(() => { + const enabled = Flag.KILO_EXPERIMENTAL_SESSION_SWITCHING const today = new Date().toDateString() const all = global() // kilocode_change const sessionMap = new Map( @@ -163,47 +168,76 @@ export function DialogSessionList() { const searchResult = searchResults() const displayOrder = searchResult ? orderByRecency(searchResult) : browseOrder() - return displayOrder - .map((id) => sessionMap.get(id)) - .filter((x) => x !== undefined) - .map((x) => { - const workspace = x.workspaceID ? project.workspace.get(x.workspaceID) : undefined - let footer: JSX.Element | string = "" - if (Flag.KILO_EXPERIMENTAL_WORKSPACES) { - if (x.workspaceID) { - footer = workspace ? ( - - ) : ( - - ) - } - } else { - footer = Locale.time(x.time.updated) - } + const dismissed = enabled ? new Set(local.session.dismissedRecent()) : new Set() + const pinned = enabled ? local.session.pinned().filter((id) => sessionMap.has(id)) : [] + const pinnedSet = new Set(pinned) + const slotByID = enabled + ? new Map(local.session.slots().map((id, i) => [id, i + 1])) + : new Map() - const date = new Date(x.time.updated) - let category = date.toDateString() - if (category === today) { - category = "Today" - } - const isDeleting = toDelete() === x.id - const status = sync.data.session_status?.[x.id] - const isWorking = status?.type === "busy" || status?.type === "retry" - return { - title: isDeleting ? `Press ${deleteHint()} again to confirm` : x.title, - description: all && x.worktreeName ? `(${x.worktreeName})` : undefined, // kilocode_change - worktree label - bg: isDeleting ? theme.error : undefined, - value: x.id, - category, - footer, - gutter: isWorking ? () => : undefined, + const recent = enabled + ? displayOrder.filter((id) => !pinnedSet.has(id) && !dismissed.has(id)).slice(0, RECENT_LIMIT) + : [] + const recentSet = new Set(recent) + + function buildOption(id: string, category: string) { + const x = sessionMap.get(id) + if (!x) return undefined + const workspace = x.workspaceID ? project.workspace.get(x.workspaceID) : undefined + + let footer: JSX.Element | string = "" + if (Flag.KILO_EXPERIMENTAL_WORKSPACES) { + if (x.workspaceID) { + footer = workspace ? ( + + ) : ( + + ) } + } else { + footer = Locale.time(x.time.updated) + } + + const isDeleting = toDelete() === x.id + const status = sync.data.session_status?.[x.id] + const isWorking = status?.type === "busy" || status?.type === "retry" + const slot = slotByID.get(x.id) + const gutter = isWorking + ? () => + : slot !== undefined + ? () => {slot} + : undefined + return { + title: isDeleting ? `Press ${deleteHint()} again to confirm` : x.title, + description: all && x.worktreeName ? `(${x.worktreeName})` : undefined, // kilocode_change - worktree label + bg: isDeleting ? theme.error : undefined, + value: x.id, + category, + footer, + gutter, + } + } + + const remaining = displayOrder + .filter((id) => !pinnedSet.has(id) && !recentSet.has(id)) + .map((id) => { + const x = sessionMap.get(id) + if (!x) return undefined + const label = new Date(x.time.updated).toDateString() + return buildOption(id, label === today ? "Today" : label) }) + .filter((x) => x !== undefined) + + return [ + ...pinned.map((id) => buildOption(id, "Pinned")).filter((x) => x !== undefined), + ...recent.map((id) => buildOption(id, "Recent")).filter((x) => x !== undefined), + ...remaining, + ] }) onMount(() => { @@ -228,6 +262,32 @@ export function DialogSessionList() { dialog.clear() }} actions={[ + ...(Flag.KILO_EXPERIMENTAL_SESSION_SWITCHING + ? [ + { + command: "session.pin.toggle", + title: "pin/unpin", + onTrigger: (option: { value: string }) => { + local.session.togglePin(option.value) + }, + }, + { + command: "session.toggle.recent", + title: "toggle recent", + onTrigger: (option: { value: string }) => { + if (local.session.isPinned(option.value)) { + toast.show({ + variant: "info", + message: "Unpin the session first to toggle it in Recent", + duration: 3000, + }) + return + } + local.session.toggleRecent(option.value) + }, + }, + ] + : []), { command: "session.delete", title: "delete", diff --git a/packages/opencode/src/cli/cmd/tui/component/logo.tsx b/packages/opencode/src/cli/cmd/tui/component/logo.tsx index 2362cd56ca7..82667dd5c18 100644 --- a/packages/opencode/src/cli/cmd/tui/component/logo.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/logo.tsx @@ -3,7 +3,6 @@ import { useRenderer } from "@opentui/solid" import { For, createMemo, createSignal, onCleanup, onMount, type JSX } from "solid-js" import { useTheme, tint } from "@tui/context/theme" import { KiloLogo } from "./kilo-logo" // kilocode_change -import * as Sound from "@tui/util/sound" import { go, logo } from "@/cli/logo" export type LogoShape = { diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx index 5908789bba3..0999ed0537d 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx @@ -19,6 +19,9 @@ import { Locale } from "@/util/locale" import type { PromptInfo } from "./history" import { useFrecency } from "./frecency" import { useBindings } from "../../keymap" +import { Reference } from "@/reference/reference" +import type { Config } from "@/config/config" +import { displayCharAt, mentionTriggerIndex } from "@/cli/cmd/prompt-display" function removeLineRange(input: string) { const hashIndex = input.lastIndexOf("#") @@ -162,7 +165,7 @@ export function Autocomplete(props: { const input = props.input() const currentCursorOffset = input.cursorOffset - const charAfterCursor = props.value.at(currentCursorOffset) + const charAfterCursor = displayCharAt(props.value, currentCursorOffset) const needsSpace = charAfterCursor !== " " const append = "@" + text + (needsSpace ? " " : "") @@ -265,6 +268,87 @@ export function Autocomplete(props: { } } + function createReferenceFilePart(input: { + alias: string + root: string + item: string + lineRange?: { startLine: number; endLine?: number } + }) { + const filename = `${input.alias}/${ + input.lineRange && !input.item.endsWith("/") + ? `${input.item}#${input.lineRange.startLine}${input.lineRange.endLine ? `-${input.lineRange.endLine}` : ""}` + : input.item + }` + const urlObj = pathToFileURL(path.join(input.root, input.item)) + + if (input.lineRange && !input.item.endsWith("/")) { + urlObj.searchParams.set("start", String(input.lineRange.startLine)) + if (input.lineRange.endLine !== undefined) { + urlObj.searchParams.set("end", String(input.lineRange.endLine)) + } + } + + return { + filename, + url: urlObj.href, + part: { + type: "file" as const, + mime: input.item.endsWith("/") ? "application/x-directory" : "text/plain", + filename, + url: urlObj.href, + source: { + type: "file" as const, + text: { + start: 0, + end: 0, + value: "", + }, + path: filename, + }, + }, + } + } + + function referencePromptText(reference: Reference.Resolved) { + const problem = reference.kind === "invalid" ? reference.message : undefined + return [ + `Referenced configured reference @${reference.name}.`, + ...(reference.kind === "local" ? ["Kind: local directory"] : []), + ...(reference.kind === "git" ? ["Kind: git repository"] : []), + ...(reference.kind === "invalid" ? [`Repository: ${reference.repository}`] : []), + ...(reference.kind === "git" ? [`Repository: ${reference.repository}`] : []), + ...(reference.kind === "git" && reference.branch ? [`Branch/ref: ${reference.branch}`] : []), + ...(reference.kind === "invalid" ? [] : [`Reference root: ${reference.path}`]), + ...(problem + ? [`Problem: ${problem}`] + : [ + "For targeted context, inspect the reference path directly with Read, Glob, and Grep. For broader research, call the task tool with subagent scout and include this reference path.", + ]), + ].join("\n") + } + + const references = createMemo(() => + Reference.resolveAll({ + references: (sync.data.config.reference ?? {}) as NonNullable, + directory: sync.path.directory || process.cwd(), + worktree: sync.path.worktree || sync.path.directory || process.cwd(), + }), + ) + + const referenceSearch = createMemo(() => { + if (!store.visible || store.visible === "/") return + const { lineRange, baseQuery } = extractLineRange(search()) + const slash = baseQuery.indexOf("/") + if (slash === -1) return + const reference = references().find((item) => item.name === baseQuery.slice(0, slash)) + if (!reference || reference.kind === "invalid") return + return { + reference, + query: baseQuery.slice(slash + 1), + lineRange, + } + }) + function normalizeMentionPath(filePath: string) { const baseDir = sync.path.directory || process.cwd() const absolute = path.resolve(filePath) @@ -296,6 +380,7 @@ export function Autocomplete(props: { () => search(), async (query) => { if (!store.visible || store.visible === "/") return [] + if (referenceSearch()) return [] const { lineRange, baseQuery } = extractLineRange(query ?? "") @@ -344,6 +429,43 @@ export function Autocomplete(props: { }, ) + const [referenceFiles] = createResource( + () => referenceSearch(), + async (match) => { + if (!match) return [] + + const result = await sdk.client.find.files({ + directory: match.reference.path, + query: match.query, + limit: 50, + }) + + if (result.error || !result.data) return [] + + const width = props.anchor().width - 4 + return result.data.map((item): AutocompleteOption => { + const { filename, part } = createReferenceFilePart({ + alias: match.reference.name, + root: match.reference.path, + item, + lineRange: match.lineRange, + }) + return { + display: Locale.truncateMiddle(filename, width), + value: filename, + isDirectory: item.endsWith("/"), + path: filename, + onSelect: () => { + insertPart(filename, part) + }, + } + }) + }, + { + initialValue: [], + }, + ) + const mcpResources = createMemo(() => { if (!store.visible || store.visible === "/") return [] @@ -402,6 +524,22 @@ export function Autocomplete(props: { ) }) + const referenceAliases = createMemo(() => + references().map( + (reference): AutocompleteOption => ({ + display: "@" + reference.name, + description: reference.kind === "invalid" ? reference.message : " configured reference", + onSelect: () => { + insertPart(reference.name, { + type: "text", + text: referencePromptText(reference), + synthetic: true, + }) + }, + }), + ), + ) + const commands = createMemo((): AutocompleteOption[] => { const results: AutocompleteOption[] = [...command.slashes()] @@ -434,11 +572,18 @@ export function Autocomplete(props: { const options = createMemo((prev: AutocompleteOption[] | undefined) => { const filesValue = files() + const referenceFilesValue = referenceFiles() + const referenceSearchValue = referenceSearch() const agentsValue = agents() + const referenceAliasesValue = referenceAliases() const commandsValue = commands() const mixed: AutocompleteOption[] = - store.visible === "@" ? [...agentsValue, ...(filesValue || []), ...mcpResources()] : [...commandsValue] + store.visible === "@" + ? referenceSearchValue + ? referenceFilesValue || [] + : [...referenceAliasesValue, ...agentsValue, ...(filesValue || []), ...mcpResources()] + : [...commandsValue] const searchValue = search() @@ -446,7 +591,7 @@ export function Autocomplete(props: { return mixed } - if (files.loading && prev && prev.length > 0) { + if ((files.loading || referenceFiles.loading) && prev && prev.length > 0) { return prev } @@ -511,7 +656,7 @@ export function Autocomplete(props: { const input = props.input() const currentCursorOffset = input.cursorOffset - const displayText = selected.display.trimEnd() + const displayText = (selected.value ?? selected.display).trimEnd() const path = displayText.startsWith("@") ? displayText.slice(1) : displayText input.cursorOffset = store.index @@ -673,13 +818,8 @@ export function Autocomplete(props: { } // Check for "@" trigger - find the nearest "@" before cursor with no whitespace between - const text = value.slice(0, offset) - const idx = text.lastIndexOf("@") - if (idx === -1) return - - const between = text.slice(idx) - const before = idx === 0 ? undefined : value[idx - 1] - if ((before === undefined || /\s/.test(before)) && !between.match(/\s/)) { + const idx = mentionTriggerIndex(value, offset) + if (idx !== undefined) { show("@") setStore("index", idx) } diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index 1525c81c353..476149cfe59 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -341,6 +341,7 @@ export function Prompt(props: PromptProps) { const usage = createMemo(() => { if (!props.sessionID) return + const session = sync.session.get(props.sessionID) const msg = sync.data.message[props.sessionID] ?? [] const last = msg.findLast((item): item is AssistantMessage => item.role === "assistant" && item.tokens.output > 0) if (!last) return @@ -351,7 +352,7 @@ export function Prompt(props: PromptProps) { const model = sync.data.provider.find((item) => item.id === last.providerID)?.models[last.modelID] const pct = model?.limit.context ? `${Math.round((tokens / model.limit.context) * 100)}%` : undefined - const cost = msg.reduce((sum, item) => sum + (item.role === "assistant" ? item.cost : 0), 0) + const cost = session?.cost ?? 0 return { context: pct ? `${Locale.number(tokens)} (${pct})` : Locale.number(tokens), cost: cost > 0 ? money.format(cost) : undefined, @@ -964,13 +965,7 @@ export function Prompt(props: PromptProps) { target: inputTarget, enabled: (() => { cursorVersion() - return ( - inputTarget() !== undefined && - !props.disabled && - !auto()?.visible && - input !== undefined && - (input.cursorOffset === 0 || input.visualCursor.visualRow === 0) - ) + return inputTarget() !== undefined && !props.disabled && !auto()?.visible && input !== undefined })(), commands: [ { @@ -979,12 +974,12 @@ export function Prompt(props: PromptProps) { category: "Prompt", run() { if (input.cursorOffset !== 0) { - input.cursorOffset = 0 - return + if (input.scrollY + input.visualCursor.visualRow === 0) input.cursorOffset = 0 + return false } const item = history.move(-1, input.plainText) - if (!item) return + if (!item) return false input.setText(item.input) setStore("prompt", item) setStore("mode", item.mode ?? "normal") @@ -1002,13 +997,7 @@ export function Prompt(props: PromptProps) { target: inputTarget, enabled: (() => { cursorVersion() - return ( - inputTarget() !== undefined && - !props.disabled && - !auto()?.visible && - input !== undefined && - (input.cursorOffset === input.plainText.length || input.visualCursor.visualRow === input.height - 1) - ) + return inputTarget() !== undefined && !props.disabled && !auto()?.visible && input !== undefined })(), commands: [ { @@ -1017,12 +1006,16 @@ export function Prompt(props: PromptProps) { category: "Prompt", run() { if (input.cursorOffset !== input.plainText.length) { - input.cursorOffset = input.plainText.length - return + if ( + input.scrollY + input.visualCursor.visualRow === + Math.max(0, input.editorView.getTotalVirtualLineCount() - 1) + ) + input.cursorOffset = input.plainText.length + return false } const item = history.move(1, input.plainText) - if (!item) return + if (!item) return false input.setText(item.input) setStore("prompt", item) setStore("mode", item.mode ?? "normal") @@ -1035,7 +1028,24 @@ export function Prompt(props: PromptProps) { } }) + let submitting = false async function submit() { + // Prevent overlapping invocations (e.g. a double-pressed Enter, or the + // input's native onSubmit racing another dispatch). Without this guard, + // a second call slips past the empty-input check before the first call + // clears `store.prompt.input`, then awaits its own `session.create` and + // ultimately reads the now-empty store — sending a phantom empty prompt + // to a freshly created session. + if (submitting) return false + submitting = true + try { + return await submitInner() + } finally { + submitting = false + } + } + + async function submitInner() { setWarpNotice(undefined) // IME: double-defer may fire before onContentChange flushes the last diff --git a/packages/opencode/src/cli/cmd/tui/config/keybind.ts b/packages/opencode/src/cli/cmd/tui/config/keybind.ts index 63aa874ea0d..f6d05dd3110 100644 --- a/packages/opencode/src/cli/cmd/tui/config/keybind.ts +++ b/packages/opencode/src/cli/cmd/tui/config/keybind.ts @@ -2,34 +2,40 @@ export * as TuiKeybind from "./keybind" import type { KeyEvent, Renderable } from "@opentui/core" import type { Binding } from "@opentui/keymap" -import type { BindingCommandMap, BindingConfig, BindingDefaults, BindingValue } from "@opentui/keymap/extras" -import z from "zod" +import type { BindingCommandMap, BindingConfig, BindingDefaults } from "@opentui/keymap/extras" +import type { DeepMutable } from "@opencode-ai/core/schema" +import { Schema } from "effect" -const KeyStroke = z - .object({ - name: z.string(), - ctrl: z.boolean().optional(), - shift: z.boolean().optional(), - meta: z.boolean().optional(), - super: z.boolean().optional(), - hyper: z.boolean().optional(), - }) - .strict() +const KeyStroke = Schema.Struct({ + name: Schema.String, + ctrl: Schema.optional(Schema.Boolean), + shift: Schema.optional(Schema.Boolean), + meta: Schema.optional(Schema.Boolean), + super: Schema.optional(Schema.Boolean), + hyper: Schema.optional(Schema.Boolean), +}) -const BindingObject = z - .object({ - key: z.union([z.string(), KeyStroke]), - event: z.enum(["press", "release"]).optional(), - preventDefault: z.boolean().optional(), - fallthrough: z.boolean().optional(), - }) - .passthrough() +const BindingObject = Schema.StructWithRest( + Schema.Struct({ + key: Schema.Union([Schema.String, KeyStroke]), + event: Schema.optional(Schema.Literals(["press", "release"])), + preventDefault: Schema.optional(Schema.Boolean), + fallthrough: Schema.optional(Schema.Boolean), + }), + [Schema.Record(Schema.String, Schema.Unknown)], +) -const BindingItem = z.union([z.string(), KeyStroke, BindingObject]) -export const BindingValueSchema = z.union([z.literal(false), z.literal("none"), BindingItem, z.array(BindingItem)]) +const BindingItem = Schema.Union([Schema.String, KeyStroke, BindingObject]) +export const BindingValueSchema = Schema.Union([ + Schema.Literal(false), + Schema.Literal("none"), + BindingItem, + Schema.Array(BindingItem), +]) +export type BindingValueSchema = DeepMutable> type Definition = { - default: z.input + default: BindingValueSchema description: string } @@ -38,7 +44,7 @@ export const LeaderDefault = "ctrl+x" const keybind = (value: Definition["default"], description: string): Definition => ({ default: value, description }) -const Definitions = { +export const Definitions = { leader: keybind(LeaderDefault, "Leader key for keybind combinations"), app_exit: keybind("ctrl+c,ctrl+d,q", "Exit the application"), @@ -80,6 +86,19 @@ const Definitions = { session_child_cycle: keybind("right", "Go to next child session"), session_child_cycle_reverse: keybind("left", "Go to previous child session"), session_parent: keybind("up", "Go to parent session"), + session_pin_toggle: keybind("ctrl+f", "Pin or unpin session in the session list"), + session_toggle_recent: keybind("ctrl+h", "Show or hide session in the Recent group"), + session_cycle_recent: keybind("]", "Cycle to the previous recent session"), + session_cycle_recent_reverse: keybind("[", "Cycle to the next recent session"), + session_quick_switch_1: keybind("1", "Switch to session in quick slot 1"), + session_quick_switch_2: keybind("2", "Switch to session in quick slot 2"), + session_quick_switch_3: keybind("3", "Switch to session in quick slot 3"), + session_quick_switch_4: keybind("4", "Switch to session in quick slot 4"), + session_quick_switch_5: keybind("5", "Switch to session in quick slot 5"), + session_quick_switch_6: keybind("6", "Switch to session in quick slot 6"), + session_quick_switch_7: keybind("7", "Switch to session in quick slot 7"), + session_quick_switch_8: keybind("8", "Switch to session in quick slot 8"), + session_quick_switch_9: keybind("9", "Switch to session in quick slot 9"), stash_delete: keybind("ctrl+d", "Delete stash entry"), model_provider_list: keybind("ctrl+a", "Open provider list from model dialog"), @@ -206,21 +225,17 @@ const Definitions = { which_key_end: keybind("ctrl+alt+end", "Jump to last which-key binding"), } satisfies Record -type KeybindName = keyof typeof Definitions & string +type KeybindName = keyof typeof Definitions +const KeybindNames = new Set(Object.keys(Definitions)) -const KeybindShape = Object.fromEntries( - Object.entries(Definitions).map(([name, item]) => [ - name, - BindingValueSchema.optional().default(item.default).describe(item.description), - ]), -) as Record>> - -const KeybindOverrideShape = Object.fromEntries( - Object.entries(Definitions).map(([name, item]) => [name, BindingValueSchema.optional().describe(item.description)]), -) as Record> - -export const Keybinds = z.strictObject(KeybindShape).describe("TUI keybinding configuration") -export const KeybindOverrides = z.strictObject(KeybindOverrideShape).describe("TUI keybinding overrides") +export const KeybindOverrides = Schema.Struct( + Object.fromEntries( + Object.entries(Definitions).map(([name, item]) => [ + name, + Schema.optional(BindingValueSchema).annotate({ description: item.description }), + ]), + ), +).annotate({ description: "TUI keybinding overrides" }) export const Descriptions = Object.fromEntries( Object.entries(Definitions).map(([name, item]) => [name, item.description]), ) as Record @@ -262,6 +277,19 @@ export const CommandMap = { session_child_cycle: "session.child.next", session_child_cycle_reverse: "session.child.previous", session_parent: "session.parent", + session_pin_toggle: "session.pin.toggle", + session_toggle_recent: "session.toggle.recent", + session_cycle_recent: "session.cycle_recent", + session_cycle_recent_reverse: "session.cycle_recent_reverse", + session_quick_switch_1: "session.quick_switch.1", + session_quick_switch_2: "session.quick_switch.2", + session_quick_switch_3: "session.quick_switch.3", + session_quick_switch_4: "session.quick_switch.4", + session_quick_switch_5: "session.quick_switch.5", + session_quick_switch_6: "session.quick_switch.6", + session_quick_switch_7: "session.quick_switch.7", + session_quick_switch_8: "session.quick_switch.8", + session_quick_switch_9: "session.quick_switch.9", stash_delete: "stash.delete", model_provider_list: "model.dialog.provider", model_favorite_toggle: "model.dialog.favorite", @@ -371,8 +399,8 @@ const CommandDescriptions = Object.fromEntries( ]), ) as Record -export type Keybinds = z.output -export type KeybindOverrides = z.output +export type Keybinds = { [K in KeybindName]: BindingValueSchema } +export type KeybindOverrides = Partial export type BindingLookupView = { readonly bindings: readonly Binding[] get(command: string): readonly Binding[] @@ -386,6 +414,29 @@ export function toBindingConfig(keybinds: Keybinds): BindingConfig } +const decodeBindingValue = Schema.decodeUnknownSync(BindingValueSchema) + +export function defaultValue(name: KeybindName) { + return Definitions[name].default +} + +export function parse(keybinds: KeybindOverrides): Keybinds { + const invalid = unknownKeys(keybinds) + if (invalid.length) throw new Error(`Unrecognized keybind${invalid.length === 1 ? "" : "s"}: ${invalid.join(", ")}`) + return Object.fromEntries( + Object.entries(Definitions).map(([name, item]) => [ + name, + decodeBindingValue(keybinds[name as KeybindName] ?? item.default), + ]), + ) as Keybinds +} + +export const Keybinds = { parse } + +export function unknownKeys(input: object) { + return Object.keys(input).filter((key) => !KeybindNames.has(key)) +} + export function bindingDefaults(): BindingDefaults { return ({ command, binding }) => { if (binding.desc !== undefined) return diff --git a/packages/opencode/src/cli/cmd/tui/config/tui-migrate.ts b/packages/opencode/src/cli/cmd/tui/config/tui-migrate.ts index 3b328f7bb5b..2d44ebbe46b 100644 --- a/packages/opencode/src/cli/cmd/tui/config/tui-migrate.ts +++ b/packages/opencode/src/cli/cmd/tui/config/tui-migrate.ts @@ -2,8 +2,8 @@ import path from "path" import { access, constants } from "fs/promises" // kilocode_change import { type ParseError as JsoncParseError, applyEdits, modify, parse as parseJsonc } from "jsonc-parser" import { unique } from "remeda" -import z from "zod" -import { TuiInfo, TuiOptions } from "./tui-schema" +import { Option, Schema } from "effect" +import { DiffStyle, ScrollAcceleration, ScrollSpeed } from "./tui-schema" import { Flag } from "@opencode-ai/core/flag/flag" import { Global } from "@opencode-ai/core/global" import { Filesystem } from "@/util/filesystem" @@ -14,16 +14,11 @@ const log = Log.create({ service: "tui.migrate" }) const TUI_SCHEMA_URL = "https://opencode.ai/tui.json" -const LegacyTheme = TuiInfo.shape.theme.optional() -const LegacyRecord = z.record(z.string(), z.unknown()).optional() - -const TuiLegacy = z - .object({ - scroll_speed: TuiOptions.shape.scroll_speed.catch(undefined), - scroll_acceleration: TuiOptions.shape.scroll_acceleration.catch(undefined), - diff_style: TuiOptions.shape.diff_style.catch(undefined), - }) - .strip() +const decodeTheme = Schema.decodeUnknownOption(Schema.String) +const decodeRecord = Schema.decodeUnknownOption(Schema.Record(Schema.String, Schema.Unknown)) +const decodeScrollSpeed = Schema.decodeUnknownOption(ScrollSpeed) +const decodeScrollAcceleration = Schema.decodeUnknownOption(ScrollAcceleration) +const decodeDiffStyle = Schema.decodeUnknownOption(DiffStyle) interface MigrateInput { cwd: string @@ -47,13 +42,13 @@ export async function migrateTuiConfig(input: MigrateInput) { const data = parseJsonc(source, errors, { allowTrailingComma: true }) if (errors.length || !data || typeof data !== "object" || Array.isArray(data)) continue - const theme = LegacyTheme.safeParse("theme" in data ? data.theme : undefined) - const keybinds = LegacyRecord.safeParse("keybinds" in data ? data.keybinds : undefined) - const legacyTui = LegacyRecord.safeParse("tui" in data ? data.tui : undefined) + const theme = decodeTheme("theme" in data ? data.theme : undefined) + const keybinds = decodeRecord("keybinds" in data ? data.keybinds : undefined) + const legacyTui = decodeRecord("tui" in data ? data.tui : undefined) const extracted = { - theme: theme.success ? theme.data : undefined, - keybinds: keybinds.success ? keybinds.data : undefined, - tui: legacyTui.success ? legacyTui.data : undefined, + theme: Option.getOrUndefined(theme), + keybinds: Option.getOrUndefined(keybinds), + tui: Option.getOrUndefined(legacyTui), } const tui = extracted.tui ? normalizeTui(extracted.tui) : undefined if (extracted.theme === undefined && extracted.keybinds === undefined && !tui) continue @@ -86,16 +81,23 @@ export async function migrateTuiConfig(input: MigrateInput) { } } -function normalizeTui(data: Record) { - const parsed = TuiLegacy.parse(data) - if ( - parsed.scroll_speed === undefined && +function normalizeTui(data: Record): + | { + scroll_speed: number | undefined + scroll_acceleration: { enabled: boolean } | undefined + diff_style: "auto" | "stacked" | undefined + } + | undefined { + const parsed = { + scroll_speed: Option.getOrUndefined(decodeScrollSpeed(data.scroll_speed)), + scroll_acceleration: Option.getOrUndefined(decodeScrollAcceleration(data.scroll_acceleration)), + diff_style: Option.getOrUndefined(decodeDiffStyle(data.diff_style)), + } + return parsed.scroll_speed === undefined && parsed.diff_style === undefined && parsed.scroll_acceleration === undefined - ) { - return - } - return parsed + ? undefined + : parsed } async function backupAndStripLegacy(file: string, source: string) { diff --git a/packages/opencode/src/cli/cmd/tui/config/tui-schema.ts b/packages/opencode/src/cli/cmd/tui/config/tui-schema.ts index 135e767e6b8..1eec1e7a68a 100644 --- a/packages/opencode/src/cli/cmd/tui/config/tui-schema.ts +++ b/packages/opencode/src/cli/cmd/tui/config/tui-schema.ts @@ -1,46 +1,78 @@ -import z from "zod" import { ConfigPlugin } from "@/config/plugin" import { TuiKeybind } from "./keybind" +import { Schema } from "effect" +import { isRecord } from "@/util/record" +import { Filesystem } from "@/util/filesystem" +import { TuiAttentionSoundNames, type TuiAttentionSoundName } from "@kilocode/plugin/tui" + +export type TuiAttentionSoundPaths = Partial> + +export function isAttentionSoundName(value: string): value is TuiAttentionSoundName { + return TuiAttentionSoundNames.includes(value as TuiAttentionSoundName) +} + +export function resolveAttentionSoundPaths( + root: string, + sounds: unknown, + options?: { trim?: boolean }, +): TuiAttentionSoundPaths { + if (!isRecord(sounds)) return {} + return Object.fromEntries( + Object.entries(sounds).flatMap(([name, file]) => { + if (!isAttentionSoundName(name)) return [] + if (typeof file !== "string") return [] + const value = options?.trim ? file.trim() : file + if (!value) return [] + return [[name, Filesystem.resolveFilePath(root, value)]] + }), + ) +} export const KeymapLeaderTimeoutDefault = 2000 -const KeymapLeaderTimeout = z.number().int().positive().describe("Leader key timeout in milliseconds") - -export const TuiOptions = z.object({ - leader_timeout: KeymapLeaderTimeout.optional(), - scroll_speed: z.number().min(0.001).optional().describe("TUI scroll speed"), - scroll_acceleration: z - .object({ - enabled: z.boolean().describe("Enable scroll acceleration"), - }) - .optional() - .describe("Scroll acceleration settings"), - diff_style: z - .enum(["auto", "stacked"]) - .optional() - .describe("Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column"), - mouse: z.boolean().optional().describe("Enable or disable mouse capture (default: true)"), - // kilocode_change start - support documented TUI attention config in Console - attention: z - .object({ - enabled: z.boolean().optional().describe("Enable TUI attention alerts"), - notifications: z.boolean().optional().describe("Enable desktop notifications for TUI attention alerts"), - sound: z.boolean().optional().describe("Enable sound for TUI attention alerts"), - volume: z.number().min(0).max(1).optional().describe("TUI attention sound volume"), - }) - .optional() - .describe("TUI attention settings"), - // kilocode_change end +const KeymapLeaderTimeout = Schema.Int.check(Schema.isGreaterThan(0)).annotate({ + description: "Leader key timeout in milliseconds", }) -export const TuiInfo = z - .object({ - $schema: z.string().optional(), - theme: z.string().optional(), - keybinds: TuiKeybind.KeybindOverrides.optional(), - plugin: ConfigPlugin.Spec.zod.array().optional(), - plugin_enabled: z.record(z.string(), z.boolean()).optional(), - }) - .extend(TuiOptions.shape) - .strict() - -export const TuiJsonSchemaInfo = TuiInfo +const TuiAttentionSounds = Schema.Struct({ + default: Schema.optional(Schema.String), + question: Schema.optional(Schema.String), + permission: Schema.optional(Schema.String), + error: Schema.optional(Schema.String), + done: Schema.optional(Schema.String), + subagent_done: Schema.optional(Schema.String), +}) + +export const ScrollSpeed = Schema.Number.check(Schema.isGreaterThanOrEqualTo(0.001)) + +export const ScrollAcceleration = Schema.Struct({ + enabled: Schema.Boolean.annotate({ description: "Enable scroll acceleration" }), +}).annotate({ description: "Scroll acceleration settings" }) + +export const DiffStyle = Schema.Literals(["auto", "stacked"]).annotate({ + description: "Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column", +}) + +export const Attention = Schema.Struct({ + enabled: Schema.optional(Schema.Boolean), + notifications: Schema.optional(Schema.Boolean), + sound: Schema.optional(Schema.Boolean), + volume: Schema.optional(Schema.Number.check(Schema.isGreaterThanOrEqualTo(0), Schema.isLessThanOrEqualTo(1))), + sound_pack: Schema.optional(Schema.String), + sounds: Schema.optional(TuiAttentionSounds), +}).annotate({ description: "Attention notification and sound settings" }) + +export const TuiInfo = Schema.Struct({ + $schema: Schema.optional(Schema.String), + theme: Schema.optional(Schema.String), + keybinds: Schema.optional(TuiKeybind.KeybindOverrides), + plugin: Schema.optional(Schema.Array(ConfigPlugin.Spec)), + plugin_enabled: Schema.optional(Schema.Record(Schema.String, Schema.Boolean)), + leader_timeout: Schema.optional(KeymapLeaderTimeout), + attention: Schema.optional(Attention), + scroll_speed: Schema.optional(ScrollSpeed).annotate({ + description: "TUI scroll speed", + }), + scroll_acceleration: Schema.optional(ScrollAcceleration), + diff_style: Schema.optional(DiffStyle), + mouse: Schema.optional(Schema.Boolean).annotate({ description: "Enable or disable mouse capture (default: true)" }), +}) diff --git a/packages/opencode/src/cli/cmd/tui/config/tui.ts b/packages/opencode/src/cli/cmd/tui/config/tui.ts index 1d4fbdc80e9..0934295c364 100644 --- a/packages/opencode/src/cli/cmd/tui/config/tui.ts +++ b/packages/opencode/src/cli/cmd/tui/config/tui.ts @@ -1,13 +1,13 @@ export * as TuiConfig from "./tui" -import type z from "zod" +import path from "path" import { createBindingLookup } from "@opentui/keymap/extras" import { mergeDeep, unique } from "remeda" -import { Context, Effect, Fiber, Layer } from "effect" +import { Cause, Context, Effect, Fiber, Layer, Schema } from "effect" import { ConfigParse } from "@/config/parse" import * as ConfigPaths from "@/config/paths" import { migrateTuiConfig } from "./tui-migrate" -import { KeymapLeaderTimeoutDefault, TuiInfo, TuiJsonSchemaInfo } from "./tui-schema" +import { KeymapLeaderTimeoutDefault, resolveAttentionSoundPaths, TuiInfo } from "./tui-schema" import { Flag } from "@opencode-ai/core/flag/flag" import { isRecord } from "@/util/record" import { Global } from "@opencode-ai/core/global" @@ -22,19 +22,29 @@ import * as Log from "@opencode-ai/core/util/log" import { ConfigVariable } from "@/config/variable" import { Npm } from "@opencode-ai/core/npm" import { KilocodeDefaultPlugins } from "@/kilocode/config/default-plugins" // kilocode_change +import type { DeepMutable } from "@opencode-ai/core/schema" +import type { TuiAttentionSoundName } from "@kilocode/plugin/tui" +import { FormatError, FormatUnknownError } from "@/cli/error" const log = Log.create({ service: "tui.config" }) export const Info = TuiInfo -export const JsonSchemaInfo = TuiJsonSchemaInfo -export type Info = z.output +export type Info = DeepMutable> type Acc = { result: Info plugin_origins: ConfigPlugin.Origin[] } -export type Resolved = Omit & { +export type Resolved = Omit & { + attention: { + enabled: boolean + notifications: boolean + sound: boolean + volume: number + sound_pack: string + sounds: Partial> + } keybinds: TuiKeybind.BindingLookupView leader_timeout: number // Internal resolved plugin list used by runtime loading. @@ -71,39 +81,26 @@ function normalize(raw: Record) { } } -export function effective(config: Info): Info { - const keybinds = { ...(config.keybinds ?? {}) } - if (process.platform === "win32") { - // Native Windows terminals do not support POSIX suspend, so prefer prompt undo. - keybinds.terminal_suspend = "none" - keybinds.input_undo ??= unique([ - "ctrl+z", - ...String(TuiKeybind.Keybinds.shape.input_undo.parse(undefined)).split(","), - ]).join(",") - } - return { - ...config, - keybinds: TuiKeybind.Keybinds.parse(keybinds), - leader_timeout: config.leader_timeout ?? KeymapLeaderTimeoutDefault, - } -} +function dropUnknownKeybinds(input: Record, configFilepath: string) { + if (!isRecord(input.keybinds)) return input + + const invalid = TuiKeybind.unknownKeys(input.keybinds) + if (!invalid.length) return input -export function resolve(config: Info, origins?: ConfigPlugin.Origin[]): Resolved { - const info = effective(config) - const keybinds = TuiKeybind.Keybinds.parse(info.keybinds ?? {}) + log.warn("ignored unknown tui keybinds", { + path: configFilepath, + keybinds: invalid, + hint: "Remove these entries or rename them to keys from the tui.json schema.", + }) return { - ...info, - keybinds: createBindingLookup(TuiKeybind.toBindingConfig(keybinds), { - commandMap: TuiKeybind.CommandMap, - bindingDefaults: TuiKeybind.bindingDefaults(), - }), - leader_timeout: info.leader_timeout ?? KeymapLeaderTimeoutDefault, - plugin_origins: origins?.length ? origins : undefined, + ...input, + keybinds: Object.fromEntries(Object.entries(input.keybinds).filter(([key]) => !invalid.includes(key))), } } const loadState = Effect.fn("TuiConfig.loadState")(function* (ctx: { directory: string }) { const afs = yield* AppFileSystem.Service + let appliedOrder = 0 const resolvePlugins = (config: Info, configFilepath: string): Effect.Effect => Effect.gen(function* () { @@ -124,14 +121,29 @@ const loadState = Effect.fn("TuiConfig.loadState")(function* (ctx: { directory: if (!isRecord(data)) return {} as Info // Flatten a nested "tui" key so users who wrote `{ "tui": { ... } }` inside tui.json // (mirroring the old opencode.json shape) still get their settings applied. - const validated = ConfigParse.schema(Info, normalize(data), configFilepath) + const normalized = dropUnknownKeybinds(normalize(data), configFilepath) + const parsed = ConfigParse.schema(Info, normalized, configFilepath) + const validated = parsed.attention?.sounds + ? { + ...parsed, + attention: { + ...parsed.attention, + sounds: resolveAttentionSoundPaths(path.dirname(configFilepath), parsed.attention.sounds), + }, + } + : parsed return yield* resolvePlugins(validated, configFilepath) }).pipe( - // catchCause (not tapErrorCause + orElseSucceed) because ConfigParse.jsonc/.schema + // catchCause (not tapErrorCause + orElseSucceed) because JSONC parsing and validation // can sync-throw — those become defects, which orElseSucceed wouldn't catch. Effect.catchCause((cause) => Effect.sync(() => { - log.warn("invalid tui config", { path: configFilepath, cause }) + const error = Cause.squash(cause) + const reason = FormatError(error) ?? FormatUnknownError(error) + log.warn("skipping invalid tui config", { + path: configFilepath, + reason, + }) return {} as Info }), ), @@ -145,18 +157,28 @@ const loadState = Effect.fn("TuiConfig.loadState")(function* (ctx: { directory: const text = yield* afs.readFileStringSafe(filepath).pipe( Effect.catchCause((cause) => Effect.sync(() => { - log.warn("failed to read tui config", { path: filepath, cause }) + const error = Cause.squash(cause) + const reason = FormatError(error) ?? FormatUnknownError(error) + log.warn("failed to read tui config", { + path: filepath, + reason, + }) return undefined }), ), ) if (!text) return {} as Info + log.info("loading tui config", { path: filepath }) return yield* load(text, filepath) }) const mergeFile = (acc: Acc, file: string) => Effect.gen(function* () { const data = yield* loadFile(file) + if (Object.keys(data).length) { + appliedOrder += 1 + log.info("applying tui config", { path: file, order: appliedOrder }) + } acc.result = mergeDeep(acc.result, data) if (!data.plugin?.length) return @@ -215,10 +237,34 @@ const loadState = Effect.fn("TuiConfig.loadState")(function* (ctx: { directory: } } - const info = effective(acc.result) - const result = resolve(info, acc.plugin_origins) + const keybinds = { ...acc.result.keybinds } + if (process.platform === "win32") { + // Native Windows terminals do not support POSIX suspend, so prefer prompt undo. + keybinds.terminal_suspend = "none" + const inputUndo = TuiKeybind.defaultValue("input_undo") + keybinds.input_undo ??= unique(["ctrl+z", ...(typeof inputUndo === "string" ? inputUndo.split(",") : [])]).join(",") + } + const parsedKeybinds = TuiKeybind.parse(keybinds) + const info = acc.result + const result: Resolved = { + ...info, + attention: { + enabled: acc.result.attention?.enabled ?? false, + notifications: acc.result.attention?.notifications ?? true, + sound: acc.result.attention?.sound ?? true, + volume: acc.result.attention?.volume ?? 0.4, + sound_pack: acc.result.attention?.sound_pack ?? "opencode.default", + sounds: acc.result.attention?.sounds ?? {}, + }, + keybinds: createBindingLookup(TuiKeybind.toBindingConfig(parsedKeybinds), { + commandMap: TuiKeybind.CommandMap, + bindingDefaults: TuiKeybind.bindingDefaults(), + }), + leader_timeout: acc.result.leader_timeout ?? KeymapLeaderTimeoutDefault, + plugin_origins: acc.plugin_origins.length ? acc.plugin_origins : undefined, + } - // kilocode_change start — inject Kilo default plugins to keep TUI aligned with server config + // kilocode_change start - inject Kilo default plugins to keep TUI aligned with server config KilocodeDefaultPlugins.apply(result, { disabled: Flag.KILO_DISABLE_DEFAULT_PLUGINS, log }) info.plugin = result.plugin // kilocode_change end diff --git a/packages/opencode/src/cli/cmd/tui/context/editor-zed.ts b/packages/opencode/src/cli/cmd/tui/context/editor-zed.ts index 008a2d13249..b572302c8ce 100644 --- a/packages/opencode/src/cli/cmd/tui/context/editor-zed.ts +++ b/packages/opencode/src/cli/cmd/tui/context/editor-zed.ts @@ -1,33 +1,36 @@ import { Database } from "bun:sqlite" import os from "node:os" import path from "node:path" -import z from "zod" +import { Option, Schema } from "effect" import { Filesystem } from "@/util/filesystem" import type { EditorSelection } from "./editor" -const ZedEditorRowSchema = z.object({ - item_kind: z.string(), - editor_id: z.number().nullable(), - workspace_id: z.number(), - workspace_paths: z.string().nullable(), - timestamp: z.string(), - buffer_path: z.string().nullable(), +const ZedEditorRowSchema = Schema.Struct({ + item_kind: Schema.String, + editor_id: Schema.NullOr(Schema.Number), + workspace_id: Schema.Number, + workspace_paths: Schema.NullOr(Schema.String), + timestamp: Schema.String, + buffer_path: Schema.NullOr(Schema.String), }) -const ZedSelectionRowSchema = z.object({ - selection_start: z.number().nullable(), - selection_end: z.number().nullable(), +const ZedSelectionRowSchema = Schema.Struct({ + selection_start: Schema.NullOr(Schema.Number), + selection_end: Schema.NullOr(Schema.Number), }) -const ZedEditorContentsSchema = z.object({ - contents: z.string().nullable(), +const ZedEditorContentsSchema = Schema.Struct({ + contents: Schema.NullOr(Schema.String), }) +const decodeZedEditorRow = Schema.decodeUnknownOption(ZedEditorRowSchema) +const decodeZedSelectionRow = Schema.decodeUnknownOption(ZedSelectionRowSchema) +const decodeZedEditorContents = Schema.decodeUnknownOption(ZedEditorContentsSchema) + const utf8 = new TextEncoder() -type ZedEditorRow = z.infer +type ZedEditorRow = Schema.Schema.Type type ZedActiveEditorRow = ZedEditorRow & { item_kind: "Editor"; editor_id: number } -type ZedSelectionRow = z.infer export type ZedSelectionResult = | { type: "selection"; selection: EditorSelection } @@ -107,8 +110,8 @@ function queryZedActiveEditor(dbPath: string, cwd: string) { .all() const rows = raw.flatMap((row) => { - const parsed = ZedEditorRowSchema.safeParse(row) - return parsed.success ? [parsed.data] : [] + const parsed = decodeZedEditorRow(row) + return Option.isSome(parsed) ? [parsed.value] : [] }) if (raw.length > 0 && rows.length === 0) return { type: "unavailable" as const } @@ -143,8 +146,8 @@ function queryZedEditorSelections(dbPath: string, row: ZedActiveEditorRow) { .all({ $editorID: row.editor_id, $workspaceID: row.workspace_id }) const selections = raw.flatMap((selection) => { - const parsed = ZedSelectionRowSchema.safeParse(selection) - return parsed.success ? [parsed.data] : [] + const parsed = decodeZedSelectionRow(selection) + return Option.isSome(parsed) ? [parsed.value] : [] }) if (raw.length > 0 && selections.length === 0) return { type: "unavailable" as const } @@ -160,7 +163,7 @@ function queryZedEditorContents(dbPath: string, row: ZedActiveEditorRow) { let db: Database | undefined try { db = new Database(dbPath, { readonly: true }) - const parsed = ZedEditorContentsSchema.safeParse( + const parsed = decodeZedEditorContents( db .query( `select contents @@ -169,8 +172,8 @@ function queryZedEditorContents(dbPath: string, row: ZedActiveEditorRow) { ) .get({ $editorID: row.editor_id, $workspaceID: row.workspace_id }), ) - if (!parsed.success) return { type: "unavailable" as const } - return { type: "contents" as const, contents: parsed.data.contents } + if (Option.isNone(parsed)) return { type: "unavailable" as const } + return { type: "contents" as const, contents: parsed.value.contents } } catch { return { type: "unavailable" as const } } finally { diff --git a/packages/opencode/src/cli/cmd/tui/context/editor.ts b/packages/opencode/src/cli/cmd/tui/context/editor.ts index 3f190f11c0a..8e2884abd1d 100644 --- a/packages/opencode/src/cli/cmd/tui/context/editor.ts +++ b/packages/opencode/src/cli/cmd/tui/context/editor.ts @@ -3,92 +3,102 @@ import os from "node:os" import path from "node:path" import { onCleanup, onMount } from "solid-js" import { createStore } from "solid-js/store" -import z from "zod" +import { Option, Schema, SchemaGetter } from "effect" import { isRecord } from "@/util/record" import { createSimpleContext } from "./helper" import { resolveZedDbPath, resolveZedSelection } from "./editor-zed" const MCP_PROTOCOL_VERSION = "2025-11-25" -const JsonRpcMessageSchema = z.object({ - id: z.union([z.number(), z.string(), z.null()]).optional(), - method: z.string().optional(), - params: z.unknown().optional(), - result: z.unknown().optional(), - error: z - .object({ - code: z.number().optional(), - message: z.string().optional(), - }) - .optional(), +const JsonRpcMessageSchema = Schema.Struct({ + id: Schema.optional(Schema.Union([Schema.Number, Schema.String, Schema.Null])), + method: Schema.optional(Schema.String), + params: Schema.optional(Schema.Unknown), + result: Schema.optional(Schema.Unknown), + error: Schema.optional( + Schema.Struct({ + code: Schema.optional(Schema.Number), + message: Schema.optional(Schema.String), + }), + ), }) -const PositionSchema = z.object({ - line: z.number(), - character: z.number(), +const PositionSchema = Schema.Struct({ + line: Schema.Number, + character: Schema.Number, }) -const EditorSelectionRangeSchema = z.object({ - text: z.string(), - selection: z.object({ +const EditorSelectionRangeSchema = Schema.Struct({ + text: Schema.String, + selection: Schema.Struct({ start: PositionSchema, end: PositionSchema, }), }) -const EditorSelectionSchema = z - .union([ - z.object({ - filePath: z.string(), - source: z.enum(["websocket", "zed"]).optional(), - ranges: z.array(EditorSelectionRangeSchema).min(1), - }), - z.object({ - text: z.string(), - filePath: z.string(), - source: z.enum(["websocket", "zed"]).optional(), - selection: z.object({ - start: PositionSchema, - end: PositionSchema, - }), +const EditorSelectionRangesSchema = Schema.Struct({ + filePath: Schema.String, + source: Schema.optional(Schema.Literals(["websocket", "zed"])), + ranges: Schema.mutable(Schema.Array(EditorSelectionRangeSchema).check(Schema.isMinLength(1))), +}) + +const EditorSelectionSchema = Schema.Union([ + EditorSelectionRangesSchema, + Schema.Struct({ + text: Schema.String, + filePath: Schema.String, + source: Schema.optional(Schema.Literals(["websocket", "zed"])), + selection: Schema.Struct({ + start: PositionSchema, + end: PositionSchema, }), - ]) - .transform((value) => - "ranges" in value - ? value - : { - filePath: value.filePath, - source: value.source, - ranges: [ - { - text: value.text, - selection: value.selection, - }, - ], - }, - ) - -const EditorMentionSchema = z.object({ - filePath: z.string(), - lineStart: z.number(), - lineEnd: z.number(), + }), +]).pipe( + Schema.decodeTo(EditorSelectionRangesSchema, { + decode: SchemaGetter.transform((value) => + "ranges" in value + ? value + : { + filePath: value.filePath, + source: value.source, + ranges: [ + { + text: value.text, + selection: value.selection, + }, + ], + }, + ), + encode: SchemaGetter.passthrough({ strict: false }), + }), +) + +const EditorMentionSchema = Schema.Struct({ + filePath: Schema.String, + lineStart: Schema.Number, + lineEnd: Schema.Number, }) -const EditorServerInfoSchema = z.object({ - protocolVersion: z.string().optional(), - serverInfo: z - .object({ - name: z.string().optional(), - version: z.string().optional(), - }) - .optional(), +const EditorServerInfoSchema = Schema.Struct({ + protocolVersion: Schema.optional(Schema.String), + serverInfo: Schema.optional( + Schema.Struct({ + name: Schema.optional(Schema.String), + version: Schema.optional(Schema.String), + }), + ), }) -type JsonRpcMessage = z.infer -export type EditorSelection = z.infer -export type EditorMention = z.infer +const decodeJsonRpcMessage = Schema.decodeUnknownOption(JsonRpcMessageSchema) +const decodeEditorSelection = Schema.decodeUnknownOption(EditorSelectionSchema) +const decodeEditorMention = Schema.decodeUnknownOption(EditorMentionSchema) +const decodeEditorServerInfo = Schema.decodeUnknownOption(EditorServerInfoSchema) + +type JsonRpcMessage = Schema.Schema.Type +export type EditorSelection = Schema.Schema.Type +export type EditorMention = Schema.Schema.Type export type EditorLabelState = "pending" | "sent" | "none" -type EditorServerInfo = z.infer +type EditorServerInfo = Schema.Schema.Type type EditorConnection = { url: string @@ -214,16 +224,15 @@ export const { use: useEditorContext, provider: EditorContextProvider } = create const message = parseMessage(event.data) if (!message) return - const selection = - message.method === "selection_changed" ? EditorSelectionSchema.safeParse(message.params) : undefined - if (selection?.success) { - setSelection({ ...selection.data, source: "websocket" }) + const selection = message.method === "selection_changed" ? decodeEditorSelection(message.params) : Option.none() + if (Option.isSome(selection)) { + setSelection({ ...selection.value, source: "websocket" }) return } - const mention = message.method === "at_mentioned" ? EditorMentionSchema.safeParse(message.params) : undefined - if (mention?.success) { - mentionListeners.forEach((listener) => listener(mention.data)) + const mention = message.method === "at_mentioned" ? decodeEditorMention(message.params) : Option.none() + if (Option.isSome(mention)) { + mentionListeners.forEach((listener) => listener(mention.value)) return } @@ -235,9 +244,9 @@ export const { use: useEditorContext, provider: EditorContextProvider } = create pending.delete(message.id) if (message.error) return - const initialize = method === "initialize" ? EditorServerInfoSchema.safeParse(message.result) : undefined - if (initialize?.success) { - setStore("server", initialize.data) + const initialize = method === "initialize" ? decodeEditorServerInfo(message.result) : Option.none() + if (Option.isSome(initialize)) { + setStore("server", initialize.value) send({ method: "notifications/initialized" }) return } @@ -447,7 +456,7 @@ function parseMessage(value: unknown) { if (typeof value !== "string") return try { - return JsonRpcMessageSchema.parse(JSON.parse(value)) + return Option.getOrUndefined(decodeJsonRpcMessage(JSON.parse(value))) } catch { return } diff --git a/packages/opencode/src/cli/cmd/tui/context/event.ts b/packages/opencode/src/cli/cmd/tui/context/event.ts index c52fb4ad812..1167095b730 100644 --- a/packages/opencode/src/cli/cmd/tui/context/event.ts +++ b/packages/opencode/src/cli/cmd/tui/context/event.ts @@ -1,45 +1,97 @@ -import type { Event } from "@kilocode/sdk/v2" +import type { Event, GlobalEvent } from "@kilocode/sdk/v2" + +type SyncEvent = Extract +type WireSyncEvent = { + type: "sync" + syncEvent: { + type: string + id: string + seq: number + aggregateID: string + data: unknown + } +} import { useProject } from "./project" import { useSDK } from "./sdk" +type EventMetadata = { + workspace: string | undefined +} + +// kilocode_change start - normalize the runtime SyncEvent wire envelope to the generated SDK shape +export function normalizeSyncEvent(payload: unknown): SyncEvent | undefined { + if (!payload || typeof payload !== "object" || !("type" in payload) || payload.type !== "sync") return + if ("name" in payload) return payload as SyncEvent + if (!("syncEvent" in payload) || !payload.syncEvent || typeof payload.syncEvent !== "object") return + + const event = payload.syncEvent as WireSyncEvent["syncEvent"] + if ( + typeof event.type !== "string" || + typeof event.id !== "string" || + typeof event.seq !== "number" || + typeof event.aggregateID !== "string" || + !("data" in event) + ) + return + + return { + type: "sync", + name: event.type, + id: event.id, + seq: event.seq, + aggregateID: event.aggregateID, + data: event.data, + } as SyncEvent +} +// kilocode_change end + export function useEvent() { const project = useProject() const sdk = useSDK() - function subscribe(handler: (event: Event) => void) { + function subscribe(handler: (event: Event, metadata: EventMetadata) => void) { return sdk.event.on("event", (event) => { - if (event.payload.type === "sync") { - return - } - - // Special hack for truly global events - if (event.directory === "global") { - handler(event.payload) - } - - if (project.workspace.current()) { - if (event.workspace === project.workspace.current()) { - handler(event.payload) - } - - return + if (event.payload.type === "sync") return + if (event.directory === "global" || event.project === project.project()) { + handler(event.payload, { workspace: event.workspace }) } + }) + } - if (event.directory === project.instance.directory()) { - handler(event.payload) + function sync(handler: (event: SyncEvent, metadata: EventMetadata) => void) { + return sdk.event.on("event", (event) => { + const payload = normalizeSyncEvent(event.payload) + if (!payload) return + if (event.directory === "global" || event.project === project.project()) { + handler(payload, { workspace: event.workspace }) } }) } - function on(type: T, handler: (event: Extract) => void) { - return subscribe((event) => { + function on( + type: T, + handler: (event: Extract, metadata: EventMetadata) => void, + ) { + return subscribe((event: Event, metadata: EventMetadata) => { if (event.type !== type) return - handler(event as Extract) + handler(event as Extract, metadata) + }) + } + + function onSync( + name: T, + handler: (event: Extract, metadata: EventMetadata) => void, + ) { + return sync((event: SyncEvent, metadata: EventMetadata) => { + if (event.name !== name) return + handler(event as Extract, metadata) }) } return { subscribe, + sync, on, + onSync, } } diff --git a/packages/opencode/src/cli/cmd/tui/context/local.tsx b/packages/opencode/src/cli/cmd/tui/context/local.tsx index 02df37eeb3e..1c162fab798 100644 --- a/packages/opencode/src/cli/cmd/tui/context/local.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/local.tsx @@ -1,11 +1,14 @@ import { createStore } from "solid-js/store" import { createSimpleContext } from "./helper" -import { batch, createEffect, createMemo } from "solid-js" +import { batch, createEffect, createMemo, on } from "solid-js" import { useSync } from "@tui/context/sync" import { useTheme } from "@tui/context/theme" +import { useRoute } from "@tui/context/route" +import { useEvent } from "@tui/context/event" import { uniqueBy } from "remeda" import path from "path" import { Global } from "@opencode-ai/core/global" +import { Flag } from "@opencode-ai/core/flag/flag" import { iife } from "@/util/iife" import { useToast } from "../ui/toast" import { useArgs } from "./args" @@ -447,6 +450,192 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ } }) + const session = iife(() => { + const [sessionStore, setSessionStore] = createStore<{ + ready: boolean + pinned: string[] + dismissedRecent: string[] + recentOrder: string[] + }>({ + ready: false, + pinned: [], + dismissedRecent: [], + recentOrder: [], + }) + + const filePath = path.join(Global.Path.state, "session.json") + const state = { + pending: false, + } + + function save() { + if (!sessionStore.ready) { + state.pending = true + return + } + state.pending = false + void Filesystem.writeJson(filePath, { + pinned: sessionStore.pinned, + dismissedRecent: sessionStore.dismissedRecent, + recentOrder: sessionStore.recentOrder, + }) + } + + Filesystem.readJson(filePath) + .then((x: any) => { + if (Array.isArray(x.pinned)) setSessionStore("pinned", x.pinned) + if (Array.isArray(x.dismissedRecent)) setSessionStore("dismissedRecent", x.dismissedRecent) + if (Array.isArray(x.recentOrder)) setSessionStore("recentOrder", x.recentOrder) + }) + .catch(() => {}) + .finally(() => { + setSessionStore("ready", true) + if (state.pending) save() + }) + + const route = useRoute() + const event = useEvent() + let cycling = false + + const slots = createMemo(() => { + const rootSessions = sync.data.session.filter((x) => x.parentID === undefined) + const existing = new Set(rootSessions.map((x) => x.id)) + const dismissed = new Set(sessionStore.dismissedRecent) + const pins = sessionStore.pinned.filter((id) => existing.has(id)) + const pinnedSet = new Set(pins) + const recent = rootSessions + .filter((x) => !pinnedSet.has(x.id) && !dismissed.has(x.id)) + .toSorted((a, b) => b.time.updated - a.time.updated) + .map((x) => x.id) + return [...pins, ...recent].slice(0, 9) + }) + + function prune(sessionID: string) { + batch(() => { + if (sessionStore.pinned.includes(sessionID)) { + setSessionStore( + "pinned", + sessionStore.pinned.filter((x) => x !== sessionID), + ) + } + if (sessionStore.dismissedRecent.includes(sessionID)) { + setSessionStore( + "dismissedRecent", + sessionStore.dismissedRecent.filter((x) => x !== sessionID), + ) + } + if (sessionStore.recentOrder.includes(sessionID)) { + setSessionStore( + "recentOrder", + sessionStore.recentOrder.filter((x) => x !== sessionID), + ) + } + save() + }) + } + + event.onSync("session.deleted.1", (evt) => { + prune(evt.data.sessionID) + }) + + if (Flag.KILO_EXPERIMENTAL_SESSION_SWITCHING) { + createEffect( + on( + () => (sessionStore.ready && route.data.type === "session" ? route.data.sessionID : undefined), + (sessionID) => { + if (!sessionID) return + if (cycling) { + cycling = false + return + } + const filtered = sessionStore.recentOrder.filter((x) => x !== sessionID) + const next = [sessionID, ...filtered].slice(0, 20) + setSessionStore("recentOrder", next) + save() + }, + ), + ) + } + + return { + get ready() { + return sessionStore.ready + }, + pinned() { + return sessionStore.pinned + }, + dismissedRecent() { + return sessionStore.dismissedRecent + }, + recentOrder() { + return sessionStore.recentOrder + }, + slots, + isPinned(sessionID: string) { + return sessionStore.pinned.includes(sessionID) + }, + isDismissed(sessionID: string) { + return sessionStore.dismissedRecent.includes(sessionID) + }, + togglePin(sessionID: string) { + batch(() => { + const exists = sessionStore.pinned.includes(sessionID) + const next = exists + ? sessionStore.pinned.filter((x) => x !== sessionID) + : [sessionID, ...sessionStore.pinned] + setSessionStore("pinned", next) + save() + }) + }, + toggleRecent(sessionID: string) { + batch(() => { + const exists = sessionStore.dismissedRecent.includes(sessionID) + const next = exists + ? sessionStore.dismissedRecent.filter((x) => x !== sessionID) + : [sessionID, ...sessionStore.dismissedRecent] + setSessionStore("dismissedRecent", next) + save() + }) + }, + quickSwitch(slot: number) { + const target = slots()[slot - 1] + if (!target) return + if (route.data.type === "session" && route.data.sessionID === target) return + route.navigate({ type: "session", sessionID: target }) + }, + cycleRecent(direction: 1 | -1) { + if (route.data.type !== "session") { + toast.show({ + variant: "info", + message: "Open a session first to cycle between recent sessions", + duration: 3000, + }) + return + } + const current = route.data.sessionID + const order = sessionStore.recentOrder.filter((id) => + sync.data.session.some((s) => s.id === id && s.parentID === undefined), + ) + if (order.length < 2) { + toast.show({ + variant: "info", + message: "No other recent sessions to cycle to", + duration: 3000, + }) + return + } + const index = order.indexOf(current) + if (index === -1) return + const next = index + direction + if (next < 0 || next >= order.length) return + const target = order[next] + if (!target || target === current) return + cycling = true + route.navigate({ type: "session", sessionID: target }) + }, + } + }) + const mcp = { isEnabled(name: string) { const status = sync.data.mcp[name] @@ -482,6 +671,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ model, agent, mcp, + session, } return result }, diff --git a/packages/opencode/src/cli/cmd/tui/context/sync-v2.tsx b/packages/opencode/src/cli/cmd/tui/context/sync-v2.tsx index faa3e0465a6..a0102e5cb27 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync-v2.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync-v2.tsx @@ -70,216 +70,216 @@ export const { use: useSyncV2, provider: SyncProviderV2 } = createSimpleContext( ) } - event.subscribe((event) => { - switch (event.type) { - case "session.next.prompted": { - update(event.properties.sessionID, (draft) => { + event.sync((event) => { + switch (event.name) { + case "session.next.prompted.1": { + update(event.data.sessionID, (draft) => { draft.unshift({ id: event.id, type: "user", - text: event.properties.prompt.text, - files: event.properties.prompt.files, - agents: event.properties.prompt.agents, - time: { created: event.properties.timestamp }, + text: event.data.prompt.text, + files: event.data.prompt.files, + agents: event.data.prompt.agents, + time: { created: event.data.timestamp }, }) }) break } - case "session.next.synthetic": - update(event.properties.sessionID, (draft) => { + case "session.next.synthetic.1": + update(event.data.sessionID, (draft) => { draft.unshift({ id: event.id, type: "synthetic", - sessionID: event.properties.sessionID, - text: event.properties.text, - time: { created: event.properties.timestamp }, + sessionID: event.data.sessionID, + text: event.data.text, + time: { created: event.data.timestamp }, }) }) break - case "session.next.shell.started": - update(event.properties.sessionID, (draft) => { + case "session.next.shell.started.1": + update(event.data.sessionID, (draft) => { draft.unshift({ id: event.id, type: "shell", - callID: event.properties.callID, - command: event.properties.command, + callID: event.data.callID, + command: event.data.command, output: "", - time: { created: event.properties.timestamp }, + time: { created: event.data.timestamp }, }) }) break - case "session.next.shell.ended": - update(event.properties.sessionID, (draft) => { - const match = activeShell(draft, event.properties.callID) + case "session.next.shell.ended.1": + update(event.data.sessionID, (draft) => { + const match = activeShell(draft, event.data.callID) if (!match) return - match.output = event.properties.output - match.time.completed = event.properties.timestamp + match.output = event.data.output + match.time.completed = event.data.timestamp }) break - case "session.next.step.started": - update(event.properties.sessionID, (draft) => { + case "session.next.step.started.1": + update(event.data.sessionID, (draft) => { const currentAssistant = activeAssistant(draft) - if (currentAssistant) currentAssistant.time.completed = event.properties.timestamp + if (currentAssistant) currentAssistant.time.completed = event.data.timestamp draft.unshift({ id: event.id, type: "assistant", - agent: event.properties.agent, - model: event.properties.model, + agent: event.data.agent, + model: event.data.model, content: [], - snapshot: event.properties.snapshot ? { start: event.properties.snapshot } : undefined, - time: { created: event.properties.timestamp }, + snapshot: event.data.snapshot ? { start: event.data.snapshot } : undefined, + time: { created: event.data.timestamp }, }) }) break - case "session.next.step.ended": - update(event.properties.sessionID, (draft) => { + case "session.next.step.ended.1": + update(event.data.sessionID, (draft) => { const currentAssistant = activeAssistant(draft) if (!currentAssistant) return - currentAssistant.time.completed = event.properties.timestamp - currentAssistant.finish = event.properties.finish - currentAssistant.cost = event.properties.cost - currentAssistant.tokens = event.properties.tokens - if (event.properties.snapshot) - currentAssistant.snapshot = { ...currentAssistant.snapshot, end: event.properties.snapshot } + currentAssistant.time.completed = event.data.timestamp + currentAssistant.finish = event.data.finish + currentAssistant.cost = event.data.cost + currentAssistant.tokens = event.data.tokens + if (event.data.snapshot) + currentAssistant.snapshot = { ...currentAssistant.snapshot, end: event.data.snapshot } }) break - case "session.next.step.failed": - update(event.properties.sessionID, (draft) => { + case "session.next.step.failed.1": + update(event.data.sessionID, (draft) => { const currentAssistant = activeAssistant(draft) if (!currentAssistant) return - currentAssistant.time.completed = event.properties.timestamp + currentAssistant.time.completed = event.data.timestamp currentAssistant.finish = "error" - currentAssistant.error = event.properties.error + currentAssistant.error = event.data.error }) break - case "session.next.text.started": - update(event.properties.sessionID, (draft) => { + case "session.next.text.started.1": + update(event.data.sessionID, (draft) => { activeAssistant(draft)?.content.push({ type: "text", text: "" }) }) break - case "session.next.text.delta": - update(event.properties.sessionID, (draft) => { + case "session.next.text.delta.1": + update(event.data.sessionID, (draft) => { const match = latestText(activeAssistant(draft)) - if (match) match.text += event.properties.delta + if (match) match.text += event.data.delta }) break - case "session.next.text.ended": - update(event.properties.sessionID, (draft) => { + case "session.next.text.ended.1": + update(event.data.sessionID, (draft) => { const match = latestText(activeAssistant(draft)) - if (match) match.text = event.properties.text + if (match) match.text = event.data.text }) break - case "session.next.tool.input.started": - update(event.properties.sessionID, (draft) => { + case "session.next.tool.input.started.1": + update(event.data.sessionID, (draft) => { activeAssistant(draft)?.content.push({ type: "tool", - id: event.properties.callID, - name: event.properties.name, - time: { created: event.properties.timestamp }, + id: event.data.callID, + name: event.data.name, + time: { created: event.data.timestamp }, state: { status: "pending", input: "" }, }) }) break - case "session.next.tool.input.delta": - update(event.properties.sessionID, (draft) => { - const match = latestTool(activeAssistant(draft), event.properties.callID) - if (match?.state.status === "pending") match.state.input += event.properties.delta + case "session.next.tool.input.delta.1": + update(event.data.sessionID, (draft) => { + const match = latestTool(activeAssistant(draft), event.data.callID) + if (match?.state.status === "pending") match.state.input += event.data.delta }) break - case "session.next.tool.input.ended": + case "session.next.tool.input.ended.1": break - case "session.next.tool.called": - update(event.properties.sessionID, (draft) => { - const match = latestTool(activeAssistant(draft), event.properties.callID) + case "session.next.tool.called.1": + update(event.data.sessionID, (draft) => { + const match = latestTool(activeAssistant(draft), event.data.callID) if (!match) return - match.time.ran = event.properties.timestamp - match.provider = event.properties.provider - match.state = { status: "running", input: event.properties.input, structured: {}, content: [] } + match.time.ran = event.data.timestamp + match.provider = event.data.provider + match.state = { status: "running", input: event.data.input, structured: {}, content: [] } }) break - case "session.next.tool.progress": - update(event.properties.sessionID, (draft) => { - const match = latestTool(activeAssistant(draft), event.properties.callID) + case "session.next.tool.progress.1": + update(event.data.sessionID, (draft) => { + const match = latestTool(activeAssistant(draft), event.data.callID) if (match?.state.status !== "running") return - match.state.structured = event.properties.structured - match.state.content = [...event.properties.content] + match.state.structured = event.data.structured + match.state.content = [...event.data.content] }) break - case "session.next.tool.success": - update(event.properties.sessionID, (draft) => { - const match = latestTool(activeAssistant(draft), event.properties.callID) + case "session.next.tool.success.1": + update(event.data.sessionID, (draft) => { + const match = latestTool(activeAssistant(draft), event.data.callID) if (match?.state.status !== "running") return match.state = { status: "completed", input: match.state.input, - structured: event.properties.structured, - content: [...event.properties.content], + structured: event.data.structured, + content: [...event.data.content], } - match.provider = event.properties.provider - match.time.completed = event.properties.timestamp + match.provider = event.data.provider + match.time.completed = event.data.timestamp }) break - case "session.next.tool.failed": - update(event.properties.sessionID, (draft) => { - const match = latestTool(activeAssistant(draft), event.properties.callID) + case "session.next.tool.failed.1": + update(event.data.sessionID, (draft) => { + const match = latestTool(activeAssistant(draft), event.data.callID) if (match?.state.status !== "running") return match.state = { status: "error", - error: event.properties.error, + error: event.data.error, input: match.state.input, structured: match.state.structured, content: match.state.content, } - match.provider = event.properties.provider - match.time.completed = event.properties.timestamp + match.provider = event.data.provider + match.time.completed = event.data.timestamp }) break - case "session.next.reasoning.started": - update(event.properties.sessionID, (draft) => { + case "session.next.reasoning.started.1": + update(event.data.sessionID, (draft) => { activeAssistant(draft)?.content.push({ type: "reasoning", - id: event.properties.reasoningID, + id: event.data.reasoningID, text: "", }) }) break - case "session.next.reasoning.delta": - update(event.properties.sessionID, (draft) => { - const match = latestReasoning(activeAssistant(draft), event.properties.reasoningID) - if (match) match.text += event.properties.delta + case "session.next.reasoning.delta.1": + update(event.data.sessionID, (draft) => { + const match = latestReasoning(activeAssistant(draft), event.data.reasoningID) + if (match) match.text += event.data.delta }) break - case "session.next.reasoning.ended": - update(event.properties.sessionID, (draft) => { - const match = latestReasoning(activeAssistant(draft), event.properties.reasoningID) - if (match) match.text = event.properties.text + case "session.next.reasoning.ended.1": + update(event.data.sessionID, (draft) => { + const match = latestReasoning(activeAssistant(draft), event.data.reasoningID) + if (match) match.text = event.data.text }) break - case "session.next.retried": + case "session.next.retried.1": break - case "session.next.compaction.started": - update(event.properties.sessionID, (draft) => { + case "session.next.compaction.started.1": + update(event.data.sessionID, (draft) => { draft.unshift({ id: event.id, type: "compaction", - reason: event.properties.reason, + reason: event.data.reason, summary: "", - time: { created: event.properties.timestamp }, + time: { created: event.data.timestamp }, }) }) break - case "session.next.compaction.delta": - update(event.properties.sessionID, (draft) => { + case "session.next.compaction.delta.1": + update(event.data.sessionID, (draft) => { const match = activeCompaction(draft) - if (match) match.summary += event.properties.text + if (match) match.summary += event.data.text }) break - case "session.next.compaction.ended": - update(event.properties.sessionID, (draft) => { + case "session.next.compaction.ended.1": + update(event.data.sessionID, (draft) => { const match = activeCompaction(draft) if (!match) return - match.summary = event.properties.text - match.include = event.properties.include + match.summary = event.data.text + match.include = event.data.include }) break } diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index 70c51d557ab..5e6a6fe1ee9 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -36,7 +36,6 @@ import { useToast } from "@tui/ui/toast" // kilocode_change import * as Log from "@opencode-ai/core/util/log" import { emptyConsoleState, type ConsoleState } from "@/config/console-state" import type { IndexingStatus } from "@kilocode/kilo-indexing/status" // kilocode_change -import { KiloIndexing } from "@/kilocode/indexing" // kilocode_change import path from "path" import { useKV } from "./kv" import { aggregateFailures } from "./aggregate-failures" @@ -179,6 +178,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ const fullSyncedSessions = new Set() const deleted = new Set() // kilocode_change let syncedWorkspace = project.workspace.current() + let vcsVersion = 0 // kilocode_change function sessionListQuery(): { scope?: "project"; path?: string } { if (!kv.get("session_directory_filter_enabled", true)) return { scope: "project" } @@ -196,7 +196,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ .then((x) => (x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id))) } - event.subscribe((event) => { + event.subscribe((event, { workspace }) => { switch (event.type) { case "server.instance.disposed": // kilocode_change start @@ -345,37 +345,6 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ setStore("session_diff", event.properties.sessionID, event.properties.diff) break - // kilocode_change start - case "session.deleted": { - const sid = event.properties.info.id - const match = Binary.search(store.session, sid, (s) => s.id) - if (match.found) { - setStore( - "session", - produce((draft) => { - draft.splice(match.index, 1) - }), - ) - } - evict(sid) - break - } - // kilocode_change end - case "session.updated": { - const result = Binary.search(store.session, event.properties.info.id, (s) => s.id) - if (result.found) { - setStore("session", result.index, reconcile(event.properties.info)) - break - } - setStore( - "session", - produce((draft) => { - draft.splice(result.index, 0, event.properties.info) - }), - ) - break - } - // kilocode_change start case "session.status": { setStore("session_status", event.properties.sessionID, event.properties.status) @@ -424,83 +393,6 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ } // kilocode_change end - // kilocode_change start - case "message.updated": { - const info = strip(event.properties.info) - const messages = store.message[info.sessionID] - if (!messages) { - setStore("message", info.sessionID, [info]) - break - } - const result = Binary.search(messages, info.id, (m) => m.id) - if (result.found) { - setStore("message", info.sessionID, result.index, reconcile(info)) - break - } - setStore( - "message", - info.sessionID, - produce((draft) => { - draft.splice(result.index, 0, info) - }), - ) - const updated = store.message[info.sessionID] - if (updated.length > 100) { - const oldest = updated[0] - batch(() => { - setStore( - "message", - info.sessionID, - produce((draft) => { - draft.shift() - }), - ) - setStore( - "part", - produce((draft) => { - delete draft[oldest.id] - }), - ) - }) - } - break - } - // kilocode_change end - case "message.removed": { - const messages = store.message[event.properties.sessionID] - const result = Binary.search(messages, event.properties.messageID, (m) => m.id) - if (result.found) { - setStore( - "message", - event.properties.sessionID, - produce((draft) => { - draft.splice(result.index, 1) - }), - ) - } - break - } - case "message.part.updated": { - const parts = store.part[event.properties.part.messageID] - if (!parts) { - setStore("part", event.properties.part.messageID, [event.properties.part]) - break - } - const result = Binary.search(parts, event.properties.part.id, (p) => p.id) - if (result.found) { - setStore("part", event.properties.part.messageID, result.index, reconcile(event.properties.part)) - break - } - setStore( - "part", - event.properties.part.messageID, - produce((draft) => { - draft.splice(result.index, 0, event.properties.part) - }), - ) - break - } - case "message.part.delta": { const parts = store.part[event.properties.messageID] if (!parts) break @@ -519,20 +411,6 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ break } - case "message.part.removed": { - const parts = store.part[event.properties.messageID] - const result = Binary.search(parts, event.properties.partID, (p) => p.id) - if (result.found) - setStore( - "part", - event.properties.messageID, - produce((draft) => { - draft.splice(result.index, 1) - }), - ) - break - } - case "lsp.updated": { const workspace = project.workspace.current() void sdk.client.lsp.status({ workspace }).then((x) => setStore("lsp", x.data ?? [])) @@ -540,7 +418,10 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ } case "vcs.branch.updated": { - setStore("vcs", { branch: event.properties.branch }) + if (workspace === project.workspace.current()) { + vcsVersion += 1 + setStore("vcs", { branch: event.properties.branch }) + } break } @@ -562,6 +443,140 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ } }) + event.sync((event) => { + switch (event.name) { + case "session.created.1": { + const info = event.data.info + const match = Binary.search(store.session, info.id, (s) => s.id) + if (match.found) setStore("session", match.index, reconcile(info)) + if (!match.found) { + setStore( + "session", + produce((draft) => { + draft.splice(match.index, 0, info) + }), + ) + } + break + } + case "session.updated.1": { + const sid = event.data.sessionID + const match = Binary.search(store.session, sid, (s) => s.id) + if (!match.found) break + setStore( + "session", + match.index, + produce((draft) => { + Object.assign(draft, event.data.info) + }), + ) + break + } + case "session.deleted.1": { + const sid = event.data.sessionID + const match = Binary.search(store.session, sid, (s) => s.id) + if (match.found) { + setStore( + "session", + produce((draft) => { + draft.splice(match.index, 1) + }), + ) + } + evict(sid) + break + } + case "message.updated.1": { + const info = strip(event.data.info) + const messages = store.message[info.sessionID] + if (!messages) { + setStore("message", info.sessionID, [info]) + break + } + const match = Binary.search(messages, info.id, (m) => m.id) + if (match.found) { + setStore("message", info.sessionID, match.index, reconcile(info)) + break + } + setStore( + "message", + info.sessionID, + produce((draft) => { + draft.splice(match.index, 0, info) + }), + ) + const updated = store.message[info.sessionID] + if (updated.length <= 100) break + const oldest = updated[0] + batch(() => { + setStore( + "message", + info.sessionID, + produce((draft) => { + draft.shift() + }), + ) + setStore( + "part", + produce((draft) => { + delete draft[oldest.id] + }), + ) + }) + break + } + case "message.removed.1": { + const messages = store.message[event.data.sessionID] + if (!messages) break + const match = Binary.search(messages, event.data.messageID, (m) => m.id) + if (!match.found) break + setStore( + "message", + event.data.sessionID, + produce((draft) => { + draft.splice(match.index, 1) + }), + ) + break + } + case "message.part.updated.1": { + const part = event.data.part + const parts = store.part[part.messageID] + if (!parts) { + setStore("part", part.messageID, [part]) + break + } + const match = Binary.search(parts, part.id, (p) => p.id) + if (match.found) { + setStore("part", part.messageID, match.index, reconcile(part)) + break + } + setStore( + "part", + part.messageID, + produce((draft) => { + draft.splice(match.index, 0, part) + }), + ) + break + } + case "message.part.removed.1": { + const parts = store.part[event.data.messageID] + if (!parts) break + const match = Binary.search(parts, event.data.partID, (p) => p.id) + if (!match.found) break + setStore( + "part", + event.data.messageID, + produce((draft) => { + draft.splice(match.index, 1) + }), + ) + break + } + } + }) + const exit = useExit() const args = useArgs() @@ -578,6 +593,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ } const projectPromise = project.sync() const sessionListPromise = projectPromise.then(() => listSessions()) + const version = vcsVersion // kilocode_change // blocking - include session.list when continuing a session const providersPromise = sdk.client.config.providers({ workspace }, { throwOnError: true }) @@ -698,7 +714,11 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ setStore("session_status", reconcile(x.data ?? {})) }), sdk.client.provider.auth({ workspace }).then((x) => setStore("provider_auth", reconcile(x.data ?? {}))), - sdk.client.vcs.get({ workspace }).then((x) => setStore("vcs", reconcile(x.data))), + sdk.client.vcs.get({ workspace }).then((x) => { + if (version === vcsVersion && workspace === project.workspace.current()) { + setStore("vcs", reconcile(x.data)) + } + }), project.workspace.sync(), // kilocode_change start - show config warnings as persistent toast sdk.client.config @@ -716,7 +736,9 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ }) }) .catch(() => {}), - KiloIndexing.current().then((x) => setStore("indexing", reconcile(x))), + sdk.client.indexing + .status({ workspace }) + .then((x) => setStore("indexing", reconcile(x.data ?? store.indexing))), // kilocode_change end ]).then(() => { setStore("status", "complete") diff --git a/packages/opencode/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx b/packages/opencode/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx index 78868d847ed..15489f2aab4 100644 --- a/packages/opencode/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx +++ b/packages/opencode/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx @@ -1,14 +1,52 @@ -// kilocode_change start - use Kilo-specific tips component -export { Tips } from "@/kilocode/components/tips" -// kilocode_change end - -import { createMemo, For } from "solid-js" +import type { TuiPluginApi } from "@kilocode/plugin/tui" +import { createMemo, For, type Accessor } from "solid-js" import { DEFAULT_THEMES, useTheme } from "@tui/context/theme" +import { Flag } from "@opencode-ai/core/flag/flag" +import { useCommandShortcut } from "../../keymap" const themeCount = Object.keys(DEFAULT_THEMES).length -const themeTip = `Use {highlight}/themes{/highlight} or {highlight}Ctrl+X T{/highlight} to switch between ${themeCount} built-in themes` type TipPart = { text: string; highlight: boolean } +type TipShortcut = Accessor +type Shortcuts = { + agentCycle: TipShortcut + childFirst: TipShortcut + childNext: TipShortcut + childPrevious: TipShortcut + commandList: TipShortcut + editorOpen: TipShortcut + helpShow: TipShortcut + inputClear: TipShortcut + inputNewline: TipShortcut + inputPaste: TipShortcut + inputUndo: TipShortcut + leader: TipShortcut + messagesCopy: TipShortcut + messagesFirst: TipShortcut + messagesLast: TipShortcut + messagesPageDown: TipShortcut + messagesPageUp: TipShortcut + messagesToggleConceal: TipShortcut + modelCycleRecent: TipShortcut + modelList: TipShortcut + sessionCycleRecent: TipShortcut + sessionCycleRecentReverse: TipShortcut + sessionExport: TipShortcut + sessionInterrupt: TipShortcut + sessionList: TipShortcut + sessionNew: TipShortcut + sessionParent: TipShortcut + sessionPinToggle: TipShortcut + sessionQuickSwitch1: TipShortcut + sessionQuickSwitch9: TipShortcut + sessionSidebarToggle: TipShortcut + sessionTimeline: TipShortcut + sessionToggleRecent: TipShortcut + statusView: TipShortcut + terminalSuspend: TipShortcut + themeList: TipShortcut +} +type Tip = string | ((shortcuts: Shortcuts) => string | undefined) function parse(tip: string): TipPart[] { const parts: TipPart[] = [] @@ -36,19 +74,86 @@ function parse(tip: string): TipPart[] { const NO_MODELS_TIP = "Run {highlight}/connect{/highlight} to add an AI provider and start coding" -// kilocode_change - renamed Tips -> _Tips so the upstream implementation stays -// as shadowed dead code while we re-export the real Tips from @/kilocode/components/tips -function _Tips(props: { connected?: boolean }) { +function shortcutText(value: string) { + return `{highlight}${value}{/highlight}` +} + +function commandText(command: string, shortcut: string) { + if (!shortcut) return shortcutText(command) + return `${shortcutText(command)} or ${shortcutText(shortcut)}` +} + +function press(shortcut: string, text: string) { + if (!shortcut) return undefined + return `Press ${shortcutText(shortcut)} ${text}` +} + +function configShortcut(api: TuiPluginApi, command: string): TipShortcut { + return () => + api.tuiConfig.keybinds + .get(command) + .map((binding) => api.keys.formatSequence(Array.from(api.keymap.parseKeySequence(binding.key)))) + .filter(Boolean) + .join(", ") +} + +export function Tips(props: { api: TuiPluginApi; connected?: boolean }) { const theme = useTheme().theme - const randomTip = TIPS[Math.floor(Math.random() * TIPS.length)] - const parts = createMemo(() => parse(props.connected === false ? NO_MODELS_TIP : randomTip)) + const tipOffset = Math.random() + const shortcuts: Shortcuts = { + agentCycle: useCommandShortcut("agent.cycle"), + childFirst: configShortcut(props.api, "session.child.first"), + childNext: configShortcut(props.api, "session.child.next"), + childPrevious: configShortcut(props.api, "session.child.previous"), + commandList: useCommandShortcut("command.palette.show"), + editorOpen: useCommandShortcut("prompt.editor"), + helpShow: useCommandShortcut("help.show"), + inputClear: useCommandShortcut("prompt.clear"), + inputNewline: useCommandShortcut("input.newline"), + inputPaste: useCommandShortcut("prompt.paste"), + inputUndo: useCommandShortcut("input.undo"), + leader: configShortcut(props.api, "leader"), + messagesCopy: configShortcut(props.api, "messages.copy"), + messagesFirst: configShortcut(props.api, "session.first"), + messagesLast: configShortcut(props.api, "session.last"), + messagesPageDown: configShortcut(props.api, "session.page.down"), + messagesPageUp: configShortcut(props.api, "session.page.up"), + messagesToggleConceal: configShortcut(props.api, "session.toggle.conceal"), + modelCycleRecent: useCommandShortcut("model.cycle_recent"), + modelList: useCommandShortcut("model.list"), + sessionCycleRecent: useCommandShortcut("session.cycle_recent"), + sessionCycleRecentReverse: useCommandShortcut("session.cycle_recent_reverse"), + sessionExport: configShortcut(props.api, "session.export"), + sessionInterrupt: configShortcut(props.api, "session.interrupt"), + sessionList: useCommandShortcut("session.list"), + sessionNew: useCommandShortcut("session.new"), + sessionParent: configShortcut(props.api, "session.parent"), + sessionPinToggle: configShortcut(props.api, "session.pin.toggle"), + sessionQuickSwitch1: useCommandShortcut("session.quick_switch.1"), + sessionQuickSwitch9: useCommandShortcut("session.quick_switch.9"), + sessionSidebarToggle: configShortcut(props.api, "session.sidebar.toggle"), + sessionTimeline: configShortcut(props.api, "session.timeline"), + sessionToggleRecent: configShortcut(props.api, "session.toggle.recent"), + statusView: useCommandShortcut("opencode.status"), + terminalSuspend: useCommandShortcut("terminal.suspend"), + themeList: useCommandShortcut("theme.switch"), + } + const tip = createMemo(() => { + if (props.connected === false) return NO_MODELS_TIP + const tips = TIPS.flatMap((item) => { + const value = typeof item === "string" ? item : item(shortcuts) + return value ? [value] : [] + }) + return tips[Math.floor(tipOffset * tips.length)] ?? NO_MODELS_TIP + }) + const parts = createMemo(() => parse(tip())) return ( ● Tip{" "} - + {(part) => {part.text}} @@ -57,63 +162,90 @@ function _Tips(props: { connected?: boolean }) { ) } -const TIPS = [ +const TIPS: Tip[] = [ "Type {highlight}@{/highlight} followed by a filename to fuzzy search and attach files", "Start a message with {highlight}!{/highlight} to run shell commands directly (e.g., {highlight}!ls -la{/highlight})", - "Press {highlight}Tab{/highlight} to cycle between Build and Plan agents", + (shortcuts) => press(shortcuts.agentCycle(), "to cycle between Build and Plan agents"), "Use {highlight}/undo{/highlight} to revert the last message and file changes", "Use {highlight}/redo{/highlight} to restore previously undone messages and file changes", - "Run {highlight}/share{/highlight} to create a public link to your conversation at opencode.ai", + "Run {highlight}/share{/highlight} to create a public link to your conversation", // kilocode_change "Drag and drop images or PDFs into the terminal to add them as context", - "Press {highlight}Ctrl+V{/highlight} to paste images from your clipboard into the prompt", - "Press {highlight}Ctrl+X E{/highlight} or {highlight}/editor{/highlight} to compose messages in your external editor", + (shortcuts) => press(shortcuts.inputPaste(), "to paste images from your clipboard into the prompt"), + (shortcuts) => `Use ${commandText("/editor", shortcuts.editorOpen())} to compose messages in your external editor`, "Run {highlight}/init{/highlight} to auto-generate project rules based on your codebase", - "Run {highlight}/models{/highlight} or {highlight}Ctrl+X M{/highlight} to see and switch between available AI models", - themeTip, - "Press {highlight}Ctrl+X N{/highlight} or {highlight}/new{/highlight} to start a fresh conversation session", - "Use {highlight}/sessions{/highlight} or {highlight}Ctrl+X L{/highlight} to list and continue previous conversations", + (shortcuts) => `Use ${commandText("/models", shortcuts.modelList())} to see and switch between available AI models`, + (shortcuts) => `Use ${commandText("/themes", shortcuts.themeList())} to switch between ${themeCount} built-in themes`, + (shortcuts) => `Use ${commandText("/new", shortcuts.sessionNew())} to start a fresh conversation session`, + (shortcuts) => `Use ${commandText("/sessions", shortcuts.sessionList())} to list and continue previous conversations`, + ...(Flag.KILO_EXPERIMENTAL_SESSION_SWITCHING + ? ([ + (shortcuts) => + press(shortcuts.sessionPinToggle(), "in the session list to pin a session so it stays at the top"), + (shortcuts) => + shortcuts.sessionQuickSwitch1() && shortcuts.sessionQuickSwitch9() + ? `Pinned and recent sessions are bound to ${shortcutText(shortcuts.sessionQuickSwitch1())} through ${shortcutText(shortcuts.sessionQuickSwitch9())} for one-press switching` + : undefined, + (shortcuts) => + shortcuts.sessionCycleRecent() && shortcuts.sessionCycleRecentReverse() + ? `Press ${shortcutText(shortcuts.sessionCycleRecent())} / ${shortcutText(shortcuts.sessionCycleRecentReverse())} to cycle through recently visited sessions` + : undefined, + (shortcuts) => + press(shortcuts.sessionToggleRecent(), "in the session list to show or hide a session in the Recent group"), + ] satisfies Tip[]) + : []), "Run {highlight}/compact{/highlight} to summarize long sessions near context limits", - "Press {highlight}Ctrl+X X{/highlight} or {highlight}/export{/highlight} to save the conversation as Markdown", - "Press {highlight}Ctrl+X Y{/highlight} to copy the assistant's last message to clipboard", - "Press {highlight}Ctrl+P{/highlight} to see all available actions and commands", + (shortcuts) => `Use ${commandText("/export", shortcuts.sessionExport())} to save the conversation as Markdown`, + (shortcuts) => press(shortcuts.messagesCopy(), "to copy the assistant's last message to clipboard"), + (shortcuts) => press(shortcuts.commandList(), "to see all available actions and commands"), "Run {highlight}/connect{/highlight} to add API keys for 75+ supported LLM providers", - "The leader key is {highlight}Ctrl+X{/highlight}; combine with other keys for quick actions", - "Press {highlight}F2{/highlight} to quickly switch between recently used models", - "Press {highlight}Ctrl+X B{/highlight} to show/hide the sidebar panel", - "Use {highlight}PageUp{/highlight}/{highlight}PageDown{/highlight} to navigate through conversation history", - "Press {highlight}Ctrl+G{/highlight} or {highlight}Home{/highlight} to jump to the beginning of the conversation", - "Press {highlight}Ctrl+Alt+G{/highlight} or {highlight}End{/highlight} to jump to the most recent message", - "Press {highlight}Shift+Enter{/highlight} or {highlight}Ctrl+J{/highlight} to add newlines in your prompt", - "Press {highlight}Ctrl+C{/highlight} when typing to clear the input field", - "Press {highlight}Escape{/highlight} to stop the AI mid-response", + (shortcuts) => `The leader key is ${shortcutText(shortcuts.leader())}; combine with other keys for quick actions`, + (shortcuts) => press(shortcuts.modelCycleRecent(), "to quickly switch between recently used models"), + (shortcuts) => press(shortcuts.sessionSidebarToggle(), "in a session to show or hide the sidebar panel"), + (shortcuts) => + shortcuts.messagesPageUp() && shortcuts.messagesPageDown() + ? `Use ${shortcutText(shortcuts.messagesPageUp())}/${shortcutText(shortcuts.messagesPageDown())} to navigate through conversation history` + : undefined, + (shortcuts) => press(shortcuts.messagesFirst(), "to jump to the beginning of the conversation"), + (shortcuts) => press(shortcuts.messagesLast(), "to jump to the most recent message"), + (shortcuts) => press(shortcuts.inputNewline(), "to add newlines in your prompt"), + (shortcuts) => press(shortcuts.inputClear(), "when typing to clear the input field"), + (shortcuts) => press(shortcuts.sessionInterrupt(), "to stop the AI mid-response"), "Switch to {highlight}Plan{/highlight} agent to get suggestions without making actual changes", "Use {highlight}@agent-name{/highlight} in prompts to invoke specialized subagents", - "Press {highlight}Ctrl+X Right/Left{/highlight} to cycle through parent and child sessions", - "Create {highlight}opencode.json{/highlight} for server settings and {highlight}tui.json{/highlight} for TUI settings", - "Place TUI settings in {highlight}~/.config/opencode/tui.json{/highlight} for global config", + (shortcuts) => { + const items = [ + shortcuts.sessionParent(), + shortcuts.childFirst(), + shortcuts.childPrevious(), + shortcuts.childNext(), + ].filter(Boolean) + if (!items.length) return undefined + return `Use ${items.map(shortcutText).join(" / ")} to move between parent and child sessions` + }, + "Create {highlight}kilo.json{/highlight} for server settings and {highlight}tui.json{/highlight} for TUI settings", // kilocode_change + "Place TUI settings in {highlight}~/.config/kilo/tui.json{/highlight} for global config", // kilocode_change "Add {highlight}$schema{/highlight} to your config for autocomplete in your editor", "Configure {highlight}model{/highlight} in config to set your default model", "Override any keybind in {highlight}tui.json{/highlight} via the {highlight}keybinds{/highlight} section", "Set any keybind to {highlight}none{/highlight} to disable it completely", "Configure local or remote MCP servers in the {highlight}mcp{/highlight} config section", - "OpenCode auto-handles OAuth for remote MCP servers requiring auth", - "Add {highlight}.md{/highlight} files to {highlight}.opencode/command/{/highlight} to define reusable custom prompts", + "Add {highlight}.md{/highlight} files to {highlight}.opencode/commands/{/highlight} to define reusable custom prompts", "Use {highlight}$ARGUMENTS{/highlight}, {highlight}$1{/highlight}, {highlight}$2{/highlight} in custom commands for dynamic input", "Use backticks in commands to inject shell output (e.g., {highlight}`git status`{/highlight})", - "Add {highlight}.md{/highlight} files to {highlight}.opencode/agent/{/highlight} for specialized AI personas", + "Add {highlight}.md{/highlight} files to {highlight}.opencode/agents/{/highlight} for specialized AI personas", "Configure per-agent permissions for {highlight}edit{/highlight}, {highlight}bash{/highlight}, and {highlight}webfetch{/highlight} tools", 'Use patterns like {highlight}"git *": "allow"{/highlight} for granular bash permissions', 'Set {highlight}"rm -rf *": "deny"{/highlight} to block destructive commands', 'Configure {highlight}"git push": "ask"{/highlight} to require approval before pushing', - "OpenCode auto-formats files using prettier, gofmt, ruff, and more", - 'Set {highlight}"formatter": false{/highlight} in config to disable all auto-formatting', + 'Set {highlight}"formatter": true{/highlight} in config to enable built-in formatters like prettier, gofmt, and ruff', + 'Set {highlight}"formatter": false{/highlight} in config to disable formatters enabled by another config layer', "Define custom formatter commands with file extensions in config", - "OpenCode uses LSP servers for intelligent code analysis", + 'Set {highlight}"lsp": true{/highlight} in config to enable built-in LSP servers for code analysis', "Create {highlight}.ts{/highlight} files in {highlight}.opencode/tools/{/highlight} to define new LLM tools", "Tool definitions can invoke scripts written in Python, Go, etc", - "Add {highlight}.ts{/highlight} files to {highlight}.opencode/plugin/{/highlight} for event hooks", + "Add {highlight}.ts{/highlight} files to {highlight}.opencode/plugins/{/highlight} for event hooks", "Use plugins to send OS notifications when sessions complete", - "Create a plugin to prevent OpenCode from reading sensitive files", + "Create a plugin to prevent Kilo from reading sensitive files", // kilocode_change "Use {highlight}kilo run{/highlight} for non-interactive scripting", // kilocode_change "Use {highlight}kilo --continue{/highlight} to resume the last session", // kilocode_change "Use {highlight}kilo run -f file.ts{/highlight} to attach files via CLI", // kilocode_change @@ -130,7 +262,7 @@ const TIPS = [ 'Use {highlight}"theme": "system"{/highlight} to match your terminal\'s colors', "Create JSON theme files in {highlight}.opencode/themes/{/highlight} directory", "Themes support dark/light variants for both modes", - "Reference ANSI colors 0-255 in custom themes", + "Use numeric xterm color codes 0-255 in custom theme JSON", "Use {highlight}{env:VAR_NAME}{/highlight} syntax to reference environment variables in config", "Use {highlight}{file:path}{/highlight} to include file contents in config values", "Use {highlight}instructions{/highlight} in config to load additional rules files", @@ -144,20 +276,25 @@ const TIPS = [ "Run {highlight}/unshare{/highlight} to remove a session from public access", "Permission {highlight}doom_loop{/highlight} prevents infinite tool call loops", "Permission {highlight}external_directory{/highlight} protects files outside project", - "Run {highlight}opencode debug config{/highlight} to troubleshoot configuration", + "Run {highlight}kilo debug config{/highlight} to troubleshoot configuration", // kilocode_change "Use {highlight}--print-logs{/highlight} flag to see detailed logs in stderr", - "Press {highlight}Ctrl+X G{/highlight} or {highlight}/timeline{/highlight} to jump to specific messages", - "Press {highlight}Ctrl+X H{/highlight} to toggle code block visibility in messages", - "Press {highlight}Ctrl+X S{/highlight} or {highlight}/status{/highlight} to see system status info", + (shortcuts) => `Use ${commandText("/timeline", shortcuts.sessionTimeline())} to jump to specific messages`, + (shortcuts) => press(shortcuts.messagesToggleConceal(), "to toggle code block visibility in messages"), + (shortcuts) => `Use ${commandText("/status", shortcuts.statusView())} to see system status info`, "Enable {highlight}scroll_acceleration{/highlight} in {highlight}tui.json{/highlight} for smooth macOS-style scrolling", - "Toggle username display in chat via command palette ({highlight}Ctrl+P{/highlight})", - "Run {highlight}docker run -it --rm ghcr.io/kilo-org/kilocode{/highlight} for containerized use", - "Use {highlight}/connect{/highlight} with OpenCode Zen for curated, tested models", + (shortcuts) => + shortcuts.commandList() + ? `Toggle username display in chat via the command palette (${shortcutText(shortcuts.commandList())})` + : "Toggle username display in chat via the command palette", + "Run {highlight}docker run -it --rm ghcr.io/kilo-org/kilocode{/highlight} for containerized use", // kilocode_change + "Use {highlight}/connect{/highlight} with Kilo Gateway for curated, tested models", // kilocode_change "Commit your project's {highlight}AGENTS.md{/highlight} file to Git for team sharing", "Use {highlight}/review{/highlight} to review uncommitted changes, branches, or PRs", - "Run {highlight}/help{/highlight} or {highlight}Ctrl+X H{/highlight} to show the help dialog", + (shortcuts) => `Use ${commandText("/help", shortcuts.helpShow())} to show the help dialog`, "Use {highlight}/rename{/highlight} to rename the current session", ...(process.platform === "win32" - ? ["Press {highlight}Ctrl+Z{/highlight} to undo changes in your prompt"] - : ["Press {highlight}Ctrl+Z{/highlight} to suspend the terminal and return to your shell"]), + ? ([(shortcuts) => press(shortcuts.inputUndo(), "to undo changes in your prompt")] satisfies Tip[]) + : ([ + (shortcuts) => press(shortcuts.terminalSuspend(), "to suspend the terminal and return to your shell"), + ] satisfies Tip[])), ] diff --git a/packages/opencode/src/cli/cmd/tui/feature-plugins/home/tips.tsx b/packages/opencode/src/cli/cmd/tui/feature-plugins/home/tips.tsx index 36d6de9a888..da53e18edf0 100644 --- a/packages/opencode/src/cli/cmd/tui/feature-plugins/home/tips.tsx +++ b/packages/opencode/src/cli/cmd/tui/feature-plugins/home/tips.tsx @@ -24,9 +24,9 @@ function View(props: { api: TuiPluginApi; hidden: boolean; show: boolean; connec })) return ( - + - + ) diff --git a/packages/opencode/src/cli/cmd/tui/feature-plugins/sidebar/context.tsx b/packages/opencode/src/cli/cmd/tui/feature-plugins/sidebar/context.tsx index 52bd301e52e..fde2d0cb140 100644 --- a/packages/opencode/src/cli/cmd/tui/feature-plugins/sidebar/context.tsx +++ b/packages/opencode/src/cli/cmd/tui/feature-plugins/sidebar/context.tsx @@ -13,7 +13,8 @@ const money = new Intl.NumberFormat("en-US", { function View(props: { api: TuiPluginApi; session_id: string }) { const theme = () => props.api.theme.current const msg = createMemo(() => props.api.state.session.messages(props.session_id)) - const cost = createMemo(() => msg().reduce((sum, item) => sum + (item.role === "assistant" ? item.cost : 0), 0)) + const session = createMemo(() => props.api.state.session.get(props.session_id)) + const cost = createMemo(() => session()?.cost ?? 0) const state = createMemo(() => { const last = msg().findLast((item): item is AssistantMessage => item.role === "assistant" && item.tokens.output > 0) diff --git a/packages/opencode/src/cli/cmd/tui/feature-plugins/system/notifications.ts b/packages/opencode/src/cli/cmd/tui/feature-plugins/system/notifications.ts new file mode 100644 index 00000000000..98127f131f4 --- /dev/null +++ b/packages/opencode/src/cli/cmd/tui/feature-plugins/system/notifications.ts @@ -0,0 +1,94 @@ +import type { Event } from "@kilocode/sdk/v2" +import type { TuiAttentionSoundName, TuiPlugin, TuiPluginApi } from "@kilocode/plugin/tui" +import type { InternalTuiPlugin } from "../../plugin/internal" + +const id = "internal:notifications" + +type SessionError = Extract["properties"]["error"] + +function notify(api: TuiPluginApi, sessionID: string | undefined, message: string, sound: TuiAttentionSoundName) { + const session = sessionID ? api.state.session.get(sessionID) : undefined + const isSubagent = session?.parentID !== undefined + void api.attention.notify({ + title: session?.title, + message, + notification: isSubagent ? false : { when: "blurred" }, + sound: { name: sound, when: "always" }, + }) +} + +function sessionErrorMessage(error: SessionError) { + if (error?.name === "MessageAbortedError") return "Session aborted" + const data = error?.data + if (data && typeof data === "object" && "message" in data && data.message === "SSE read timed out") { + return "Model stopped responding" + } + return "Session error" +} + +const tui: TuiPlugin = async (api) => { + const active = new Set() + const errored = new Set() + const questions = new Set() + const permissions = new Set() + + api.event.on("question.asked", (event) => { + if (questions.has(event.properties.id)) return + questions.add(event.properties.id) + notify(api, event.properties.sessionID, "Question needs input", "question") + }) + + api.event.on("question.replied", (event) => { + questions.delete(event.properties.requestID) + }) + + api.event.on("question.rejected", (event) => { + questions.delete(event.properties.requestID) + }) + + api.event.on("permission.asked", (event) => { + if (permissions.has(event.properties.id)) return + permissions.add(event.properties.id) + notify(api, event.properties.sessionID, "Permission needs input", "permission") + }) + + api.event.on("permission.replied", (event) => { + permissions.delete(event.properties.requestID) + }) + + api.event.on("session.status", (event) => { + const sessionID = event.properties.sessionID + if (event.properties.status.type === "busy" || event.properties.status.type === "retry") { + active.add(sessionID) + errored.delete(sessionID) + return + } + + if (event.properties.status.type !== "idle") return + if (!active.has(sessionID)) return + active.delete(sessionID) + + if (errored.has(sessionID)) { + errored.delete(sessionID) + return + } + + const session = api.state.session.get(sessionID) + notify(api, sessionID, "Session done", session?.parentID ? "subagent_done" : "done") + }) + + api.event.on("session.error", (event) => { + const sessionID = event.properties.sessionID + if (!sessionID) return + if (!active.has(sessionID)) return + errored.add(sessionID) + notify(api, sessionID, sessionErrorMessage(event.properties.error), "error") + }) +} + +const plugin: InternalTuiPlugin = { + id, + tui, +} + +export default plugin diff --git a/packages/opencode/src/cli/cmd/tui/feature-plugins/system/session-v2.tsx b/packages/opencode/src/cli/cmd/tui/feature-plugins/system/session-v2.tsx index 3a0f78540d7..f3d29d9016c 100644 --- a/packages/opencode/src/cli/cmd/tui/feature-plugins/system/session-v2.tsx +++ b/packages/opencode/src/cli/cmd/tui/feature-plugins/system/session-v2.tsx @@ -438,9 +438,6 @@ function AssistantTool(props: { part: SessionMessageAssistantTool; sessionID: st - - - @@ -773,15 +770,6 @@ function WebFetch(props: ToolProps) { ) } -function CodeSearch(props: ToolProps) { - return ( - - Exa Code Search "{stringValue(props.input.query) ?? pendingInput(props.part)}"{" "} - {(results) => <>({results()} results)} - - ) -} - function WebSearch(props: ToolProps) { const label = createMemo(() => webSearchProviderLabel(props.metadata.provider)) return ( diff --git a/packages/opencode/src/cli/cmd/tui/plugin/api.tsx b/packages/opencode/src/cli/cmd/tui/plugin/api.tsx index 8ce51e65f50..0897a69d6c4 100644 --- a/packages/opencode/src/cli/cmd/tui/plugin/api.tsx +++ b/packages/opencode/src/cli/cmd/tui/plugin/api.tsx @@ -40,6 +40,7 @@ type Input = { theme: ReturnType toast: ReturnType renderer: TuiPluginApi["renderer"] + attention: TuiPluginApi["attention"] } function routeRegister(routes: RouteMap, list: TuiRouteDefinition[], bump: () => void) { @@ -151,6 +152,9 @@ function stateApi(sync: ReturnType): TuiPluginApi["state"] { count() { return sync.data.session.length }, + get(sessionID) { + return sync.session.get(sessionID) + }, diff(sessionID) { return (sync.data.session_diff[sessionID] ?? []).flatMap((item) => item.file === undefined ? [] : [{ ...item, file: item.file }], @@ -212,6 +216,7 @@ export function createTuiApi(input: Input): TuiPluginApi { } return { app: appApi(), + attention: input.attention, // Keep deprecated `api.command` working for v1 plugins; remove in v2. command: createCommandShim(input.keymap, input.dialog, input.tuiConfig.keybinds), keys: { diff --git a/packages/opencode/src/cli/cmd/tui/plugin/internal.ts b/packages/opencode/src/cli/cmd/tui/plugin/internal.ts index f351ec9f13a..9407bf8df76 100644 --- a/packages/opencode/src/cli/cmd/tui/plugin/internal.ts +++ b/packages/opencode/src/cli/cmd/tui/plugin/internal.ts @@ -6,6 +6,7 @@ import HomeOnboarding from "@/kilocode/plugins/home-onboarding" import KiloHomeFooter from "@/kilocode/plugins/home-footer" import KiloSidebarFooter from "@/kilocode/plugins/sidebar-footer" import KiloSidebarBackgroundProcesses from "@/kilocode/plugins/sidebar-background-processes" +import KiloSidebarIndexing from "@/kilocode/plugins/sidebar-indexing" import KiloSidebarPr from "@/kilocode/plugins/sidebar-pr" import KiloSidebarUsage from "@/kilocode/plugins/sidebar-usage" // kilocode_change end @@ -16,10 +17,11 @@ import SidebarTodo from "../feature-plugins/sidebar/todo" import SidebarFiles from "../feature-plugins/sidebar/files" import SidebarFooter from "../feature-plugins/sidebar/footer" import PluginManager from "../feature-plugins/system/plugins" +import Notifications from "../feature-plugins/system/notifications" import SessionV2Debug from "../feature-plugins/system/session-v2" import WhichKey from "../feature-plugins/system/which-key" import type { TuiPlugin, TuiPluginModule } from "@kilocode/plugin/tui" -import { Flag } from "@opencode-ai/core/flag/flag" +import type { RuntimeFlags } from "@/effect/runtime-flags" export type InternalTuiPlugin = Omit & { id: string @@ -27,23 +29,27 @@ export type InternalTuiPlugin = Omit & { enabled?: boolean } -export const INTERNAL_TUI_PLUGINS: InternalTuiPlugin[] = [ - HomeNews, // kilocode_change - HomeOnboarding, // kilocode_change - KiloHomeFooter, // kilocode_change - KiloSidebarFooter, // kilocode_change - KiloSidebarBackgroundProcesses, // kilocode_change - KiloSidebarPr, // kilocode_change - KiloSidebarUsage, // kilocode_change - HomeFooter, - HomeTips, - SidebarContext, - SidebarMcp, - SidebarLsp, - SidebarTodo, - SidebarFiles, - SidebarFooter, - PluginManager, - WhichKey, - ...(Flag.KILO_EXPERIMENTAL_EVENT_SYSTEM ? [SessionV2Debug] : []), -] +export function internalTuiPlugins(flags: Pick): InternalTuiPlugin[] { + return [ + HomeNews, // kilocode_change + HomeOnboarding, // kilocode_change + KiloHomeFooter, // kilocode_change + KiloSidebarFooter, // kilocode_change + KiloSidebarBackgroundProcesses, // kilocode_change + KiloSidebarIndexing, // kilocode_change + KiloSidebarPr, // kilocode_change + KiloSidebarUsage, // kilocode_change + HomeFooter, + HomeTips, + SidebarContext, + SidebarMcp, + SidebarLsp, + SidebarTodo, + SidebarFiles, + SidebarFooter, + Notifications, + PluginManager, + WhichKey, + ...(flags.experimentalEventSystem ? [SessionV2Debug] : []), + ] +} diff --git a/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts b/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts index 3d4f998c30f..028c10c891f 100644 --- a/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts +++ b/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts @@ -17,7 +17,7 @@ import { TuiConfig } from "@/cli/cmd/tui/config/tui" import * as Log from "@opencode-ai/core/util/log" import { errorData, errorMessage } from "@/util/error" import { isRecord } from "@/util/record" -import { WithInstance } from "@/project/with-instance" +import { resolveAttentionSoundPaths } from "../config/tui-schema" import { readPackageThemes, readPluginId, @@ -35,11 +35,13 @@ import { Filesystem } from "@/util/filesystem" import { Process } from "@/util/process" import { Flock } from "@opencode-ai/core/util/flock" import { Flag } from "@opencode-ai/core/flag/flag" -import { INTERNAL_TUI_PLUGINS, type InternalTuiPlugin } from "./internal" +import { internalTuiPlugins, type InternalTuiPlugin } from "./internal" import { setupSlots, Slot as View } from "./slots" import type { HostPluginApi, HostSlots } from "./slots" import { ConfigPlugin } from "@/config/plugin" import { createCommandShim } from "./command-shim" +import { RuntimeFlags } from "@/effect/runtime-flags" +import { Effect } from "effect" ensureRuntimePluginSupport({ additional: keymapRuntimeModules }) @@ -52,7 +54,7 @@ type PluginLoad = { id: string module: TuiPluginModule origin: ConfigPlugin.Origin - theme_root: string + plugin_root: string theme_files: string[] } @@ -107,6 +109,7 @@ const ScopedKeymapMethods = new Set([ type RuntimeState = { directory: string api: Api + dispose?: () => void slots: HostSlots plugins: PluginEntry[] plugins_by_id: Map @@ -157,6 +160,37 @@ function createScopedKeymap(keymap: TuiPluginApi["keymap"], scope: PluginScope): }) } +function createScopedAttention( + attention: TuiPluginApi["attention"], + scope: PluginScope, + root: string, +): TuiPluginApi["attention"] { + return { + notify(input) { + return attention.notify(input) + }, + soundboard: { + registerPack(pack) { + return scope.track( + attention.soundboard.registerPack({ + ...pack, + sounds: resolveAttentionSoundPaths(root, pack.sounds, { trim: true }), + }), + ) + }, + activate(id, options) { + return attention.soundboard.activate(id, options) + }, + current() { + return attention.soundboard.current() + }, + list() { + return attention.soundboard.list() + }, + }, + } +} + type CleanupResult = { type: "ok" } | { type: "error"; error: unknown } | { type: "timeout" } function runCleanup(fn: () => unknown, ms: number): Promise { @@ -205,8 +239,7 @@ function createThemeInstaller( plugin: PluginEntry, ): TuiTheme["install"] { return async (file) => { - const raw = file.startsWith("file://") ? fileURLToPath(file) : file - const src = path.isAbsolute(raw) ? raw : path.resolve(root, raw) + const src = Filesystem.resolveFilePath(root, file) const name = path.basename(src, path.extname(src)) const source_dir = path.dirname(meta.source) const local_dir = @@ -331,7 +364,7 @@ function loadInternalPlugin(item: InternalTuiPlugin): PluginLoad { scope: "global", source: target, }, - theme_root: process.cwd(), + plugin_root: process.cwd(), theme_files: [], } } @@ -353,7 +386,7 @@ async function readThemeFiles(spec: string, pkg?: PluginPackage) { async function syncPluginThemes(plugin: PluginEntry) { if (!plugin.load.theme_files.length) return if (plugin.meta.state === "same") return - const install = createThemeInstaller(plugin.load.origin, plugin.load.theme_root, plugin.load.spec, plugin) + const install = createThemeInstaller(plugin.load.origin, plugin.load.plugin_root, plugin.load.spec, plugin) for (const file of plugin.load.theme_files) { await install(file).catch((error) => { warn("failed to sync tui plugin oc-themes", { path: plugin.load.spec, id: plugin.id, theme: file, error }) @@ -553,7 +586,7 @@ function pluginApi(runtime: RuntimeState, plugin: PluginEntry, scope: PluginScop } const theme: TuiPluginApi["theme"] = Object.assign(Object.create(api.theme), { - install: createThemeInstaller(load.origin, load.theme_root, load.spec, plugin), + install: createThemeInstaller(load.origin, load.plugin_root, load.spec, plugin), }) const event: TuiPluginApi["event"] = { @@ -577,6 +610,7 @@ function pluginApi(runtime: RuntimeState, plugin: PluginEntry, scope: PluginScop return { app: api.app, + attention: createScopedAttention(api.attention, scope, load.plugin_root), // Keep deprecated `api.command` working for v1 plugins; remove in v2. command: createCommandShim(keymap, api.ui.dialog, api.tuiConfig.keybinds), keys: api.keys, @@ -683,7 +717,7 @@ async function resolveExternalPlugins(list: ConfigPlugin.Origin[], wait: () => P id, module: mod, origin, - theme_root: loaded.pkg?.dir ?? resolveRoot(loaded.target), + plugin_root: loaded.pkg?.dir ?? resolveRoot(loaded.target), theme_files, } }, @@ -710,7 +744,7 @@ async function resolveExternalPlugins(list: ConfigPlugin.Origin[], wait: () => P id, module: EMPTY_TUI, origin, - theme_root: loaded.pkg?.dir ?? resolveRoot(loaded.target), + plugin_root: loaded.pkg?.dir ?? resolveRoot(loaded.target), theme_files, } }, @@ -838,10 +872,7 @@ async function addPluginBySpec(state: RuntimeState | undefined, raw: string) { state.pending.delete(spec) return true } - const ready = await WithInstance.provide({ - directory: state.directory, - fn: () => resolveExternalPlugins([cfg], () => TuiConfig.waitForDependencies()), - }).catch((error) => { + const ready = await resolveExternalPlugins([cfg], () => TuiConfig.waitForDependencies()).catch((error) => { fail("failed to add tui plugin", { path: next, error }) return [] as PluginLoad[] }) @@ -971,7 +1002,7 @@ let loaded: Promise | undefined let runtime: RuntimeState | undefined export const Slot = View -export async function init(input: { api: HostPluginApi; config: TuiConfig.Resolved }) { +export async function init(input: { api: HostPluginApi; config: TuiConfig.Resolved; dispose?: () => void }) { const cwd = process.cwd() if (loaded) { if (dir !== cwd) { @@ -1018,15 +1049,17 @@ export async function dispose() { for (const plugin of queue) { await deactivatePluginEntry(state, plugin, false) } + state.dispose?.() } -async function load(input: { api: Api; config: TuiConfig.Resolved }) { +async function load(input: { api: Api; config: TuiConfig.Resolved; dispose?: () => void }) { const { api, config } = input const cwd = process.cwd() const slots = setupSlots(api) const next: RuntimeState = { directory: cwd, api, + dispose: input.dispose, slots, plugins: [], plugins_by_id: new Map(), @@ -1034,42 +1067,42 @@ async function load(input: { api: Api; config: TuiConfig.Resolved }) { } runtime = next try { - await WithInstance.provide({ - directory: cwd, - fn: async () => { - const records = Flag.KILO_PURE ? [] : (config.plugin_origins ?? []) - if (Flag.KILO_PURE && config.plugin_origins?.length) { - log.info("skipping external tui plugins in pure mode", { count: config.plugin_origins.length }) - } + const flags = await Effect.runPromise( + Effect.gen(function* () { + return yield* RuntimeFlags.Service + }).pipe(Effect.provide(RuntimeFlags.defaultLayer)), + ) + const records = Flag.KILO_PURE ? [] : (config.plugin_origins ?? []) + if (Flag.KILO_PURE && config.plugin_origins?.length) { + log.info("skipping external tui plugins in pure mode", { count: config.plugin_origins.length }) + } - for (const item of INTERNAL_TUI_PLUGINS) { - log.info("loading internal tui plugin", { id: item.id }) - const entry = loadInternalPlugin(item) - const meta = createMeta(entry.source, entry.spec, entry.target, undefined, entry.id) - addPluginEntry(next, { - id: entry.id, - load: entry, - meta, - themes: {}, - plugin: entry.module.tui, - enabled: item.enabled ?? true, - }) - } + for (const item of internalTuiPlugins(flags)) { + log.info("loading internal tui plugin", { id: item.id }) + const entry = loadInternalPlugin(item) + const meta = createMeta(entry.source, entry.spec, entry.target, undefined, entry.id) + addPluginEntry(next, { + id: entry.id, + load: entry, + meta, + themes: {}, + plugin: entry.module.tui, + enabled: item.enabled ?? true, + }) + } - const ready = await resolveExternalPlugins(records, () => TuiConfig.waitForDependencies()) - await addExternalPluginEntries(next, ready) - - applyInitialPluginEnabledState(next, config) - for (const plugin of next.plugins) { - if (!plugin.enabled) continue - // Keep plugin execution sequential for deterministic side effects: - // command registration order affects keybind/command precedence, - // route registration is last-wins when ids collide, - // and hook chains rely on stable plugin ordering. - await activatePluginEntry(next, plugin, false) - } - }, - }) + const ready = await resolveExternalPlugins(records, () => TuiConfig.waitForDependencies()) + await addExternalPluginEntries(next, ready) + + applyInitialPluginEnabledState(next, config) + for (const plugin of next.plugins) { + if (!plugin.enabled) continue + // Keep plugin execution sequential for deterministic side effects: + // command registration order affects keybind/command precedence, + // route registration is last-wins when ids collide, + // and hook chains rely on stable plugin ordering. + await activatePluginEntry(next, plugin, false) + } } catch (error) { fail("failed to load tui plugins", { directory: cwd, error }) } diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/footer.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/footer.tsx index 60bf5de92e6..c61a8fa029e 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/footer.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/footer.tsx @@ -8,23 +8,6 @@ import { createStore } from "solid-js/store" import { useRoute } from "../../context/route" import { useEvent } from "../../context/event" // kilocode_change import { RemoteIndicator } from "@/kilocode/remote-tui" // kilocode_change -import { formatIndexingLabel } from "@/kilocode/indexing-label" // kilocode_change -import type { IndexingStatusState } from "@kilocode/kilo-indexing/status" // kilocode_change -import { indexingEnabled } from "@/kilocode/indexing-feature" // kilocode_change - -// kilocode_change start -function indexingTone(state: IndexingStatusState, theme: ReturnType["theme"]) { - if (state === "Complete") return theme.success - if (state === "Error") return theme.error - if (state === "In Progress") return theme.warning - if (state === "Standby") return theme.textMuted - return theme.textMuted -} - -function indexingText(indexing: ReturnType["data"]["indexing"]) { - return formatIndexingLabel(indexing) -} -// kilocode_change end export function Footer() { const { theme } = useTheme() @@ -41,7 +24,6 @@ export function Footer() { const connected = useConnected() const sdk = useSDK() // kilocode_change const event = useEvent() // kilocode_change - const indexing = createMemo(() => sync.data.indexing) // kilocode_change const [store, setStore] = createStore({ welcome: false, @@ -114,9 +96,6 @@ export function Footer() { {mcp()} MCP - - {indexingText(indexing()).slice(0, 48)} - {/* kilocode_change end */} /status diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 34eca44992e..e299c6d7050 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -11,6 +11,7 @@ import { onMount, Show, Switch, + untrack, useContext, } from "solid-js" import { Dynamic } from "solid-js/web" @@ -68,7 +69,6 @@ import { DialogForkFromTimeline } from "./dialog-fork-from-timeline" import { DialogSessionRename } from "../../component/dialog-session-rename" import { Sidebar } from "./sidebar" import { SubagentFooter } from "./subagent-footer.tsx" -import { Flag } from "@opencode-ai/core/flag/flag" import { LANGUAGE_EXTENSIONS } from "@/lsp/language" import parsers from "../../../../../../parsers-config.ts" import * as Clipboard from "../../util/clipboard" @@ -98,7 +98,6 @@ import { session as banner } from "@/kilocode/cli/logo" import { formatMarkdownTables } from "../../util/markdown" import { bell } from "@/kilocode/bell" -import { SessionIndexing } from "@/kilocode/components/session-indexing" import { submitFeedback } from "@/kilocode/cli/cmd/tui/feedback" // kilocode_change end import { getScrollAcceleration } from "../../util/scroll" @@ -378,7 +377,7 @@ export function Session() { createEffect(() => { const sessionID = route.sessionID void (async () => { - const previousWorkspace = project.workspace.current() + const previousWorkspace = untrack(() => project.workspace.current()) const result = await sdk.client.session.get({ sessionID }, { throwOnError: true }) if (!result.data) { toast.show({ @@ -416,8 +415,8 @@ export function Session() { }) let lastSwitch: string | undefined = undefined - event.on("message.part.updated", (evt) => { - const part = evt.properties.part + event.onSync("message.part.updated.1", (evt) => { + const part = evt.data.part if (part.type !== "tool") return if (part.sessionID !== route.sessionID) return if (part.state.status !== "completed") return @@ -887,7 +886,7 @@ export function Session() { title: "Line up", value: "session.line.up", category: "Session", - enabled: false, + hidden: true, run: () => { scroll.scrollBy(-1) dialog.clear() @@ -897,7 +896,7 @@ export function Session() { title: "Line down", value: "session.line.down", category: "Session", - enabled: false, + hidden: true, run: () => { scroll.scrollBy(1) dialog.clear() @@ -1439,9 +1438,6 @@ export function Session() { {/* kilocode_change end */} - {/* kilocode_change start */} - - {/* kilocode_change end */} @@ -1732,29 +1728,15 @@ function TextPart(props: { last: boolean; part: TextPart; message: AssistantMess return ( - - - - - - - - + ) @@ -2212,11 +2194,11 @@ function WebFetch(props: ToolProps) { } function WebSearch(props: ToolProps) { - const metadata = props.metadata as { numResults?: number; provider?: unknown } + const metadata = () => props.metadata as { numResults?: number; provider?: unknown } return ( - {webSearchProviderLabel(metadata.provider)} "{props.input.query}"{" "} - ({metadata.numResults} results) + {webSearchProviderLabel(metadata().provider)} "{props.input.query}"{" "} + ({metadata().numResults} results) ) } @@ -2319,7 +2301,9 @@ function Task(props: ToolProps) { const content = createMemo(() => { if (!props.input.description) return "" - let content = [`${Locale.titlecase(props.input.subagent_type ?? "General")} Task — ${props.input.description}`] + const description = + props.metadata.background === true ? `${props.input.description} (background)` : props.input.description + let content = [`${Locale.titlecase(props.input.subagent_type ?? "General")} Task — ${description}`] // kilocode_change start if (isRunning()) { @@ -2336,7 +2320,11 @@ function Task(props: ToolProps) { // kilocode_change end if (props.part.state.status === "completed") { - content.push(`└ ${tools().length} toolcalls · ${Locale.duration(duration())}`) + content.push( + props.metadata.background === true + ? `└ ${tools().length} toolcalls` + : `└ ${tools().length} toolcalls · ${Locale.duration(duration())}`, + ) } return content.join("\n") diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/question.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/question.tsx index 86cafe97958..443d1d28cb4 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/question.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/question.tsx @@ -1,5 +1,6 @@ import { createStore } from "solid-js/store" import { createMemo, createSignal, For, Show } from "solid-js" +import { useRenderer } from "@opentui/solid" import type { TextareaRenderable } from "@opentui/core" import { selectedForeground, tint, useTheme } from "../../context/theme" import type { QuestionAnswer, QuestionRequest } from "@kilocode/sdk/v2" @@ -18,6 +19,7 @@ export function QuestionPrompt(props: { // kilocode_change end const sdk = useSDK() const { theme } = useTheme() + const renderer = useRenderer() const tuiConfig = useTuiConfig() const questions = createMemo(() => props.request.questions) @@ -309,7 +311,10 @@ export function QuestionPrompt(props: { } onMouseOver={() => setTabHover(index())} onMouseOut={() => setTabHover(null)} - onMouseUp={() => selectTab(index())} + onMouseUp={() => { + if (renderer.getSelection()?.getSelectedText()) return + selectTab(index()) + }} > setTabHover("confirm")} onMouseOut={() => setTabHover(null)} - onMouseUp={() => selectTab(questions().length)} + onMouseUp={() => { + if (renderer.getSelection()?.getSelectedText()) return + selectTab(questions().length) + }} > Confirm @@ -358,7 +366,10 @@ export function QuestionPrompt(props: { moveTo(i())} onMouseDown={() => moveTo(i())} - onMouseUp={() => selectOption()} + onMouseUp={() => { + if (renderer.getSelection()?.getSelectedText()) return + selectOption() + }} > @@ -387,7 +398,10 @@ export function QuestionPrompt(props: { moveTo(options().length)} onMouseDown={() => moveTo(options().length)} - onMouseUp={() => selectOption()} + onMouseUp={() => { + if (renderer.getSelection()?.getSelectedText()) return + selectOption() + }} > diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/subagent-footer.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/subagent-footer.tsx index 38a6537c924..9d9e0f812e0 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/subagent-footer.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/subagent-footer.tsx @@ -59,7 +59,7 @@ export function SubagentFooter() { const model = sync.data.provider.find((item) => item.id === last.providerID)?.models[last.modelID] const pct = model?.limit.context ? `${Math.round((tokens / model.limit.context) * 100)}%` : undefined - const cost = msg.reduce((sum, item) => sum + (item.role === "assistant" ? item.cost : 0), 0) + const cost = session()?.cost ?? 0 const money = new Intl.NumberFormat("en-US", { style: "currency", diff --git a/packages/opencode/src/cli/cmd/tui/util/audio.ts b/packages/opencode/src/cli/cmd/tui/util/audio.ts new file mode 100644 index 00000000000..7d7c3d5a422 --- /dev/null +++ b/packages/opencode/src/cli/cmd/tui/util/audio.ts @@ -0,0 +1,58 @@ +import { Audio, type AudioErrorContext, type AudioPlayOptions, type AudioSound, type AudioVoice } from "@opentui/core" +import * as Log from "@opencode-ai/core/util/log" + +const log = Log.create({ service: "tui.audio" }) + +let audio: Audio | null | undefined +const sounds = new Map>() + +function getAudio() { + if (audio !== undefined) return audio + try { + const next = Audio.create({ autoStart: false }) + next.on("error", (error: Error, context: AudioErrorContext) => { + log.debug("tui audio error", { error, context }) + }) + audio = next + return next + } catch (error) { + log.debug("failed to create tui audio", { error }) + audio = null + return null + } +} + +export function loadSoundFile(file: string) { + const current = getAudio() + if (!current) return Promise.resolve(null) + const cached = sounds.get(file) + if (cached) return cached + const task = Bun.file(file) + .bytes() + .then((bytes) => current.loadSound(bytes)) + .catch((error) => { + log.debug("failed to load tui sound", { file, error }) + return null + }) + sounds.set(file, task) + return task +} + +export function play(sound: AudioSound, options?: AudioPlayOptions) { + const current = getAudio() + if (!current) return null + if (!current.isStarted() && !current.start()) return null + return current.play(sound, options) +} + +export function stopVoice(voice: AudioVoice) { + return audio?.stopVoice(voice) ?? false +} + +export function dispose() { + audio?.dispose() + audio = undefined + sounds.clear() +} + +export * as TuiAudio from "./audio" diff --git a/packages/opencode/src/cli/cmd/tui/util/clipboard.ts b/packages/opencode/src/cli/cmd/tui/util/clipboard.ts index 3a9996902dd..be3cec14c6a 100644 --- a/packages/opencode/src/cli/cmd/tui/util/clipboard.ts +++ b/packages/opencode/src/cli/cmd/tui/util/clipboard.ts @@ -3,9 +3,21 @@ import { lazy } from "../../../../util/lazy.js" import { tmpdir } from "os" import path from "path" import fs from "fs/promises" +import { Effect } from "effect" +import { ChildProcess } from "effect/unstable/process" +import { AppProcess } from "@opencode-ai/core/process" import * as Filesystem from "../../../../util/filesystem" import * as Process from "../../../../util/process" +const writeWithStdin = (cmd: string[], text: string): Promise => + Effect.runPromise( + AppProcess.Service.use((svc) => svc.run(ChildProcess.make(cmd[0]!, cmd.slice(1)), { stdin: text })).pipe( + Effect.provide(AppProcess.defaultLayer), + Effect.catch(() => Effect.void), + Effect.asVoid, + ), + ).catch(() => undefined) + // Lazy load which and clipboardy to avoid expensive execa/which/isexe chain at startup const getWhich = lazy(async () => { const { which } = await import("../../../../util/which") @@ -125,49 +137,23 @@ const getCopyMethod = lazy(async () => { if (os === "linux") { if (process.env["WAYLAND_DISPLAY"] && which("wl-copy")) { console.log("clipboard: using wl-copy") - return async (text: string) => { - const proc = Process.spawn(["wl-copy"], { stdin: "pipe", stdout: "ignore", stderr: "ignore" }) - if (!proc.stdin) return - proc.stdin.write(text) - proc.stdin.end() - await proc.exited.catch(() => {}) - } + return (text: string) => writeWithStdin(["wl-copy"], text) } if (which("xclip")) { console.log("clipboard: using xclip") - return async (text: string) => { - const proc = Process.spawn(["xclip", "-selection", "clipboard"], { - stdin: "pipe", - stdout: "ignore", - stderr: "ignore", - }) - if (!proc.stdin) return - proc.stdin.write(text) - proc.stdin.end() - await proc.exited.catch(() => {}) - } + return (text: string) => writeWithStdin(["xclip", "-selection", "clipboard"], text) } if (which("xsel")) { console.log("clipboard: using xsel") - return async (text: string) => { - const proc = Process.spawn(["xsel", "--clipboard", "--input"], { - stdin: "pipe", - stdout: "ignore", - stderr: "ignore", - }) - if (!proc.stdin) return - proc.stdin.write(text) - proc.stdin.end() - await proc.exited.catch(() => {}) - } + return (text: string) => writeWithStdin(["xsel", "--clipboard", "--input"], text) } } if (os === "win32") { console.log("clipboard: using powershell") - return async (text: string) => { + return (text: string) => // Pipe via stdin to avoid PowerShell string interpolation ($env:FOO, $(), etc.) - const proc = Process.spawn( + writeWithStdin( [ "powershell.exe", "-NonInteractive", @@ -175,18 +161,8 @@ const getCopyMethod = lazy(async () => { "-Command", "[Console]::InputEncoding = [System.Text.Encoding]::UTF8; Set-Clipboard -Value ([Console]::In.ReadToEnd())", ], - { - stdin: "pipe", - stdout: "ignore", - stderr: "ignore", - }, + text, ) - - if (!proc.stdin) return - proc.stdin.write(text) - proc.stdin.end() - await proc.exited.catch(() => {}) - } } console.log("clipboard: no native support") diff --git a/packages/opencode/src/cli/cmd/tui/util/sound.ts b/packages/opencode/src/cli/cmd/tui/util/sound.ts deleted file mode 100644 index df8b4dc2d6e..00000000000 --- a/packages/opencode/src/cli/cmd/tui/util/sound.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { Player } from "cli-sound" -import { mkdirSync } from "node:fs" -import { tmpdir } from "node:os" -import { basename, join } from "node:path" -import { Process } from "@/util/process" -import { which } from "@/util/which" -import pulseA from "../asset/pulse-a.wav" with { type: "file" } -import pulseB from "../asset/pulse-b.wav" with { type: "file" } -import pulseC from "../asset/pulse-c.wav" with { type: "file" } -import charge from "../asset/charge.wav" with { type: "file" } - -const FILE = [pulseA, pulseB, pulseC] - -const HUM = charge -const DIR = join(tmpdir(), "opencode-sfx") - -const LIST = [ - "ffplay", - "mpv", - "mpg123", - "mpg321", - "mplayer", - "afplay", - "play", - "omxplayer", - "aplay", - "cmdmp3", - "cvlc", - "powershell.exe", -] as const - -type Kind = (typeof LIST)[number] - -function args(kind: Kind, file: string, volume: number) { - if (kind === "ffplay") return [kind, "-autoexit", "-nodisp", "-af", `volume=${volume}`, file] - if (kind === "mpv") - return [kind, "--no-video", "--audio-display=no", "--volume", String(Math.round(volume * 100)), file] - if (kind === "mpg123" || kind === "mpg321") return [kind, "-g", String(Math.round(volume * 100)), file] - if (kind === "mplayer") return [kind, "-vo", "null", "-volume", String(Math.round(volume * 100)), file] - if (kind === "afplay" || kind === "omxplayer" || kind === "aplay" || kind === "cmdmp3") return [kind, file] - if (kind === "play") return [kind, "-v", String(volume), file] - if (kind === "cvlc") return [kind, `--gain=${volume}`, "--play-and-exit", file] - return [kind, "-c", `(New-Object Media.SoundPlayer '${file.replace(/'/g, "''")}').PlaySync()`] -} - -let item: Player | null | undefined -let kind: Kind | null | undefined -let proc: Process.Child | undefined -let tail: ReturnType | undefined -let cache: Promise<{ hum: string; pulse: string[] }> | undefined -let seq = 0 -let shot = 0 - -function load() { - if (item !== undefined) return item - try { - item = new Player({ volume: 0.35 }) - } catch { - item = null - } - return item -} - -async function file(path: string) { - mkdirSync(DIR, { recursive: true }) - const next = join(DIR, basename(path)) - const out = Bun.file(next) - if (await out.exists()) return next - await Bun.write(out, Bun.file(path)) - return next -} - -function asset() { - cache ??= Promise.all([file(HUM), Promise.all(FILE.map(file))]).then(([hum, pulse]) => ({ hum, pulse })) - return cache -} - -function pick() { - if (kind !== undefined) return kind - kind = LIST.find((item) => which(item)) ?? null - return kind -} - -function run(file: string, volume: number) { - const kind = pick() - if (!kind) return - return Process.spawn(args(kind, file, volume), { - stdin: "ignore", - stdout: "ignore", - stderr: "ignore", - }) -} - -function clear() { - if (!tail) return - clearTimeout(tail) - tail = undefined -} - -function play(file: string, volume: number) { - const item = load() - if (!item) return run(file, volume)?.exited - return item.play(file, { volume }).catch(() => run(file, volume)?.exited) -} - -export function start() { - stop() - const id = ++seq - void asset().then(({ hum }) => { - if (id !== seq) return - const next = run(hum, 0.24) - if (!next) return - proc = next - void next.exited.then( - () => { - if (id !== seq) return - if (proc === next) proc = undefined - }, - () => { - if (id !== seq) return - if (proc === next) proc = undefined - }, - ) - }) -} - -export function stop(delay = 0) { - seq++ - clear() - if (!proc) return - const next = proc - if (delay <= 0) { - proc = undefined - void Process.stop(next).catch(() => undefined) - return - } - tail = setTimeout(() => { - tail = undefined - if (proc === next) proc = undefined - void Process.stop(next).catch(() => undefined) - }, delay) -} - -export function pulse(scale = 1) { - stop(140) - const index = shot++ % FILE.length - void asset() - .then(({ pulse }) => play(pulse[index], 0.26 + 0.14 * scale)) - .catch(() => undefined) -} - -export function dispose() { - stop() -} - -export * as Sound from "./sound" diff --git a/packages/opencode/src/cli/error.ts b/packages/opencode/src/cli/error.ts index 8b5097c1828..c3bbc9968e1 100644 --- a/packages/opencode/src/cli/error.ts +++ b/packages/opencode/src/cli/error.ts @@ -1,17 +1,42 @@ import { NamedError } from "@opencode-ai/core/util/error" import { errorFormat } from "@/util/error" +import { isRecord } from "@/util/record" interface ErrorLike { name?: string _tag?: string message?: string - data?: Record + data?: Record } +type ConfigIssue = { message: string; path: string[] } + function isTaggedError(error: unknown, tag: string): boolean { - return ( - typeof error === "object" && error !== null && "_tag" in error && (error as Record)._tag === tag - ) + return isRecord(error) && error._tag === tag +} + +function configData(input: unknown, tag: string): Record | undefined { + if (!isRecord(input)) return undefined + if (input.name === tag && isRecord(input.data)) return input.data + if (input._tag === tag) return input + return undefined +} + +function stringField(input: Record, key: string): string | undefined { + return typeof input[key] === "string" ? input[key] : undefined +} + +function configIssues(input: Record): ConfigIssue[] { + return Array.isArray(input.issues) + ? input.issues.filter((issue): issue is ConfigIssue => { + if (!isRecord(issue)) return false + return ( + typeof issue.message === "string" && + Array.isArray(issue.path) && + issue.path.every((x) => typeof x === "string") + ) + }) + : [] } export function FormatError(input: unknown) { @@ -33,11 +58,13 @@ export function FormatError(input: unknown) { } // ProviderModelNotFoundError: { providerID: string, modelID: string, suggestions?: string[] } - if (NamedError.hasName(input, "ProviderModelNotFoundError")) { - const data = (input as ErrorLike).data - const suggestions: string[] = Array.isArray(data?.suggestions) ? data.suggestions : [] + const providerModelNotFound = configData(input, "ProviderModelNotFoundError") + if (providerModelNotFound) { + const suggestions = Array.isArray(providerModelNotFound.suggestions) + ? providerModelNotFound.suggestions.filter((x) => typeof x === "string") + : [] return [ - `Model not found: ${data?.providerID}/${data?.modelID}`, + `Model not found: ${providerModelNotFound.providerID}/${providerModelNotFound.modelID}`, ...(suggestions.length ? ["Did you mean: " + suggestions.join(", ")] : []), `Try: \`kilo models\` to list available models`, // kilocode_change `Or check your config (opencode.json) provider/model names`, @@ -45,41 +72,44 @@ export function FormatError(input: unknown) { } // ProviderInitError: { providerID: string } - if (NamedError.hasName(input, "ProviderInitError")) { - return `Failed to initialize provider "${(input as ErrorLike).data?.providerID}". Check credentials and configuration.` + const providerInit = configData(input, "ProviderInitError") + if (providerInit) { + return `Failed to initialize provider "${stringField(providerInit, "providerID")}". Check credentials and configuration.` } // ConfigJsonError: { path: string, message?: string } - if (NamedError.hasName(input, "ConfigJsonError")) { - const data = (input as ErrorLike).data - return `Config file at ${data?.path} is not valid JSON(C)` + (data?.message ? `: ${data.message}` : "") + const configJson = configData(input, "ConfigJsonError") + if (configJson) { + const message = stringField(configJson, "message") + return `Config file at ${stringField(configJson, "path")} is not valid JSON(C)` + (message ? `: ${message}` : "") } // ConfigDirectoryTypoError: { dir: string, path: string, suggestion: string } - if (NamedError.hasName(input, "ConfigDirectoryTypoError")) { - const data = (input as ErrorLike).data - return `Directory "${data?.dir}" in ${data?.path} is not valid. Rename the directory to "${data?.suggestion}" or remove it. This is a common typo.` + const configDirectoryTypo = configData(input, "ConfigDirectoryTypoError") + if (configDirectoryTypo) { + return `Directory "${stringField(configDirectoryTypo, "dir")}" in ${stringField(configDirectoryTypo, "path")} is not valid. Rename the directory to "${stringField(configDirectoryTypo, "suggestion")}" or remove it. This is a common typo.` } // ConfigFrontmatterError: { message: string } - if (NamedError.hasName(input, "ConfigFrontmatterError")) { - return (input as ErrorLike).data?.message ?? "" + const configFrontmatter = configData(input, "ConfigFrontmatterError") + if (configFrontmatter) { + return stringField(configFrontmatter, "message") ?? "" } // ConfigInvalidError: { path?: string, message?: string, issues?: Array<{ message: string, path: string[] }> } - if (NamedError.hasName(input, "ConfigInvalidError")) { - const data = (input as ErrorLike).data - const path = data?.path - const message = data?.message - const issues: Array<{ message: string; path: string[] }> = Array.isArray(data?.issues) ? data.issues : [] + const configInvalid = configData(input, "ConfigInvalidError") + if (configInvalid) { + const path = stringField(configInvalid, "path") + const message = stringField(configInvalid, "message") + const issues = configIssues(configInvalid) return [ `Configuration is invalid${path && path !== "config" ? ` at ${path}` : ""}` + (message ? `: ${message}` : ""), ...issues.map((issue) => "↳ " + issue.message + " " + issue.path.join(".")), ].join("\n") } - // UICancelledError: void (no data) - if (NamedError.hasName(input, "UICancelledError")) { + // UICancelledError: user cancelled an interactive CLI prompt + if (isTaggedError(input, "UICancelledError") || NamedError.hasName(input, "UICancelledError")) { return "" } } diff --git a/packages/opencode/src/cli/ui.ts b/packages/opencode/src/cli/ui.ts index 1d238027394..5805469381c 100644 --- a/packages/opencode/src/cli/ui.ts +++ b/packages/opencode/src/cli/ui.ts @@ -1,6 +1,5 @@ -import z from "zod" import { EOL } from "os" -import { NamedError } from "@opencode-ai/core/util/error" +import { Schema } from "effect" import { logo as glyphs } from "./logo" import { tui } from "../kilocode/cli/logo" // kilocode_change @@ -8,7 +7,7 @@ import { tui } from "../kilocode/cli/logo" // kilocode_change const wordmark = [...tui()] // kilocode_change end -export const CancelledError = NamedError.create("UICancelledError", z.void()) +export class CancelledError extends Schema.TaggedErrorClass()("UICancelledError", {}) {} export const Style = { TEXT_HIGHLIGHT: "\x1b[96m", diff --git a/packages/opencode/src/command/index.ts b/packages/opencode/src/command/index.ts index 2e9f76e031a..1c92d2aa20c 100644 --- a/packages/opencode/src/command/index.ts +++ b/packages/opencode/src/command/index.ts @@ -4,9 +4,6 @@ import { EffectBridge } from "@/effect/bridge" import type { InstanceContext } from "@/project/instance" import { SessionID, MessageID } from "@/session/schema" import { Effect, Layer, Context, Schema } from "effect" -import z from "zod" -import { zod, ZodOverride } from "@opencode-ai/core/effect-zod" -import { withStatics } from "@opencode-ai/core/schema" import { Config } from "@/config/config" import { MCP } from "../mcp" import { Skill } from "../skill" @@ -37,14 +34,11 @@ export const Info = Schema.Struct({ model: Schema.optional(Schema.String), source: Schema.optional(Schema.Literals(["command", "mcp", "skill"])), // Some command templates are lazy promises from MCP prompt resolution. - template: Schema.Unknown.annotate({ [ZodOverride]: z.promise(z.string()).or(z.string()) }), + template: Schema.Unknown, subtask: Schema.optional(Schema.Boolean), hints: Schema.Array(Schema.String), -}) - .annotate({ identifier: "Command" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "Command" }) -// for some reason zod is inferring `string` for z.promise(z.string()).or(z.string()) so we have to manually override it export type Info = Omit, "template"> & { template: Promise | string } export function hints(template: string) { diff --git a/packages/opencode/src/config/agent.ts b/packages/opencode/src/config/agent.ts index d4bcb1d96a8..2ff636a1663 100644 --- a/packages/opencode/src/config/agent.ts +++ b/packages/opencode/src/config/agent.ts @@ -3,8 +3,7 @@ export * as ConfigAgent from "./agent" import path from "path" // kilocode_change import { Exit, Schema, SchemaGetter } from "effect" import { Bus } from "@/bus" -import { zod } from "@opencode-ai/core/effect-zod" -import { PositiveInt, withStatics } from "@opencode-ai/core/schema" +import { PositiveInt } from "@opencode-ai/core/schema" import * as Log from "@opencode-ai/core/util/log" import { NamedError } from "@opencode-ai/core/util/error" import { Glob } from "@opencode-ai/core/util/glob" @@ -119,9 +118,7 @@ export const Info = AgentSchema.pipe( decode: SchemaGetter.transform(normalize), encode: SchemaGetter.passthrough({ strict: false }), }), -) - .annotate({ identifier: "AgentConfig" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +).annotate({ identifier: "AgentConfig" }) export type Info = Schema.Schema.Type // kilocode_change start @@ -183,7 +180,7 @@ export async function load(dir: string, warnings?: Warning[]) { // kilocode_change end // kilocode_change start - use Effect schema (propertyOrder: original) + non-fatal handleInvalid try { - result[config.name] = ConfigParse.effectSchema(Info, config, item) as Info + result[config.name] = ConfigParse.schema(Info, config, item) as Info } catch (err) { if (ConfigError.InvalidError.isInstance(err)) { await KilocodeConfig.handleInvalid("agent", item, err.data.issues ?? [], err, warnings) @@ -232,7 +229,7 @@ export async function loadMode(dir: string, warnings?: Warning[]) { // kilocode_change start - use Effect schema (propertyOrder: original) + non-fatal handleInvalid try { result[config.name] = { - ...(ConfigParse.effectSchema(Info, config, item) as Info), + ...(ConfigParse.schema(Info, config, item) as Info), mode: "primary" as const, } } catch (err) { diff --git a/packages/opencode/src/config/attachment.ts b/packages/opencode/src/config/attachment.ts index 7af429afdee..80e44bc2e4f 100644 --- a/packages/opencode/src/config/attachment.ts +++ b/packages/opencode/src/config/attachment.ts @@ -1,8 +1,7 @@ export * as ConfigAttachment from "./attachment" import { Schema } from "effect" -import { zod } from "@opencode-ai/core/effect-zod" -import { PositiveInt, withStatics } from "@opencode-ai/core/schema" +import { PositiveInt } from "@opencode-ai/core/schema" export const Image = Schema.Struct({ auto_resize: Schema.optional(Schema.Boolean).annotate({ @@ -15,16 +14,12 @@ export const Image = Schema.Struct({ description: "Maximum image height before resizing or rejecting the attachment (default: 2000)", }), max_base64_bytes: Schema.optional(PositiveInt).annotate({ - description: "Maximum base64 payload bytes for an image attachment (default: 4718592)", + description: "Maximum base64 payload bytes for an image attachment (default: 5242880)", }), -}) - .annotate({ identifier: "ImageAttachmentConfig" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "ImageAttachmentConfig" }) export type Image = Schema.Schema.Type export const Info = Schema.Struct({ image: Schema.optional(Image).annotate({ description: "Image attachment configuration" }), -}) - .annotate({ identifier: "AttachmentConfig" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "AttachmentConfig" }) export type Info = Schema.Schema.Type diff --git a/packages/opencode/src/config/command.ts b/packages/opencode/src/config/command.ts index b992aa83c3e..92a044813e3 100644 --- a/packages/opencode/src/config/command.ts +++ b/packages/opencode/src/config/command.ts @@ -1,12 +1,10 @@ export * as ConfigCommand from "./command" import * as Log from "@opencode-ai/core/util/log" -import { Schema } from "effect" +import { Cause, Exit, Schema, SchemaIssue } from "effect" import { NamedError } from "@opencode-ai/core/util/error" import { Glob } from "@opencode-ai/core/util/glob" import { Bus } from "@/bus" -import { zod } from "@opencode-ai/core/effect-zod" -import { withStatics } from "@opencode-ai/core/schema" import { configEntryNameFromPath } from "./entry-name" import * as ConfigMarkdown from "./markdown" import { ConfigModelID } from "./model-id" @@ -23,10 +21,12 @@ export const Info = Schema.Struct({ agent: Schema.optional(Schema.String), model: Schema.optional(ConfigModelID), subtask: Schema.optional(Schema.Boolean), -}).pipe(withStatics((s) => ({ zod: zod(s) }))) +}) export type Info = Schema.Schema.Type +const decodeInfo = Schema.decodeUnknownExit(Info) + // kilocode_change start export async function load(dir: string, warnings?: Warning[]) { // kilocode_change end @@ -74,13 +74,22 @@ export async function load(dir: string, warnings?: Warning[]) { ...md.data, template: md.content.trim(), } - const parsed = Info.zod.safeParse(config) - if (parsed.success) { - result[config.name] = parsed.data + const parsed = decodeInfo(config, { errors: "all", propertyOrder: "original" }) + if (Exit.isSuccess(parsed)) { + result[config.name] = parsed.value continue } // kilocode_change start - await KilocodeConfig.handleInvalid("command", item, parsed.error.issues, parsed.error, warnings) + const error = Cause.squash(parsed.cause) + const issues = Schema.isSchemaError(error) + ? SchemaIssue.makeFormatterStandardSchemaV1()(error.issue).issues.map((issue) => ({ + ...issue, + message: issue.message, + path: issue.path?.map(String) ?? [], + })) + : [{ message: String(error), path: [] }] + const cause = error instanceof Error ? error : new Error(String(error)) + await KilocodeConfig.handleInvalid("command", item, issues, cause, warnings) // kilocode_change end } return result diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index a54acf89a38..cc3c38a99f7 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -2,7 +2,6 @@ import * as Log from "@opencode-ai/core/util/log" import path from "path" import { pathToFileURL } from "url" import os from "os" -import z from "zod" import { mergeDeep } from "remeda" import { Global } from "@opencode-ai/core/global" import fsNode from "fs/promises" @@ -26,8 +25,7 @@ import { InstanceState } from "@/effect/instance-state" import { Context, Duration, Effect, Exit, Fiber, Layer, Option, Schema } from "effect" import { EffectFlock } from "@opencode-ai/core/util/effect-flock" import { containsPath } from "../project/instance-context" -import { zod } from "@opencode-ai/core/effect-zod" -import { NonNegativeInt, PositiveInt, withStatics, type DeepMutable } from "@opencode-ai/core/schema" +import { NonNegativeInt, PositiveInt, type DeepMutable } from "@opencode-ai/core/schema" import { ConfigAgent } from "./agent" import { ConfigAttachment } from "./attachment" import { ConfigCommand } from "./command" @@ -47,6 +45,7 @@ import { ConfigServer } from "./server" import { ConfigSkills } from "./skills" import { ConfigVariable } from "./variable" import { Npm } from "@opencode-ai/core/npm" +import z from "zod" // kilocode_change - Kilo config compatibility schemas // kilocode_change start import { ZodOverride } from "@opencode-ai/core/effect-zod" import { KilocodeConfig } from "../kilocode/config/config" @@ -138,8 +137,6 @@ async function resolveLoadedPlugins( return config } -export const Server = ConfigServer.Server.zod -export const Layout = ConfigLayout.Layout.zod export type Layout = ConfigLayout.Layout // kilocode_change start - indexing configuration @@ -155,14 +152,6 @@ const Percent = Schema.Number.check(Schema.isGreaterThan(0), Schema.isLessThanOr const IndexingRef = KiloIndexingSchema.annotate({ [ZodOverride]: KiloIndexingConfig }) // kilocode_change -// The Effect Schema is the canonical source of truth. The `.zod` compatibility -// surface is derived from it so plugin/SDK Zod consumers keep working without -// a parallel hand-maintained Zod definition. -// -// The walker emits `z.object({...})` which is non-strict by default. Config -// historically uses `.strict()` (additionalProperties: false in openapi.json), -// so layer that on after derivation. Re-apply the Config ref afterward -// since `.strict()` strips the walker's meta annotation. export const Info = Schema.Struct({ $schema: Schema.optional(Schema.String).annotate({ description: "JSON schema reference for configuration validation", @@ -179,7 +168,7 @@ export const Info = Schema.Struct({ }), skills: Schema.optional(ConfigSkills.Info).annotate({ description: "Additional skill folder paths" }), reference: Schema.optional(ConfigReference.Info).annotate({ - description: "Named git or local directory references that can be @ mentioned as Scout-backed subagents", + description: "Named git or local directory references that can be mentioned as @alias or @alias/path", }), watcher: Schema.optional( Schema.Struct({ @@ -377,15 +366,7 @@ export const Info = Schema.Struct({ }), }), ), -}) - .annotate({ identifier: "Config" }) - .pipe( - withStatics((s) => ({ - zod: (zod(s) as unknown as z.ZodObject).strict().meta({ ref: "Config" }) as unknown as z.ZodType< - DeepMutable> - >, - })), - ) +}).annotate({ identifier: "Config" }) // Uses the shared `DeepMutable` from `@opencode-ai/core/schema`. See the definition // there for why the local variant is needed over `Types.DeepMutable` from @@ -482,14 +463,11 @@ function writableGlobal(info: Info) { return next } -export const ConfigDirectoryTypoError = NamedError.create( - "ConfigDirectoryTypoError", - z.object({ - path: z.string(), - dir: z.string(), - suggestion: z.string(), - }), -) +export const ConfigDirectoryTypoError = NamedError.create("ConfigDirectoryTypoError", { + path: Schema.String, + dir: Schema.String, + suggestion: Schema.String, +}) export const layer = Layer.effect( Service, @@ -513,7 +491,7 @@ export const layer = Layer.effect( ), ) const parsed = ConfigParse.jsonc(expanded, source) - const data = ConfigParse.effectSchema(Info, normalizeLoadedConfig(parsed, source), source) + const data = ConfigParse.schema(Info, normalizeLoadedConfig(parsed, source), source) if (!("path" in options)) return data yield* Effect.promise(() => resolveLoadedPlugins(data, options.path)) @@ -542,6 +520,16 @@ export const layer = Layer.effect( globalStamp = yield* KilocodeGlobalConfigStamp.read(fs, Global.Path.config) // kilocode_change end let result: Info = {} + // Seed the default global config with the schema for editor completion, but avoid writing when the user + // explicitly routes config through env-provided paths or content. + if (!Flag.KILO_CONFIG && !Flag.KILO_CONFIG_DIR && !Flag.KILO_CONFIG_CONTENT) { + const file = globalConfigFile() + if (!existsSync(file)) { + yield* fs + .writeWithDirs(file, JSON.stringify({ $schema: "https://app.kilo.ai/config.json" }, null, 2)) + .pipe(Effect.catch(() => Effect.void)) + } + } result = mergeConfig(result, yield* loadFile(path.join(Global.Path.config, "config.json"))) // kilocode_change start result = mergeConfig(result, yield* loadFile(path.join(Global.Path.config, "kilo.json"))) @@ -566,6 +554,7 @@ export const layer = Layer.effect( ) } + globalStamp = yield* KilocodeGlobalConfigStamp.read(fs, Global.Path.config) // kilocode_change return result }) @@ -1040,7 +1029,7 @@ export const layer = Layer.effect( worktree: ctx.worktree, config, read: readConfigFile, - parse: (input, file) => ConfigParse.schema(Info.zod, ConfigParse.jsonc(input, file), file), + parse: (input, file) => ConfigParse.schema(Info, ConfigParse.jsonc(input, file), file), patch: (input, patch) => patchJsonc(input, patch), writable, }) @@ -1076,7 +1065,7 @@ export const layer = Layer.effect( let next: Info let changed: boolean if (!file.endsWith(".jsonc")) { - const existing = ConfigParse.effectSchema(Info, ConfigParse.jsonc(before, file), file) + const existing = ConfigParse.schema(Info, ConfigParse.jsonc(before, file), file) const merged = KilocodeConfig.mergeConfig(writable(existing), patch) // kilocode_change const serialized = JSON.stringify(merged, null, 2) changed = serialized !== before @@ -1084,7 +1073,7 @@ export const layer = Layer.effect( next = merged } else { const updated = patchJsonc(before, patch) - next = ConfigParse.effectSchema(Info, ConfigParse.jsonc(updated, file), file) + next = ConfigParse.schema(Info, ConfigParse.jsonc(updated, file), file) changed = updated !== before if (changed) yield* fs.writeFileString(file, updated).pipe(Effect.orDie) } diff --git a/packages/opencode/src/config/console-state.ts b/packages/opencode/src/config/console-state.ts index 485e334167a..d52a148409e 100644 --- a/packages/opencode/src/config/console-state.ts +++ b/packages/opencode/src/config/console-state.ts @@ -1,14 +1,11 @@ import { Schema } from "effect" -import { zod } from "@opencode-ai/core/effect-zod" import { NonNegativeInt } from "@opencode-ai/core/schema" export class ConsoleState extends Schema.Class("ConsoleState")({ consoleManagedProviders: Schema.mutable(Schema.Array(Schema.String)), activeOrgName: Schema.optional(Schema.String), switchableOrgCount: NonNegativeInt, -}) { - static readonly zod = zod(this) -} +}) {} export const emptyConsoleState: ConsoleState = ConsoleState.make({ consoleManagedProviders: [], diff --git a/packages/opencode/src/config/error.ts b/packages/opencode/src/config/error.ts index c43598048a5..17d74fc1c3e 100644 --- a/packages/opencode/src/config/error.ts +++ b/packages/opencode/src/config/error.ts @@ -1,21 +1,23 @@ export * as ConfigError from "./error" -import z from "zod" import { NamedError } from "@opencode-ai/core/util/error" +import { Schema } from "effect" -export const JsonError = NamedError.create( - "ConfigJsonError", - z.object({ - path: z.string(), - message: z.string().optional(), +const Issue = Schema.StructWithRest( + Schema.Struct({ + message: Schema.String, + path: Schema.Array(Schema.String), }), + [Schema.Record(Schema.String, Schema.Unknown)], ) -export const InvalidError = NamedError.create( - "ConfigInvalidError", - z.object({ - path: z.string(), - issues: z.custom().optional(), - message: z.string().optional(), - }), -) +export const JsonError = NamedError.create("ConfigJsonError", { + path: Schema.String, + message: Schema.optional(Schema.String), +}) + +export const InvalidError = NamedError.create("ConfigInvalidError", { + path: Schema.String, + issues: Schema.optional(Schema.Array(Issue)), + message: Schema.optional(Schema.String), +}) diff --git a/packages/opencode/src/config/formatter.ts b/packages/opencode/src/config/formatter.ts index 222a750057c..7539fe4a771 100644 --- a/packages/opencode/src/config/formatter.ts +++ b/packages/opencode/src/config/formatter.ts @@ -1,17 +1,13 @@ export * as ConfigFormatter from "./formatter" import { Schema } from "effect" -import { zod } from "@opencode-ai/core/effect-zod" -import { withStatics } from "@opencode-ai/core/schema" export const Entry = Schema.Struct({ disabled: Schema.optional(Schema.Boolean), command: Schema.optional(Schema.mutable(Schema.Array(Schema.String))), environment: Schema.optional(Schema.Record(Schema.String, Schema.String)), extensions: Schema.optional(Schema.mutable(Schema.Array(Schema.String))), -}).pipe(withStatics((s) => ({ zod: zod(s) }))) +}) -export const Info = Schema.Union([Schema.Boolean, Schema.Record(Schema.String, Entry)]).pipe( - withStatics((s) => ({ zod: zod(s) })), -) +export const Info = Schema.Union([Schema.Boolean, Schema.Record(Schema.String, Entry)]) export type Info = Schema.Schema.Type diff --git a/packages/opencode/src/config/layout.ts b/packages/opencode/src/config/layout.ts index a5299ea9557..3ac63576dd7 100644 --- a/packages/opencode/src/config/layout.ts +++ b/packages/opencode/src/config/layout.ts @@ -1,10 +1,6 @@ import { Schema } from "effect" -import { zod } from "@opencode-ai/core/effect-zod" -import { withStatics } from "@opencode-ai/core/schema" -export const Layout = Schema.Literals(["auto", "stretch"]) - .annotate({ identifier: "LayoutConfig" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +export const Layout = Schema.Literals(["auto", "stretch"]).annotate({ identifier: "LayoutConfig" }) export type Layout = Schema.Schema.Type export * as ConfigLayout from "./layout" diff --git a/packages/opencode/src/config/lsp.ts b/packages/opencode/src/config/lsp.ts index accfbee3b2f..ea7328a809a 100644 --- a/packages/opencode/src/config/lsp.ts +++ b/packages/opencode/src/config/lsp.ts @@ -1,13 +1,11 @@ export * as ConfigLSP from "./lsp" import { Schema } from "effect" -import { zod } from "@opencode-ai/core/effect-zod" -import { withStatics } from "@opencode-ai/core/schema" import * as LSPServer from "../lsp/server" export const Disabled = Schema.Struct({ disabled: Schema.Literal(true), -}).pipe(withStatics((s) => ({ zod: zod(s) }))) +}).pipe((schema) => schema) export const Entry = Schema.Union([ Disabled, @@ -18,7 +16,7 @@ export const Entry = Schema.Union([ env: Schema.optional(Schema.Record(Schema.String, Schema.String)), initialization: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)), }), -]).pipe(withStatics((s) => ({ zod: zod(s) }))) +]).pipe((schema) => schema) /** * For custom (non-builtin) LSP server entries, `extensions` is required so the @@ -40,6 +38,6 @@ export const requiresExtensionsForCustomServers = Schema.makeFilter< export const Info = Schema.Union([Schema.Boolean, Schema.Record(Schema.String, Entry)]) .check(requiresExtensionsForCustomServers) - .pipe(withStatics((s) => ({ zod: zod(s) }))) + .pipe((schema) => schema) export type Info = Schema.Schema.Type diff --git a/packages/opencode/src/config/markdown.ts b/packages/opencode/src/config/markdown.ts index 7708af95add..02081e6bbdb 100644 --- a/packages/opencode/src/config/markdown.ts +++ b/packages/opencode/src/config/markdown.ts @@ -1,6 +1,6 @@ import { NamedError } from "@opencode-ai/core/util/error" import matter from "gray-matter" -import { z } from "zod" +import { Schema } from "effect" import { Filesystem } from "@/util/filesystem" import { KilocodeMarkdown } from "../kilocode/config/markdown" // kilocode_change @@ -95,13 +95,10 @@ export async function parse(filePath: string) { } // kilocode_change start - export structured frontmatter parse errors -export const FrontmatterError = NamedError.create( - "ConfigFrontmatterError", - z.object({ - path: z.string(), - message: z.string(), - }), -) +export const FrontmatterError = NamedError.create("ConfigFrontmatterError", { + path: Schema.String, + message: Schema.String, +}) // kilocode_change end // kilocode_change start - export helpers as namespace object diff --git a/packages/opencode/src/config/mcp.ts b/packages/opencode/src/config/mcp.ts index 1a359ecd909..d74fc20deff 100644 --- a/packages/opencode/src/config/mcp.ts +++ b/packages/opencode/src/config/mcp.ts @@ -64,9 +64,7 @@ export const OAuth = Schema.Struct({ redirectUri: Schema.optional(Schema.String).annotate({ description: "OAuth redirect URI (default: http://127.0.0.1:19876/mcp/oauth/callback).", }), -}) - .annotate({ identifier: "McpOAuthConfig" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "McpOAuthConfig" }) export type OAuth = Schema.Schema.Type export const Remote = Schema.Struct({ @@ -84,14 +82,10 @@ export const Remote = Schema.Struct({ timeout: Schema.optional(PositiveInt).annotate({ description: "Timeout in ms for MCP server requests. Defaults to 5000 (5 seconds) if not specified.", }), -}) - .annotate({ identifier: "McpRemoteConfig" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "McpRemoteConfig" }) export type Remote = Schema.Schema.Type -export const Info = Schema.Union([Local, Remote]) - .annotate({ discriminator: "type" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +export const Info = Schema.Union([Local, Remote]).annotate({ discriminator: "type" }) export type Info = Schema.Schema.Type export * as ConfigMCP from "./mcp" diff --git a/packages/opencode/src/config/model-id.ts b/packages/opencode/src/config/model-id.ts index 26fa2e0b346..ba763f99913 100644 --- a/packages/opencode/src/config/model-id.ts +++ b/packages/opencode/src/config/model-id.ts @@ -1,14 +1,5 @@ import { Schema } from "effect" -import z from "zod" -import { zod, ZodOverride } from "@opencode-ai/core/effect-zod" -import { withStatics } from "@opencode-ai/core/schema" -// The original Zod schema carried an external $ref pointing at the models.dev -// JSON schema. That external reference is not a named SDK component — it is a -// literal pointer to an outside schema — so the walker cannot re-derive it -// from AST metadata. Preserve the exact original Zod via ZodOverride. -export const ConfigModelID = Schema.String.annotate({ - [ZodOverride]: z.string().meta({ $ref: "https://models.dev/model-schema.json#/$defs/Model" }), -}).pipe(withStatics((s) => ({ zod: zod(s) }))) +export const ConfigModelID = Schema.String export type ConfigModelID = Schema.Schema.Type diff --git a/packages/opencode/src/config/parse.ts b/packages/opencode/src/config/parse.ts index f964ed4e150..90e96334fc9 100644 --- a/packages/opencode/src/config/parse.ts +++ b/packages/opencode/src/config/parse.ts @@ -2,12 +2,9 @@ export * as ConfigParse from "./parse" import { type ParseError as JsoncParseError, parse as parseJsoncImpl, printParseErrorCode } from "jsonc-parser" import { Cause, Exit, Schema as EffectSchema, SchemaIssue } from "effect" -import z from "zod" import type { DeepMutable } from "@opencode-ai/core/schema" import { InvalidError, JsonError } from "./error" -type ZodSchema = z.ZodType - export function jsonc(text: string, filepath: string): unknown { const errors: JsoncParseError[] = [] const data = parseJsoncImpl(text, errors, { allowTrailingComma: true }) @@ -35,17 +32,7 @@ export function jsonc(text: string, filepath: string): unknown { return data } -export function schema(schema: ZodSchema, data: unknown, source: string): T { - const parsed = schema.safeParse(data) - if (parsed.success) return parsed.data - - throw new InvalidError({ - path: source, - issues: parsed.error.issues, - }) -} - -export function effectSchema>( +export function schema>( schema: S, data: unknown, source: string, @@ -60,7 +47,7 @@ export function effectSchema>( keys: extra, path: [], message: `Unrecognized key${extra.length === 1 ? "" : "s"}: ${extra.join(", ")}`, - } as z.core.$ZodIssue, + }, ], }) } @@ -73,8 +60,12 @@ export function effectSchema>( { path: source, issues: EffectSchema.isSchemaError(error) - ? (SchemaIssue.makeFormatterStandardSchemaV1()(error.issue).issues as z.core.$ZodIssue[]) - : ([{ code: "custom", message: String(error), path: [] }] as z.core.$ZodIssue[]), + ? SchemaIssue.makeFormatterStandardSchemaV1()(error.issue).issues.map((issue) => ({ + ...issue, + message: issue.message, + path: issue.path?.map(String) ?? [], + })) + : [{ message: String(error), path: [] }], }, { cause: error }, ) diff --git a/packages/opencode/src/config/permission.ts b/packages/opencode/src/config/permission.ts index 04f0ff2cafe..a144a4c5ab4 100644 --- a/packages/opencode/src/config/permission.ts +++ b/packages/opencode/src/config/permission.ts @@ -1,21 +1,14 @@ export * as ConfigPermission from "./permission" import { Schema, SchemaGetter } from "effect" -import { zod } from "@opencode-ai/core/effect-zod" -import { withStatics } from "@opencode-ai/core/schema" export const Action = Schema.NullOr(Schema.Literals(["ask", "allow", "deny"])) // kilocode_change - nullable allows null as a delete sentinel .annotate({ identifier: "PermissionActionConfig" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) export type Action = Schema.Schema.Type -export const Object = Schema.Record(Schema.String, Action) - .annotate({ identifier: "PermissionObjectConfig" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +export const Object = Schema.Record(Schema.String, Action).annotate({ identifier: "PermissionObjectConfig" }) export type Object = Schema.Schema.Type -export const Rule = Schema.Union([Action, Object]) - .annotate({ identifier: "PermissionRuleConfig" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +export const Rule = Schema.Union([Action, Object]).annotate({ identifier: "PermissionRuleConfig" }) export type Rule = Schema.Schema.Type // Known permission keys get explicit types in the Effect schema for generated @@ -35,7 +28,6 @@ const InputObject = Schema.StructWithRest( question: Schema.optional(Action), webfetch: Schema.optional(Action), websearch: Schema.optional(Action), - codesearch: Schema.optional(Action), repo_clone: Schema.optional(Rule), repo_overview: Schema.optional(Rule), lsp: Schema.optional(Rule), @@ -63,12 +55,6 @@ export const Info = InputSchema.pipe( // of the same rules. encode: SchemaGetter.passthrough({ strict: false }), }), -) - .annotate({ identifier: "PermissionConfig" }) - .pipe( - // Walker already emits the decodeTo transform into the derived zod (see - // `encoded()` in effect-zod.ts), so just expose that directly. - withStatics((s) => ({ zod: zod(s) })), - ) +).annotate({ identifier: "PermissionConfig" }) type _Info = Schema.Schema.Type export type Info = { -readonly [K in keyof _Info]: _Info[K] } diff --git a/packages/opencode/src/config/plugin.ts b/packages/opencode/src/config/plugin.ts index b1e3ec6f42e..1c4d4037eb9 100644 --- a/packages/opencode/src/config/plugin.ts +++ b/packages/opencode/src/config/plugin.ts @@ -2,18 +2,14 @@ import { Glob } from "@opencode-ai/core/util/glob" import { Schema } from "effect" import { pathToFileURL } from "url" import { isPathPluginSpec, parsePluginSpecifier, resolvePathPluginTarget } from "@/plugin/shared" -import { zod } from "@opencode-ai/core/effect-zod" -import { withStatics } from "@opencode-ai/core/schema" import path from "path" -export const Options = Schema.Record(Schema.String, Schema.Unknown).pipe(withStatics((s) => ({ zod: zod(s) }))) +export const Options = Schema.Record(Schema.String, Schema.Unknown) export type Options = Schema.Schema.Type // Spec is the user-config value: either just a plugin identifier, or the identifier plus inline options. // It answers "what should we load?" but says nothing about where that value came from. -export const Spec = Schema.Union([Schema.String, Schema.mutable(Schema.Tuple([Schema.String, Options]))]).pipe( - withStatics((s) => ({ zod: zod(s) })), -) +export const Spec = Schema.Union([Schema.String, Schema.mutable(Schema.Tuple([Schema.String, Options]))]) export type Spec = Schema.Schema.Type export type Scope = "global" | "local" diff --git a/packages/opencode/src/config/provider.ts b/packages/opencode/src/config/provider.ts index b53d93edfa2..25efd37ffc5 100644 --- a/packages/opencode/src/config/provider.ts +++ b/packages/opencode/src/config/provider.ts @@ -1,7 +1,6 @@ import { Schema } from "effect" import { PROMPTS, AI_SDK_PROVIDERS } from "@kilocode/kilo-gateway" // kilocode_change -import { zod } from "@opencode-ai/core/effect-zod" -import { PositiveInt, withStatics } from "@opencode-ai/core/schema" +import { PositiveInt } from "@opencode-ai/core/schema" import { ModelStatus } from "@/provider/model-status" export const Model = Schema.Struct({ @@ -74,7 +73,7 @@ export const Model = Schema.Struct({ ), ).annotate({ description: "Variant-specific configuration" }), ), -}).pipe(withStatics((s) => ({ zod: zod(s) }))) +}) export const Info = Schema.Struct({ api: Schema.optional(Schema.String), @@ -113,9 +112,7 @@ export const Info = Schema.Struct({ ), ), models: Schema.optional(Schema.Record(Schema.String, Schema.NullOr(Model))), // kilocode_change - allow null values so removed models can be deleted via stripNulls on save -}) - .annotate({ identifier: "ProviderConfig" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "ProviderConfig" }) export type Info = Schema.Schema.Type export * as ConfigProvider from "./provider" diff --git a/packages/opencode/src/config/reference.ts b/packages/opencode/src/config/reference.ts index 36a8faff7ed..b3dec491ac9 100644 --- a/packages/opencode/src/config/reference.ts +++ b/packages/opencode/src/config/reference.ts @@ -1,8 +1,6 @@ export * as ConfigReference from "./reference" import { Schema } from "effect" -import { zod } from "@opencode-ai/core/effect-zod" -import { withStatics } from "@opencode-ai/core/schema" const Git = Schema.Struct({ repository: Schema.String.annotate({ @@ -21,7 +19,5 @@ const Local = Schema.Struct({ export const Entry = Schema.Union([Schema.String, Git, Local]).annotate({ identifier: "ReferenceConfigEntry" }) -export const Info = Schema.Record(Schema.String, Entry) - .annotate({ identifier: "ReferenceConfig" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +export const Info = Schema.Record(Schema.String, Entry).annotate({ identifier: "ReferenceConfig" }) export type Info = Schema.Schema.Type diff --git a/packages/opencode/src/config/server.ts b/packages/opencode/src/config/server.ts index 3c5051749a9..62476771cc3 100644 --- a/packages/opencode/src/config/server.ts +++ b/packages/opencode/src/config/server.ts @@ -1,6 +1,5 @@ import { Schema } from "effect" -import { zod } from "@opencode-ai/core/effect-zod" -import { PositiveInt, withStatics } from "@opencode-ai/core/schema" +import { PositiveInt } from "@opencode-ai/core/schema" export const Server = Schema.Struct({ port: Schema.optional(PositiveInt).annotate({ @@ -14,9 +13,7 @@ export const Server = Schema.Struct({ cors: Schema.optional(Schema.mutable(Schema.Array(Schema.String))).annotate({ description: "Additional domains to allow for CORS", }), -}) - .annotate({ identifier: "ServerConfig" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "ServerConfig" }) export type Server = Schema.Schema.Type export * as ConfigServer from "./server" diff --git a/packages/opencode/src/config/skills.ts b/packages/opencode/src/config/skills.ts index f707e922eee..38c0017d0f6 100644 --- a/packages/opencode/src/config/skills.ts +++ b/packages/opencode/src/config/skills.ts @@ -1,6 +1,4 @@ import { Schema } from "effect" -import { zod } from "@opencode-ai/core/effect-zod" -import { withStatics } from "@opencode-ai/core/schema" export const Info = Schema.Struct({ paths: Schema.optional(Schema.Array(Schema.String)).annotate({ @@ -9,7 +7,7 @@ export const Info = Schema.Struct({ urls: Schema.optional(Schema.Array(Schema.String)).annotate({ description: "URLs to fetch skills from (e.g., https://example.com/.well-known/skills/)", }), -}).pipe(withStatics((s) => ({ zod: zod(s) }))) +}) export type Info = Schema.Schema.Type diff --git a/packages/opencode/src/control-plane/adapters/worktree.ts b/packages/opencode/src/control-plane/adapters/worktree.ts index 605d114ace6..1c85d125a2d 100644 --- a/packages/opencode/src/control-plane/adapters/worktree.ts +++ b/packages/opencode/src/control-plane/adapters/worktree.ts @@ -22,11 +22,10 @@ export const WorktreeAdapter: WorkspaceAdapter = { description: "Create a git worktree", async configure(info) { const { AppRuntime, Worktree } = await loadWorktree() - const next = await AppRuntime.runPromise(Worktree.Service.use((svc) => svc.makeWorktreeInfo())) + const next = await AppRuntime.runPromise(Worktree.Service.use((svc) => svc.makeWorktreeInfo({ detached: true }))) return { ...info, name: next.name, - branch: next.branch, directory: next.directory, } }, @@ -38,7 +37,7 @@ export const WorktreeAdapter: WorkspaceAdapter = { svc.createFromInfo({ name: config.name, directory: config.directory, - branch: config.branch ?? config.name, + ...(config.branch ? { branch: config.branch } : {}), }), ), ) @@ -48,9 +47,8 @@ export const WorktreeAdapter: WorkspaceAdapter = { return (await AppRuntime.runPromise(Worktree.Service.use((svc) => svc.list()))).map((info) => ({ type: "worktree", name: info.name, - branch: info.branch ?? null, + branch: info.branch, directory: info.directory, - extra: null, projectID: Instance.project.id, })) }, diff --git a/packages/opencode/src/control-plane/types.ts b/packages/opencode/src/control-plane/types.ts index e78d728e040..e55ae2194e4 100644 --- a/packages/opencode/src/control-plane/types.ts +++ b/packages/opencode/src/control-plane/types.ts @@ -7,9 +7,9 @@ export const WorkspaceInfo = Schema.Struct({ id: WorkspaceID, type: Schema.String, name: Schema.String, - branch: Schema.NullOr(Schema.String), - directory: Schema.NullOr(Schema.String), - extra: Schema.NullOr(Schema.Unknown), + branch: Schema.optional(Schema.NullOr(Schema.String)), + directory: Schema.optional(Schema.NullOr(Schema.String)), + extra: Schema.optional(Schema.NullOr(Schema.Unknown)), projectID: ProjectID, }).annotate({ identifier: "Workspace" }) export type WorkspaceInfo = DeepMutable> diff --git a/packages/opencode/src/control-plane/workspace.ts b/packages/opencode/src/control-plane/workspace.ts index 7d72a839474..3b3775a3007 100644 --- a/packages/opencode/src/control-plane/workspace.ts +++ b/packages/opencode/src/control-plane/workspace.ts @@ -10,9 +10,9 @@ import { GlobalBus } from "@/bus/global" import { Auth } from "@/auth" import { SyncEvent } from "@/sync" import { EventSequenceTable, EventTable } from "@/sync/event.sql" -import { Flag } from "@opencode-ai/core/flag/flag" +import { AppFileSystem } from "@opencode-ai/core/filesystem" import * as Log from "@opencode-ai/core/util/log" -import { Filesystem } from "@/util/filesystem" +import { RuntimeFlags } from "@/effect/runtime-flags" import { ProjectID } from "@/project/schema" import { Slug } from "@opencode-ai/core/util/slug" import { WorkspaceTable } from "./workspace.sql" @@ -175,6 +175,8 @@ export const layer = Layer.effect( const http = yield* HttpClient.HttpClient const sync = yield* SyncEvent.Service const vcs = yield* Vcs.Service + const flags = yield* RuntimeFlags.Service + const fs = yield* AppFileSystem.Service const connections = new Map() const syncFibers = yield* FiberMap.make() @@ -482,7 +484,7 @@ export const layer = Layer.effect( }) const startSync = Effect.fn("Workspace.startSync")(function* (space: Info) { - if (!Flag.KILO_EXPERIMENTAL_WORKSPACES) return + if (!flags.experimentalWorkspaces) return const adapter = getAdapter(space.projectID, space.type) const target = yield* EffectBridge.fromPromise(() => adapter.target(space)).pipe( @@ -500,7 +502,7 @@ export const layer = Layer.effect( if (!target) return if (target.type === "local") { - setStatus(space.id, (yield* Effect.promise(() => Filesystem.exists(target.directory))) ? "connected" : "error") + setStatus(space.id, (yield* fs.existsSafe(target.directory)) ? "connected" : "error") return } @@ -641,7 +643,7 @@ export const layer = Layer.effect( // "claim" this session so any future events coming from // the old workspace are ignored - SyncEvent.claim(input.sessionID, input.workspaceID ?? previous.projectID) + yield* sync.claim(input.sessionID, input.workspaceID ?? previous.projectID) } } @@ -1039,7 +1041,9 @@ export const defaultLayer = layer.pipe( Layer.provide(SessionPrompt.defaultLayer), Layer.provide(Project.defaultLayer), Layer.provide(Vcs.defaultLayer), + Layer.provide(AppFileSystem.defaultLayer), Layer.provide(FetchHttpClient.layer), + Layer.provide(RuntimeFlags.defaultLayer), ) const TIMEOUT = 5000 diff --git a/packages/opencode/src/data-migration.ts b/packages/opencode/src/data-migration.ts index c3e5a9d2b0f..b6956032a41 100644 --- a/packages/opencode/src/data-migration.ts +++ b/packages/opencode/src/data-migration.ts @@ -2,7 +2,9 @@ import { Context, Effect, Layer } from "effect" import { Database } from "./storage/db" import { DataMigrationTable } from "./data-migration.sql" import * as Log from "@opencode-ai/core/util/log" -import { eq } from "drizzle-orm" +import { and, asc, eq, gt, inArray, sql } from "drizzle-orm" +import { MessageTable, SessionTable } from "./session/session.sql" +import type { SessionID } from "./session/schema" export type Migration = { name: string @@ -18,7 +20,105 @@ export class Service extends Context.Service()("@opencode/Da export const layer = Layer.effect( Service, Effect.gen(function* () { - const migrations: Migration[] = [] + const migrations: Migration[] = [ + { + name: "session_usage_from_messages", + run: Effect.gen(function* () { + type Usage = { + cost: number + tokens: { input: number; output: number; reasoning: number; cache: { read: number; write: number } } + } + + for (let cursor: SessionID | undefined, page = 1; ; page++) { + const next = yield* Effect.gen(function* () { + const sessions = yield* Effect.sync(() => + Database.use((db) => + db + .select({ id: SessionTable.id }) + .from(SessionTable) + .where(cursor ? gt(SessionTable.id, cursor) : undefined) + .orderBy(asc(SessionTable.id)) + .limit(100) + .all(), + ), + ) + if (sessions.length === 0) return + + yield* Effect.sync(() => + Database.transaction((db) => { + const usageBySession = new Map( + sessions.map((session) => [ + session.id, + { cost: 0, tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } } }, + ]), + ) + + for (const row of db + .select({ + session_id: MessageTable.session_id, + cost: sql`coalesce(sum(coalesce(json_extract(${MessageTable.data}, '$.cost'), 0)), 0)`, + tokens_input: sql`coalesce(sum(coalesce(json_extract(${MessageTable.data}, '$.tokens.input'), 0)), 0)`, + tokens_output: sql`coalesce(sum(coalesce(json_extract(${MessageTable.data}, '$.tokens.output'), 0)), 0)`, + tokens_reasoning: sql`coalesce(sum(coalesce(json_extract(${MessageTable.data}, '$.tokens.reasoning'), 0)), 0)`, + tokens_cache_read: sql`coalesce(sum(coalesce(json_extract(${MessageTable.data}, '$.tokens.cache.read'), 0)), 0)`, + tokens_cache_write: sql`coalesce(sum(coalesce(json_extract(${MessageTable.data}, '$.tokens.cache.write'), 0)), 0)`, + }) + .from(MessageTable) + .where( + and( + inArray( + MessageTable.session_id, + sessions.map((session) => session.id), + ), + sql`json_extract(${MessageTable.data}, '$.role') = 'assistant'`, + ), + ) + .groupBy(MessageTable.session_id) + .all()) { + const current = usageBySession.get(row.session_id) + if (!current) continue + current.cost = row.cost + current.tokens.input = row.tokens_input + current.tokens.output = row.tokens_output + current.tokens.reasoning = row.tokens_reasoning + current.tokens.cache.read = row.tokens_cache_read + current.tokens.cache.write = row.tokens_cache_write + } + + for (const [sessionID, value] of usageBySession) { + db.update(SessionTable) + .set({ + cost: value.cost, + tokens_input: value.tokens.input, + tokens_output: value.tokens.output, + tokens_reasoning: value.tokens.reasoning, + tokens_cache_read: value.tokens.cache.read, + tokens_cache_write: value.tokens.cache.write, + time_updated: sql`${SessionTable.time_updated}`, + }) + .where(eq(SessionTable.id, sessionID)) + .run() + } + }), + ) + + return sessions.at(-1)?.id + }).pipe( + Effect.withSpan("DataMigration.sessionUsage.page", { + attributes: { + "data_migration.name": "session_usage_from_messages", + "data_migration.page": page, + "data_migration.cursor": cursor ?? "", + }, + }), + ) + if (!next) return + cursor = next + yield* Effect.sleep("10 millis") + } + }), + }, + ] yield* Effect.gen(function* () { if (migrations.length === 0) return @@ -36,7 +136,7 @@ export const layer = Layer.effect( if (completed) continue log.info("running data migration", { name: migration.name }) - yield* migration.run + yield* migration.run.pipe(Effect.withSpan("DataMigration", { attributes: { name: migration.name } })) Database.use((db) => db .insert(DataMigrationTable) @@ -46,7 +146,9 @@ export const layer = Layer.effect( ) } }).pipe( - Effect.tapCause((cause) => Effect.logError("failed to run data migrations", { cause })), + Effect.tapCause((cause) => + Effect.logError("failed to run data migrations").pipe(Effect.annotateLogs("cause", cause)), + ), Effect.ignore, Effect.forkScoped, ) diff --git a/packages/opencode/src/effect/app-runtime.ts b/packages/opencode/src/effect/app-runtime.ts index 377b465547d..ee5bb53873d 100644 --- a/packages/opencode/src/effect/app-runtime.ts +++ b/packages/opencode/src/effect/app-runtime.ts @@ -14,7 +14,7 @@ import { FileWatcher } from "@/file/watcher" import { Storage } from "@/storage/storage" import { Snapshot } from "@/snapshot" import { Plugin } from "@/plugin" -import { ModelsDev } from "@/provider/models" +import { ModelsDev } from "@opencode-ai/core/models" import { ModelCache } from "@/provider/model-cache" // kilocode_change import { Provider } from "@/provider/provider" import { ProviderAuth } from "@/provider/auth" @@ -56,6 +56,8 @@ import { SyncEvent } from "@/sync" import { Npm } from "@opencode-ai/core/npm" import { memoMap } from "@opencode-ai/core/effect/memo-map" import { DataMigration } from "@/data-migration" +import { BackgroundJob } from "@/background/job" +import { RuntimeFlags } from "@/effect/runtime-flags" export const AppLayer = Layer.mergeAll( Npm.defaultLayer, @@ -83,6 +85,8 @@ export const AppLayer = Layer.mergeAll( Todo.defaultLayer, Session.defaultLayer, SessionStatus.defaultLayer, + BackgroundJob.defaultLayer, + RuntimeFlags.defaultLayer, SessionRunState.defaultLayer, SessionProcessor.defaultLayer, SessionCompaction.defaultLayer, diff --git a/packages/opencode/src/effect/promise.ts b/packages/opencode/src/effect/promise.ts new file mode 100644 index 00000000000..d7918796ad8 --- /dev/null +++ b/packages/opencode/src/effect/promise.ts @@ -0,0 +1,17 @@ +import { Cause, Effect } from "effect" + +export function refineRejection( + evaluate: (signal: AbortSignal) => PromiseLike, + refine: (cause: unknown) => E | undefined, +) { + return Effect.tryPromise(evaluate).pipe( + Effect.catch((error) => { + const cause = Cause.isUnknownError(error) ? error.cause : error + const refined = refine(cause) + if (refined !== undefined) return Effect.fail(refined) + return Effect.die(cause) + }), + ) +} + +export * as EffectPromise from "./promise" diff --git a/packages/opencode/src/effect/runner.ts b/packages/opencode/src/effect/runner.ts index 1e7d4c2966c..f21a61c97e5 100644 --- a/packages/opencode/src/effect/runner.ts +++ b/packages/opencode/src/effect/runner.ts @@ -4,11 +4,12 @@ export interface Runner { readonly state: State readonly busy: boolean readonly ensureRunning: (work: Effect.Effect) => Effect.Effect - readonly startShell: (work: Effect.Effect, ready?: Latch.Latch) => Effect.Effect + readonly startShell: (work: Effect.Effect, ready?: Latch.Latch) => Effect.Effect readonly cancel: Effect.Effect } export class Cancelled extends Schema.TaggedErrorClass()("RunnerCancelled", {}) {} +export class Busy extends Schema.TaggedErrorClass()("RunnerBusy", {}) {} interface RunHandle { id: number @@ -41,12 +42,11 @@ export const make = ( onIdle?: Effect.Effect onBusy?: Effect.Effect onInterrupt?: Effect.Effect - busy?: () => never }, ): Runner => { const ref = SynchronizedRef.makeUnsafe>({ _tag: "Idle" }) const idle = opts?.onIdle ?? Effect.void - const busy = opts?.onBusy ?? Effect.void + const onBusy = opts?.onBusy ?? Effect.void const onInterrupt = opts?.onInterrupt let ids = 0 @@ -137,20 +137,15 @@ export const make = ( }), ).pipe(Effect.flatten) - const startShell = (work: Effect.Effect, ready?: Latch.Latch) => + const startShell = (work: Effect.Effect, ready?: Latch.Latch): Effect.Effect => SynchronizedRef.modifyEffect( ref, Effect.fnUntraced(function* (st) { if (st._tag !== "Idle") { - return [ - Effect.sync(() => { - if (opts?.busy) opts.busy() - throw new Error("Runner is busy") - }), - st, - ] as const + const reject: Effect.Effect = Effect.fail(new Busy()) + return [reject, st] as const } - yield* busy + yield* onBusy const id = next() const cancelled = yield* Deferred.make() const fiber = yield* work.pipe(Effect.ensuring(finishShell(id)), Effect.forkChild) @@ -181,7 +176,7 @@ export const make = ( return [ Effect.gen(function* () { yield* Fiber.interrupt(st.run.fiber) - yield* Deferred.await(st.run.done).pipe(Effect.exit, Effect.asVoid) + yield* Deferred.fail(st.run.done, new Cancelled()).pipe(Effect.asVoid) yield* idleIfCurrent() }), { _tag: "Idle" } as const, diff --git a/packages/opencode/src/effect/runtime-flags.ts b/packages/opencode/src/effect/runtime-flags.ts new file mode 100644 index 00000000000..be3e198f9f1 --- /dev/null +++ b/packages/opencode/src/effect/runtime-flags.ts @@ -0,0 +1,66 @@ +import { Config, ConfigProvider, Context, Effect, Layer } from "effect" +import { ConfigService } from "@/effect/config-service" + +const bool = (name: string) => Config.boolean(name).pipe(Config.withDefault(false)) +const positiveInteger = (name: string) => + Config.number(name).pipe( + Config.map((value) => (Number.isInteger(value) && value > 0 ? value : undefined)), + Config.orElse(() => Config.succeed(undefined)), + ) +const experimental = bool("KILO_EXPERIMENTAL") +const enabledByExperimental = (name: string) => + Config.all({ experimental, enabled: bool(name) }).pipe(Config.map((flags) => flags.experimental || flags.enabled)) + +export class Service extends ConfigService.Service()("@opencode/RuntimeFlags", { + autoShare: bool("KILO_AUTO_SHARE"), + pure: bool("KILO_PURE"), + disableDefaultPlugins: bool("KILO_DISABLE_DEFAULT_PLUGINS"), + disableChannelDb: bool("KILO_DISABLE_CHANNEL_DB"), + disableEmbeddedWebUi: bool("KILO_DISABLE_EMBEDDED_WEB_UI"), + disableClaudeCodeSkills: Config.all({ + broad: bool("KILO_DISABLE_CLAUDE_CODE"), + direct: bool("KILO_DISABLE_CLAUDE_CODE_SKILLS"), + }).pipe(Config.map((flags) => flags.broad || flags.direct)), + enableExa: Config.all({ + experimental, + enabled: bool("KILO_ENABLE_EXA"), + legacy: bool("KILO_EXPERIMENTAL_EXA"), + }).pipe(Config.map((flags) => flags.experimental || flags.enabled || flags.legacy)), + enableParallel: Config.all({ + enabled: bool("KILO_ENABLE_PARALLEL"), + legacy: bool("KILO_EXPERIMENTAL_PARALLEL"), + }).pipe(Config.map((flags) => flags.enabled || flags.legacy)), + enableExperimentalModels: bool("KILO_ENABLE_EXPERIMENTAL_MODELS"), + enableQuestionTool: bool("KILO_ENABLE_QUESTION_TOOL"), + experimentalScout: enabledByExperimental("KILO_EXPERIMENTAL_SCOUT"), + experimentalBackgroundSubagents: enabledByExperimental("KILO_EXPERIMENTAL_BACKGROUND_SUBAGENTS"), + experimentalLspTy: bool("KILO_EXPERIMENTAL_LSP_TY"), + experimentalLspTool: enabledByExperimental("KILO_EXPERIMENTAL_LSP_TOOL"), + experimentalOxfmt: enabledByExperimental("KILO_EXPERIMENTAL_OXFMT"), + experimentalPlanMode: enabledByExperimental("KILO_EXPERIMENTAL_PLAN_MODE"), + experimentalEventSystem: enabledByExperimental("KILO_EXPERIMENTAL_EVENT_SYSTEM"), + experimentalWorkspaces: enabledByExperimental("KILO_EXPERIMENTAL_WORKSPACES"), + experimentalIconDiscovery: enabledByExperimental("KILO_EXPERIMENTAL_ICON_DISCOVERY"), + bashDefaultTimeoutMs: positiveInteger("KILO_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS"), + client: Config.string("KILO_CLIENT").pipe(Config.withDefault("cli")), +}) {} + +export type Info = Context.Service.Shape + +const emptyConfigLayer = Service.defaultLayer.pipe( + Layer.provide(ConfigProvider.layer(ConfigProvider.fromUnknown({}))), + Layer.orDie, +) + +export const layer = (overrides: Partial = {}) => + Layer.effect( + Service, + Effect.gen(function* () { + const flags = yield* Service + return Service.of({ ...flags, ...overrides }) + }), + ).pipe(Layer.provide(emptyConfigLayer)) + +export const defaultLayer = Service.defaultLayer.pipe(Layer.orDie) + +export * as RuntimeFlags from "./runtime-flags" diff --git a/packages/opencode/src/file/index.ts b/packages/opencode/src/file/index.ts index af910c35a6b..756d937eaee 100644 --- a/packages/opencode/src/file/index.ts +++ b/packages/opencode/src/file/index.ts @@ -15,17 +15,14 @@ import { containsPath } from "../project/instance-context" import * as Log from "@opencode-ai/core/util/log" import { Protected } from "./protected" import { Ripgrep } from "./ripgrep" -import { zod } from "@opencode-ai/core/effect-zod" -import { NonNegativeInt, type DeepMutable, withStatics } from "@opencode-ai/core/schema" +import { NonNegativeInt, type DeepMutable } from "@opencode-ai/core/schema" export const Info = Schema.Struct({ path: Schema.String, added: NonNegativeInt, removed: NonNegativeInt, status: Schema.Literals(["added", "deleted", "modified"]), -}) - .annotate({ identifier: "File" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "File" }) export type Info = DeepMutable> export const Node = Schema.Struct({ @@ -34,9 +31,7 @@ export const Node = Schema.Struct({ absolute: Schema.String, type: Schema.Literals(["file", "directory"]), ignored: Schema.Boolean, -}) - .annotate({ identifier: "FileNode" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "FileNode" }) export type Node = DeepMutable> const Hunk = Schema.Struct({ @@ -63,9 +58,7 @@ export const Content = Schema.Struct({ patch: Schema.optional(Patch), encoding: Schema.optional(Schema.Literal("base64")), mimeType: Schema.optional(Schema.String), -}) - .annotate({ identifier: "FileContent" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "FileContent" }) export type Content = DeepMutable> export const Event = { diff --git a/packages/opencode/src/file/ripgrep.ts b/packages/opencode/src/file/ripgrep.ts index 484900101da..14b985fbe16 100644 --- a/packages/opencode/src/file/ripgrep.ts +++ b/packages/opencode/src/file/ripgrep.ts @@ -11,8 +11,7 @@ import { Global } from "@opencode-ai/core/global" import * as Log from "@opencode-ai/core/util/log" import { sanitizedProcessEnv } from "@opencode-ai/core/util/opencode-process" import { which } from "@/util/which" -import { zod } from "@opencode-ai/core/effect-zod" -import { NonNegativeInt, withStatics } from "@opencode-ai/core/schema" +import { NonNegativeInt } from "@opencode-ai/core/schema" const log = Log.create({ service: "ripgrep" }) const VERSION = "15.1.0" @@ -69,7 +68,7 @@ export const SearchMatch = Schema.Struct({ end: NonNegativeInt, }), ), -}).pipe(withStatics((s) => ({ zod: zod(s) }))) +}) export const Match = Schema.Struct({ type: Schema.Literal("match"), @@ -288,8 +287,10 @@ export const layer: Layer.Layer which(process.platform === "win32" ? "rg.exe" : "rg")) + // kilocode_change start - Git for Windows can expose an MSYS rg.exe that fails when spawned natively + const system = yield* Effect.sync(() => (process.platform === "win32" ? undefined : which("rg"))) if (system && (yield* fs.isFile(system).pipe(Effect.orDie))) return system + // kilocode_change end const target = path.join(Global.Path.bin, `rg${process.platform === "win32" ? ".exe" : ""}`) if (yield* fs.isFile(target).pipe(Effect.orDie)) return target diff --git a/packages/opencode/src/file/watcher.ts b/packages/opencode/src/file/watcher.ts index 08e68cf4b47..657a3dcff03 100644 --- a/packages/opencode/src/file/watcher.ts +++ b/packages/opencode/src/file/watcher.ts @@ -4,7 +4,6 @@ import { createWrapper } from "@parcel/watcher/wrapper" import type ParcelWatcher from "@parcel/watcher" import { readdir } from "fs/promises" import path from "path" -import z from "zod" import { Bus } from "@/bus" import { BusEvent } from "@/bus/bus-event" import { InstanceState } from "@/effect/instance-state" diff --git a/packages/opencode/src/format/formatter.ts b/packages/opencode/src/format/formatter.ts index 5423aba826a..4c559631f29 100644 --- a/packages/opencode/src/format/formatter.ts +++ b/packages/opencode/src/format/formatter.ts @@ -3,9 +3,10 @@ import type { InstanceContext } from "../project/instance" import { Filesystem } from "@/util/filesystem" import { Process } from "@/util/process" import { which } from "../util/which" -import { Flag } from "@opencode-ai/core/flag/flag" -export interface Context extends Pick {} +export interface Context extends Pick { + experimentalOxfmt: boolean +} export interface Info { name: string @@ -90,7 +91,7 @@ export const oxfmt: Info = { }, extensions: [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mts", ".cts"], async enabled(context) { - if (!Flag.KILO_EXPERIMENTAL_OXFMT) return false + if (!context.experimentalOxfmt) return false const items = await Filesystem.findUp("package.json", context.directory, context.worktree) for (const item of items) { const json = await Filesystem.readJson<{ diff --git a/packages/opencode/src/format/index.ts b/packages/opencode/src/format/index.ts index c9ab433f118..50abba0ff9e 100644 --- a/packages/opencode/src/format/index.ts +++ b/packages/opencode/src/format/index.ts @@ -1,14 +1,14 @@ import { Effect, Layer, Context, Schema } from "effect" -import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process" -import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" +import { ChildProcess } from "effect/unstable/process" +import { AppProcess } from "@opencode-ai/core/process" import { InstanceState } from "@/effect/instance-state" import path from "path" import { mergeDeep } from "remeda" import { Config } from "@/config/config" +import { RuntimeFlags } from "@/effect/runtime-flags" +import { errorMessage } from "@/util/error" import * as Log from "@opencode-ai/core/util/log" import * as Formatter from "./formatter" -import { zod } from "@opencode-ai/core/effect-zod" -import { withStatics } from "@opencode-ai/core/schema" const log = Log.create({ service: "format" }) @@ -16,9 +16,7 @@ export const Status = Schema.Struct({ name: Schema.String, extensions: Schema.Array(Schema.String), enabled: Schema.Boolean, -}) - .annotate({ identifier: "FormatterStatus" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "FormatterStatus" }) export type Status = Schema.Schema.Type export interface Interface { @@ -33,7 +31,8 @@ export const layer = Layer.effect( Service, Effect.gen(function* () { const config = yield* Config.Service - const spawner = yield* ChildProcessSpawner.ChildProcessSpawner + const appProcess = yield* AppProcess.Service + const flags = yield* RuntimeFlags.Service const state = yield* InstanceState.make( Effect.fn("Format.state")(function* (ctx) { @@ -43,7 +42,7 @@ export const layer = Layer.effect( async function getCommand(item: Formatter.Info) { let cmd = commands[item.name] if (cmd === false || cmd === undefined) { - cmd = await item.enabled(ctx) + cmd = await item.enabled({ ...ctx, experimentalOxfmt: flags.experimentalOxfmt }) commands[item.name] = cmd } return cmd @@ -85,8 +84,8 @@ export const layer = Layer.effect( log.info("running", { command: cmd }) const replaced = cmd.map((x) => x.replace("$FILE", filepath)) const dir = yield* InstanceState.directory - const code = yield* spawner - .spawn( + const result = yield* appProcess + .run( ChildProcess.make(replaced[0]!, replaced.slice(1), { cwd: dir, env: item.environment, @@ -97,21 +96,20 @@ export const layer = Layer.effect( }), ) .pipe( - Effect.flatMap((handle) => handle.exitCode), - Effect.scoped, - Effect.catch(() => + Effect.catch((error) => Effect.sync(() => { log.error("failed to format file", { error: "spawn failed", command: cmd, ...item.environment, file: filepath, + cause: errorMessage(error.cause ?? error), }) - return ChildProcessSpawner.ExitCode(1) + return undefined }), ), ) - if (code !== 0) { + if (result && result.exitCode !== 0) { log.error("failed", { command: cmd, ...item.environment, @@ -204,7 +202,8 @@ export const layer = Layer.effect( export const defaultLayer = layer.pipe( Layer.provide(Config.defaultLayer), - Layer.provide(CrossSpawnSpawner.defaultLayer), + Layer.provide(AppProcess.defaultLayer), + Layer.provide(RuntimeFlags.defaultLayer), ) export * as Format from "." diff --git a/packages/opencode/src/git/index.ts b/packages/opencode/src/git/index.ts index 349bbad466e..5e76b7f7313 100644 --- a/packages/opencode/src/git/index.ts +++ b/packages/opencode/src/git/index.ts @@ -1,6 +1,6 @@ -import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" +import { AppProcess } from "@opencode-ai/core/process" import { Effect, Layer, Context, Stream } from "effect" -import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process" +import { ChildProcess } from "effect/unstable/process" const cfg = [ "--no-optional-locks", @@ -102,49 +102,31 @@ export class Service extends Context.Service()("@opencode/Gi export const layer = Layer.effect( Service, Effect.gen(function* () { - const spawner = yield* ChildProcessSpawner.ChildProcessSpawner + const appProcess = yield* AppProcess.Service const encoder = new TextEncoder() const stdin = (text: string) => Stream.make(encoder.encode(text)) const run = Effect.fn("Git.run")( function* (args: string[], opts: Options) { - const proc = ChildProcess.make("git", [...cfg, ...args], { - cwd: opts.cwd, - env: opts.env, - extendEnv: true, - stdin: opts.stdin ?? "ignore", - stdout: "pipe", - stderr: "pipe", - }) - const handle = yield* spawner.spawn(proc) - const collect = (stream: typeof handle.stdout) => - Stream.runFold( - stream, - () => ({ chunks: [] as Uint8Array[], bytes: 0, truncated: false }), - (acc, chunk) => { - if (opts.maxOutputBytes === undefined) { - acc.chunks.push(chunk) - acc.bytes += chunk.length - return acc - } - - const remaining = opts.maxOutputBytes - acc.bytes - if (remaining > 0) acc.chunks.push(remaining >= chunk.length ? chunk : chunk.slice(0, remaining)) - acc.bytes += chunk.length - acc.truncated = acc.truncated || acc.bytes > opts.maxOutputBytes - return acc - }, - ).pipe(Effect.map((x) => ({ buffer: Buffer.concat(x.chunks), truncated: x.truncated }))) - const [stdout, stderr] = yield* Effect.all([collect(handle.stdout), collect(handle.stderr)], { concurrency: 2 }) + const result = yield* appProcess.run( + ChildProcess.make("git", [...cfg, ...args], { + cwd: opts.cwd, + env: opts.env, + extendEnv: true, + stdin: opts.stdin ?? "ignore", + stdout: "pipe", + stderr: "pipe", + }), + { maxOutputBytes: opts.maxOutputBytes }, + ) return { - exitCode: yield* handle.exitCode, - text: () => stdout.buffer.toString("utf8"), - stdout: stdout.buffer, - stderr: stderr.buffer, - truncated: stdout.truncated || stderr.truncated, + exitCode: result.exitCode, + text: () => result.stdout.toString("utf8"), + stdout: result.stdout, + stderr: result.stderr, + truncated: result.stdoutTruncated || result.stderrTruncated, } satisfies Result }, - Effect.scoped, Effect.catch((err) => Effect.succeed(fail(err))), ) @@ -360,6 +342,6 @@ export const layer = Layer.effect( }), ) -export const defaultLayer = layer.pipe(Layer.provide(CrossSpawnSpawner.defaultLayer)) +export const defaultLayer = layer.pipe(Layer.provide(AppProcess.defaultLayer)) export * as Git from "." diff --git a/packages/opencode/src/id/id.ts b/packages/opencode/src/id/id.ts index 47848e2d6d6..c641fbf890c 100644 --- a/packages/opencode/src/id/id.ts +++ b/packages/opencode/src/id/id.ts @@ -1,6 +1,7 @@ import { randomBytes } from "crypto" const prefixes = { + job: "job", event: "evt", session: "ses", message: "msg", diff --git a/packages/opencode/src/ide/index.ts b/packages/opencode/src/ide/index.ts index a9b1647e6b6..6acfd5d5fe8 100644 --- a/packages/opencode/src/ide/index.ts +++ b/packages/opencode/src/ide/index.ts @@ -1,5 +1,4 @@ import { BusEvent } from "@/bus/bus-event" -import z from "zod" import { Schema } from "effect" import { NamedError } from "@opencode-ai/core/util/error" @@ -20,14 +19,11 @@ export const Event = { ), } -export const AlreadyInstalledError = NamedError.create("AlreadyInstalledError", z.object({})) +export const AlreadyInstalledError = NamedError.create("AlreadyInstalledError", {}) -export const InstallFailedError = NamedError.create( - "InstallFailedError", - z.object({ - stderr: z.string(), - }), -) +export const InstallFailedError = NamedError.create("InstallFailedError", { + stderr: Schema.String, +}) export function ide() { if (process.env["TERM_PROGRAM"] === "vscode") { diff --git a/packages/opencode/src/image/image.ts b/packages/opencode/src/image/image.ts index a696e5d7719..df3a4a0f8ce 100644 --- a/packages/opencode/src/image/image.ts +++ b/packages/opencode/src/image/image.ts @@ -1,9 +1,12 @@ import { Config } from "@/config/config" import type { MessageV2 } from "@/session/message-v2" import * as Log from "@opencode-ai/core/util/log" +import photonWasm from "@silvia-odwyer/photon-node/photon_rs_bg.wasm" with { type: "file" } import { Context, Effect, Layer, Schema } from "effect" +import path from "node:path" +import { fileURLToPath } from "node:url" -export const MAX_BASE64_BYTES = 4.5 * 1024 * 1024 // kilocode_change - share user file pre-read limit +export const MAX_BASE64_BYTES = 5 * 1024 * 1024 // kilocode_change - share user file pre-read limit const MAX_WIDTH = 2000 const MAX_HEIGHT = 2000 const AUTO_RESIZE = true @@ -22,8 +25,7 @@ function dimensions(mime: string, data: Buffer) { if (mime === "image/gif" && data.length >= 10) { const head = data.subarray(0, 6).toString("ascii") - if (head === "GIF87a" || head === "GIF89a") - return { width: data.readUInt16LE(6), height: data.readUInt16LE(8) } + if (head === "GIF87a" || head === "GIF89a") return { width: data.readUInt16LE(6), height: data.readUInt16LE(8) } } if ((mime === "image/jpeg" || mime === "image/jpg") && data.length >= 4 && data.readUInt16BE(0) === 0xffd8) { @@ -96,12 +98,12 @@ export function fallback( } // kilocode_change end -export class PhotonUnavailableError extends Schema.TaggedErrorClass()( - "ImagePhotonUnavailableError", +export class ResizerUnavailableError extends Schema.TaggedErrorClass()( + "ImageResizerUnavailableError", {}, ) { override get message() { - return "Photon image processor is unavailable" + return "Image resizer is unavailable" } } @@ -132,7 +134,7 @@ export class SizeError extends Schema.TaggedErrorClass()("ImageSizeEr } } -export type Error = PhotonUnavailableError | InvalidDataUrlError | DecodeError | SizeError +export type Error = ResizerUnavailableError | InvalidDataUrlError | DecodeError | SizeError export interface Interface { readonly normalize: (input: MessageV2.FilePart) => Effect.Effect @@ -145,20 +147,15 @@ export const layer = Layer.effect( Effect.gen(function* () { const config = yield* Config.Service const loadPhoton = yield* Effect.cached( - Effect.promise(async () => { - try { - const photonWasm = (await import("@silvia-odwyer/photon-node/photon_rs_bg.wasm", { with: { type: "file" } })) - .default - // kilocode_change start - use Kilo's embedded WASM path in compiled binaries - ;(globalThis as typeof globalThis & { __KILOCODE_PHOTON_WASM_PATH?: string }).__KILOCODE_PHOTON_WASM_PATH = - photonWasm - // kilocode_change end - return await import("@silvia-odwyer/photon-node") - } catch (err) { - log.error("failed to load Photon image processor", { err }) // kilocode_change - return null - } - }), + Effect.sync(() => { + const wasm = path.isAbsolute(photonWasm) ? photonWasm : fileURLToPath(new URL(photonWasm, import.meta.url)) + ;(globalThis as typeof globalThis & { __OPENCODE_PHOTON_WASM_PATH?: string }).__OPENCODE_PHOTON_WASM_PATH = wasm + ;(globalThis as typeof globalThis & { __KILOCODE_PHOTON_WASM_PATH?: string }).__KILOCODE_PHOTON_WASM_PATH = wasm + }).pipe( + Effect.andThen(() => Effect.tryPromise(() => import("@silvia-odwyer/photon-node"))), + Effect.tapError((error) => Effect.sync(() => log.warn("failed to load photon", { error }))), + Effect.mapError(() => new ResizerUnavailableError()), + ), ) const normalize = Effect.fn("Image.normalize")(function* (input: MessageV2.FilePart) { @@ -173,40 +170,35 @@ export const layer = Layer.effect( return yield* new InvalidDataUrlError({ url: input.url }) const base64 = input.url.slice(input.url.indexOf(";base64,") + ";base64,".length) - const photon = yield* loadPhoton - // kilocode_change start - fail closed on invalid bytes but preserve valid in-limit images without Photon - if (!photon) { - const result = fallback(input, base64, { - bytes: info.maxBase64Bytes, - width: info.maxWidth, - height: info.maxHeight, - }) - if (result instanceof Error) return yield* result - return result - } - // kilocode_change end + const bytes = Buffer.byteLength(base64, "utf8") + const photon = yield* loadPhoton.pipe( + Effect.catchTag("ImageResizerUnavailableError", () => { + const result = fallback(input, base64, { + bytes: info.maxBase64Bytes, + width: info.maxWidth, + height: info.maxHeight, + }) + return result instanceof Error ? Effect.fail(result) : Effect.succeed(undefined) + }), + ) + if (!photon) return input - const decoded = yield* Effect.sync(() => { - try { - return photon.PhotonImage.new_from_byteslice(Buffer.from(base64, "base64")) - } catch { - return undefined - } + const decoded = yield* Effect.try({ + try: () => photon.PhotonImage.new_from_byteslice(Buffer.from(base64, "base64")), + catch: (error) => { + log.warn("failed to decode image", { error }) + return new DecodeError() + }, }) - if (!decoded) return yield* new DecodeError() try { const originalWidth = decoded.get_width() const originalHeight = decoded.get_height() - if ( - originalWidth <= info.maxWidth && - originalHeight <= info.maxHeight && - Buffer.byteLength(base64, "utf8") <= info.maxBase64Bytes - ) + if (originalWidth <= info.maxWidth && originalHeight <= info.maxHeight && bytes <= info.maxBase64Bytes) return input if (!info.autoResize) return yield* new SizeError({ - bytes: Buffer.byteLength(base64, "utf8"), + bytes, max: info.maxBase64Bytes, width: originalWidth, height: originalHeight, @@ -257,7 +249,7 @@ export const layer = Layer.effect( } return yield* new SizeError({ - bytes: Buffer.byteLength(base64, "utf8"), + bytes, max: info.maxBase64Bytes, width: originalWidth, height: originalHeight, diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts index b2d988b14bc..6b22b896139 100644 --- a/packages/opencode/src/index.ts +++ b/packages/opencode/src/index.ts @@ -3,10 +3,6 @@ import { hideBin } from "yargs/helpers" import { RunCommand } from "./cli/cmd/run" import { GenerateCommand } from "./cli/cmd/generate" import * as Log from "@opencode-ai/core/util/log" -// kilocode_change start -// import { LoginCommand, LogoutCommand, SwitchCommand, OrgsCommand } from "./cli/cmd/account" -// import { ConsoleCommand } from "./cli/cmd/account" -// kilocode_change end import { ConsoleCommand } from "./cli/cmd/account" import { ProvidersCommand } from "./cli/cmd/providers" import { AgentCommand } from "./cli/cmd/agent" @@ -15,58 +11,27 @@ import { UninstallCommand } from "./cli/cmd/uninstall" import { ModelsCommand } from "./cli/cmd/models" import { UI } from "./cli/ui" import { Installation } from "./installation" -import { InstallationBuildKind, InstallationVersion } from "@opencode-ai/core/installation/version" // kilocode_change - add InstallationBuildKind +import { InstallationVersion } from "@opencode-ai/core/installation/version" import { NamedError } from "@opencode-ai/core/util/error" import { FormatError } from "./cli/error" import { ServeCommand } from "./cli/cmd/serve" import { Filesystem } from "@/util/filesystem" -import { ConfigCommand as ConfigCLICommand } from "./cli/cmd/config" // kilocode_change import { DebugCommand } from "./cli/cmd/debug" import { StatsCommand } from "./cli/cmd/stats" import { McpCommand } from "./cli/cmd/mcp" -// import { GithubCommand } from "./cli/cmd/github" // kilocode_change +import { GithubCommand } from "./cli/cmd/github" import { ExportCommand } from "./cli/cmd/export" import { ImportCommand } from "./cli/cmd/import" import { AttachCommand } from "./cli/cmd/tui/attach" import { TuiThreadCommand } from "./cli/cmd/tui/thread" import { AcpCommand } from "./cli/cmd/acp" import { EOL } from "os" -// import { WebCommand } from "./cli/cmd/web" // kilocode_change (Disabled unsupported opencode web UI) +import { WebCommand } from "./cli/cmd/web" import { PrCommand } from "./cli/cmd/pr" import { SessionCommand } from "./cli/cmd/session" -import { RemoteCommand } from "./cli/cmd/remote" // kilocode_change -import { RollCallCommand } from "./kilocode/cli/cmd/roll-call" // kilocode_change -import { ProfileCommand } from "./kilocode/cli/cmd/profile" // kilocode_change -import { DevSetupCommand, DevAliasCommand } from "./kilocode/cli/dev-setup" // kilocode_change -import { DaemonCommand } from "./kilocode/cli/cmd/daemon" // kilocode_change -import { KiloConsoleCommand } from "./kilocode/cli/cmd/console" // kilocode_change -// kilocode_change start - Import telemetry, instance disposal, and legacy migration -import { Telemetry } from "@kilocode/kilo-telemetry" -import { InstanceRuntime } from "./project/instance-runtime" // kilocode_change -import { migrateLegacyKiloAuth, ENV_FEATURE, ENV_VERSION } from "@kilocode/kilo-gateway" -import { SessionExport } from "./kilocode/session-export" // kilocode_change - -// kilocode_change - set feature for tracking. 'serve' is spawned by other services -// (extension, cloud) which set their own KILOCODE_FEATURE env var. Direct CLI use -// (any command other than 'serve') is tagged as 'cli'. If 'serve' is spawned without -// the env var, it gets 'unknown' so the misconfiguration is visible in data. -if (!process.env[ENV_FEATURE]) { - const isServe = process.argv.includes("serve") - process.env[ENV_FEATURE] = isServe ? "unknown" : "cli" -} - -// kilocode_change - set version so kilo-gateway can include it in the editor name header -if (!process.env[ENV_VERSION]) { - process.env[ENV_VERSION] = InstallationVersion -} -import { Config } from "./config/config" -import { AppRuntime } from "./effect/app-runtime" -import { Auth } from "./auth" -// kilocode_change end import { DbCommand } from "./cli/cmd/db" import path from "path" import { Global } from "@opencode-ai/core/global" -import { createHelpCommand } from "./kilocode/help-command" // kilocode_change import { JsonMigration } from "@/storage/json-migration" import { Database } from "@/storage/db" import { errorMessage } from "./util/error" @@ -74,6 +39,7 @@ import { PluginCommand } from "./cli/cmd/plug" import { Heap } from "./cli/heap" import { drizzle } from "drizzle-orm/bun-sqlite" import { ensureProcessMetadata } from "@opencode-ai/core/util/opencode-process" +import { isRecord } from "@/util/record" const processMetadata = ensureProcessMetadata("main") @@ -93,18 +59,17 @@ const args = hideBin(process.argv) function show(out: string) { const text = out.trimStart() - const end = out.endsWith(EOL) ? "" : EOL // kilocode_change - keep shell prompt on the next line if (!text.startsWith("opencode ")) { process.stderr.write(UI.logo() + EOL + EOL) - process.stderr.write(text + end) // kilocode_change + process.stderr.write(text) return } - process.stderr.write(out + end) // kilocode_change + process.stderr.write(out) } -let cli = yargs(args) // kilocode_change +const cli = yargs(args) .parserConfiguration({ "populate--": true }) - .scriptName("kilo") // kilocode_change + .scriptName("opencode") .wrap(100) .help("help", "show help") .alias("help", "h") @@ -141,7 +106,7 @@ let cli = yargs(args) // kilocode_change Heap.start() process.env.AGENT = "1" - process.env.KILO = "1" // kilocode_change + process.env.OPENCODE = "1" process.env.KILO_PID = String(process.pid) Log.Default.info("opencode", { @@ -151,31 +116,6 @@ let cli = yargs(args) // kilocode_change run_id: processMetadata.runID, }) - // kilocode_change start - Initialize telemetry - const globalCfg = await AppRuntime.runPromise(Config.Service.use((cfg) => cfg.getGlobal())) - await Telemetry.init({ - dataPath: Global.Path.data, - version: InstallationVersion, - enabled: globalCfg.experimental?.openTelemetry !== false, - }) - - // Migrate legacy Kilo CLI auth if needed - await migrateLegacyKiloAuth( - async () => (await AppRuntime.runPromise(Auth.Service.use((svc) => svc.get("kilo")))) !== undefined, - async (auth) => AppRuntime.runPromise(Auth.Service.use((svc) => svc.set("kilo", auth))), - ) - - const kiloAuth = await AppRuntime.runPromise(Auth.Service.use((svc) => svc.get("kilo"))) - if (kiloAuth) { - const token = kiloAuth.type === "oauth" ? kiloAuth.access : kiloAuth.key - const accountId = kiloAuth.type === "oauth" ? kiloAuth.accountId : undefined - await Telemetry.updateIdentity(token, accountId) - } - - Telemetry.trackCliStart() - // kilocode_change end - - // kilocode_change start - one-time database migration progress const marker = path.join(Global.Path.data, "kilo.db") if (!(await Filesystem.exists(marker))) { const tty = process.stderr.isTTY @@ -212,7 +152,6 @@ let cli = yargs(args) // kilocode_change } process.stderr.write("Database migration complete." + EOL) } - // kilocode_change end }) .usage("") .completion("completion", "generate shell completion script") @@ -223,46 +162,22 @@ let cli = yargs(args) // kilocode_change .command(RunCommand) .command(GenerateCommand) .command(DebugCommand) - // kilocode_change start - // .command(LoginCommand) - // .command(LogoutCommand) - // .command(SwitchCommand) - // .command(OrgsCommand) - // .command(ConsoleCommand) - // kilocode_change end + .command(ConsoleCommand) .command(ProvidersCommand) .command(AgentCommand) .command(UpgradeCommand) .command(UninstallCommand) .command(ServeCommand) - // .command(WebCommand) // kilocode_change (Disabled unsupported opencode web UI) + .command(WebCommand) .command(ModelsCommand) - .command(RollCallCommand) // kilocode_change - .command(ProfileCommand) // kilocode_change .command(StatsCommand) .command(ExportCommand) .command(ImportCommand) - // .command(GithubCommand) // kilocode_change (Disabled until backend is ready) + .command(GithubCommand) .command(PrCommand) .command(SessionCommand) - .command(RemoteCommand) // kilocode_change - .command(DaemonCommand) // kilocode_change - .command(KiloConsoleCommand) // kilocode_change - .command(ConfigCLICommand) // kilocode_change .command(PluginCommand) .command(DbCommand) - -// kilocode_change start - dev-only commands are hidden from release builds -if (InstallationBuildKind !== "release") { - cli = cli.command(DevSetupCommand).command(DevAliasCommand) -} -// kilocode_change end - -// kilocode_change start - registered after initial chain to avoid self-referential type error -cli = cli.command(createHelpCommand(() => cli)) - -cli = cli - // kilocode_change end .fail((msg, err) => { if ( msg?.startsWith("Unknown argument") || @@ -289,13 +204,6 @@ try { } } catch (e) { let data: Record = {} - if (e instanceof NamedError) { - const obj = e.toObject() - Object.assign(data, { - ...obj.data, - }) - } - if (e instanceof Error) { Object.assign(data, { name: e.name, @@ -305,7 +213,16 @@ try { }) } - // kilocode_change start - log extra Bun resolve metadata for startup failures + if (e instanceof NamedError) { + const obj = e.toObject() + if (isRecord(obj.data)) { + for (const [key, value] of Object.entries(obj.data)) { + if (key === "name" || key === "stack" || key === "cause") continue + data[key] = value + } + } + } + if (e instanceof ResolveMessage) { Object.assign(data, { name: e.name, @@ -317,28 +234,18 @@ try { importKind: e.importKind, }) } - // kilocode_change end - Log.Default.error("fatal", data) // kilocode_change + Log.Default.error("fatal", data) const formatted = FormatError(e) if (formatted) UI.error(formatted) if (formatted === undefined) { - UI.error("Unexpected error, check log file at " + Log.file() + " for more details" + EOL) // kilocode_change + UI.error("Unexpected error, check log file at " + Log.file() + " for more details" + EOL) process.stderr.write(errorMessage(e) + EOL) } process.exitCode = 1 } finally { - // kilocode_change start - Track CLI exit and shutdown telemetry - const exitCode = typeof process.exitCode === "number" ? process.exitCode : undefined - Telemetry.trackCliExit(exitCode) - await SessionExport.shutdown() - await Telemetry.shutdown() - // kilocode_change end - - await InstanceRuntime.disposeAllInstances() // kilocode_change - safety net disposal (no-op if already disposed) - // Some subprocesses don't react properly to SIGTERM and similar signals. // Most notably, some docker-container-based MCP servers don't handle such signals unless // run using `docker run --init`. // Explicitly exit to avoid any hanging subprocesses. - process.exit() // kilocode_change + process.exit() } diff --git a/packages/opencode/src/installation/index.ts b/packages/opencode/src/installation/index.ts index 50d91d696ca..9b0e06c4af5 100644 --- a/packages/opencode/src/installation/index.ts +++ b/packages/opencode/src/installation/index.ts @@ -1,12 +1,11 @@ import { Effect, Layer, Schema, Context, Stream } from "effect" import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http" -import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { withTransientReadRetry } from "@/util/effect-http-client" -import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process" +import { errorMessage } from "@/util/error" +import { ChildProcess } from "effect/unstable/process" +import { AppProcess } from "@opencode-ai/core/process" import path from "path" -import z from "zod" import { BusEvent } from "@/bus/bus-event" -import { Flag } from "@opencode-ai/core/flag/flag" import * as Log from "@opencode-ai/core/util/log" import { makeRuntime } from "@opencode-ai/core/effect/runtime" import semver from "semver" @@ -45,17 +44,17 @@ export function getReleaseType(current: string, latest: string): ReleaseType { return "patch" } -export const Info = z - .object({ - version: z.string(), - latest: z.string(), - }) - .meta({ - ref: "InstallationInfo", - }) -export type Info = z.infer +export const Info = Schema.Struct({ + version: Schema.String, + latest: Schema.String, +}).annotate({ identifier: "InstallationInfo" }) +export type Info = Schema.Schema.Type -export const USER_AGENT = `kilo/${InstallationChannel}/${InstallationVersion}/${Flag.KILO_CLIENT}` // kilocode_change +export function userAgent(client = "cli") { + return `opencode/${InstallationChannel}/${InstallationVersion}/${client}` +} + +export const USER_AGENT = userAgent() export function isPreview() { return InstallationChannel !== "latest" @@ -90,252 +89,235 @@ export interface Interface { export class Service extends Context.Service()("@opencode/Installation") {} -export const layer: Layer.Layer = - Layer.effect( - Service, - Effect.gen(function* () { - const http = yield* HttpClient.HttpClient - const httpOk = HttpClient.filterStatusOk(withTransientReadRetry(http)) - const spawner = yield* ChildProcessSpawner.ChildProcessSpawner - - const text = Effect.fnUntraced( - function* (cmd: string[], opts?: { cwd?: string; env?: Record }) { - const proc = ChildProcess.make(cmd[0], cmd.slice(1), { +export const layer: Layer.Layer = Layer.effect( + Service, + Effect.gen(function* () { + const http = yield* HttpClient.HttpClient + const httpOk = HttpClient.filterStatusOk(withTransientReadRetry(http)) + const appProcess = yield* AppProcess.Service + + const text = Effect.fnUntraced( + function* (cmd: string[], opts?: { cwd?: string; env?: Record }) { + const result = yield* appProcess.run( + ChildProcess.make(cmd[0], cmd.slice(1), { cwd: opts?.cwd, env: opts?.env, extendEnv: true, - }) - const handle = yield* spawner.spawn(proc) - const out = yield* Stream.mkString(Stream.decodeText(handle.stdout)) - yield* handle.exitCode - return out - }, - Effect.scoped, - Effect.catch(() => Effect.succeed("")), - ) - - const run = Effect.fnUntraced( - function* (cmd: string[], opts?: { cwd?: string; env?: Record }) { - const proc = ChildProcess.make(cmd[0], cmd.slice(1), { + }), + ) + return result.stdout.toString("utf8") + }, + Effect.catch(() => Effect.succeed("")), + ) + + const run = Effect.fnUntraced( + function* (cmd: string[], opts?: { cwd?: string; env?: Record }) { + const result = yield* appProcess.run( + ChildProcess.make(cmd[0], cmd.slice(1), { cwd: opts?.cwd, env: opts?.env, extendEnv: true, - }) - const handle = yield* spawner.spawn(proc) - const [stdout, stderr] = yield* Effect.all( - [Stream.mkString(Stream.decodeText(handle.stdout)), Stream.mkString(Stream.decodeText(handle.stderr))], - { concurrency: 2 }, - ) - const code = yield* handle.exitCode - return { code, stdout, stderr } - }, - Effect.scoped, - Effect.catch(() => Effect.succeed({ code: ChildProcessSpawner.ExitCode(1), stdout: "", stderr: "" })), - ) - - const getBrewFormula = Effect.fnUntraced(function* () { - // kilocode_change start - const tapFormula = yield* text(["brew", "list", "--formula", "Kilo-Org/tap/kilo"]) - if (tapFormula.includes("kilo")) return "Kilo-Org/tap/kilo" - const coreFormula = yield* text(["brew", "list", "--formula", "kilo"]) - if (coreFormula.includes("kilo")) return "kilo" - return "kilo" - // kilocode_change end - }) - - const upgradeCurl = Effect.fnUntraced( - function* (target: string) { - const response = yield* httpOk.execute(HttpClientRequest.get("https://kilo.ai/install")) // kilocode_change - const body = yield* response.text - const bodyBytes = new TextEncoder().encode(body) - const proc = ChildProcess.make("bash", [], { - stdin: Stream.make(bodyBytes), - env: { VERSION: target }, - extendEnv: true, - }) - const handle = yield* spawner.spawn(proc) - const [stdout, stderr] = yield* Effect.all( - [Stream.mkString(Stream.decodeText(handle.stdout)), Stream.mkString(Stream.decodeText(handle.stderr))], - { concurrency: 2 }, - ) - const code = yield* handle.exitCode - return { code, stdout, stderr } - }, - Effect.scoped, - Effect.orDie, - ) - - const result: Interface = { - info: Effect.fn("Installation.info")(function* () { - return { - version: InstallationVersion, - latest: yield* result.latest(), - } + }), + ) + return { + code: result.exitCode, + stdout: result.stdout.toString("utf8"), + stderr: result.stderr.toString("utf8"), + } + }, + Effect.catch((err) => Effect.succeed({ code: 1, stdout: "", stderr: errorMessage(err) })), + ) + + const getBrewFormula = Effect.fnUntraced(function* () { + const tapFormula = yield* text(["brew", "list", "--formula", "anomalyco/tap/opencode"]) + if (tapFormula.includes("opencode")) return "anomalyco/tap/opencode" + const coreFormula = yield* text(["brew", "list", "--formula", "opencode"]) + if (coreFormula.includes("opencode")) return "opencode" + return "opencode" + }) + + const upgradeCurl = Effect.fnUntraced(function* (target: string) { + const response = yield* httpOk.execute(HttpClientRequest.get("https://opencode.ai/install")) + const body = yield* response.text + const bodyBytes = new TextEncoder().encode(body) + const result = yield* appProcess.run( + ChildProcess.make("bash", [], { + stdin: Stream.make(bodyBytes), + env: { VERSION: target }, + extendEnv: true, }), - method: Effect.fn("Installation.method")(function* () { - if (process.execPath.includes(path.join(".kilo", "bin"))) return "curl" as Method // kilocode_change - if (process.execPath.includes(path.join(".local", "bin"))) return "curl" as Method - const exec = process.execPath.toLowerCase() - - const checks: Array<{ name: Method; command: () => Effect.Effect }> = [ - { name: "npm", command: () => text(["npm", "list", "-g", "--depth=0"]) }, - { name: "yarn", command: () => text(["yarn", "global", "list"]) }, - { name: "pnpm", command: () => text(["pnpm", "list", "-g", "--depth=0"]) }, - { name: "bun", command: () => text(["bun", "pm", "ls", "-g"]) }, - // kilocode_change start - { name: "brew", command: () => text(["brew", "list", "--formula", "kilo"]) }, - { name: "scoop", command: () => text(["scoop", "list", "kilo"]) }, - { name: "choco", command: () => text(["choco", "list", "--limit-output", "kilo"]) }, - // kilocode_change end - ] - - checks.sort((a, b) => { - const aMatches = exec.includes(a.name) - const bMatches = exec.includes(b.name) - if (aMatches && !bMatches) return -1 - if (!aMatches && bMatches) return 1 - return 0 - }) - - for (const check of checks) { - const output = yield* check.command() - const installedName = - check.name === "brew" || check.name === "choco" || check.name === "scoop" ? "kilo" : "kilo" // kilocode_change - if (output.includes(installedName)) { - return check.name - } - } - - return "unknown" as Method - }), - latest: Effect.fn("Installation.latest")(function* (installMethod?: Method) { - const detectedMethod = installMethod || (yield* result.method()) - - if (detectedMethod === "brew") { - const formula = yield* getBrewFormula() - if (formula.includes("/")) { - const infoJson = yield* text(["brew", "info", "--json=v2", formula]) - const info = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(BrewInfoV2))(infoJson) - return info.formulae[0].versions.stable - } - const response = yield* httpOk.execute( - HttpClientRequest.get("https://formulae.brew.sh/api/formula/kilo.json").pipe( - // kilocode_change - HttpClientRequest.acceptJson, - ), - ) - const data = yield* HttpClientResponse.schemaBodyJson(BrewFormula)(response) - return data.versions.stable + ) + return { + code: result.exitCode, + stdout: result.stdout.toString("utf8"), + stderr: result.stderr.toString("utf8"), + } + }, Effect.orDie) + + const result: Interface = { + info: Effect.fn("Installation.info")(function* () { + return { + version: InstallationVersion, + latest: yield* result.latest(), + } + }), + method: Effect.fn("Installation.method")(function* () { + if (process.execPath.includes(path.join(".opencode", "bin"))) return "curl" as Method + if (process.execPath.includes(path.join(".local", "bin"))) return "curl" as Method + const exec = process.execPath.toLowerCase() + + const checks: Array<{ name: Method; command: () => Effect.Effect }> = [ + { name: "npm", command: () => text(["npm", "list", "-g", "--depth=0"]) }, + { name: "yarn", command: () => text(["yarn", "global", "list"]) }, + { name: "pnpm", command: () => text(["pnpm", "list", "-g", "--depth=0"]) }, + { name: "bun", command: () => text(["bun", "pm", "ls", "-g"]) }, + { name: "brew", command: () => text(["brew", "list", "--formula", "opencode"]) }, + { name: "scoop", command: () => text(["scoop", "list", "opencode"]) }, + { name: "choco", command: () => text(["choco", "list", "--limit-output", "opencode"]) }, + ] + + checks.sort((a, b) => { + const aMatches = exec.includes(a.name) + const bMatches = exec.includes(b.name) + if (aMatches && !bMatches) return -1 + if (!aMatches && bMatches) return 1 + return 0 + }) + + for (const check of checks) { + const output = yield* check.command() + const installedName = + check.name === "brew" || check.name === "choco" || check.name === "scoop" ? "opencode" : "opencode-ai" + if (output.includes(installedName)) { + return check.name } - - if (detectedMethod === "npm" || detectedMethod === "bun" || detectedMethod === "pnpm") { - const response = yield* httpOk.execute( - HttpClientRequest.get( - `${yield* NpmConfig.registry(process.cwd())}/@kilocode%2Fcli/${InstallationChannel}`, // kilocode_change - ).pipe(HttpClientRequest.acceptJson), - ) - const data = yield* HttpClientResponse.schemaBodyJson(NpmPackage)(response) - return data.version + } + + return "unknown" as Method + }), + latest: Effect.fn("Installation.latest")(function* (installMethod?: Method) { + const detectedMethod = installMethod || (yield* result.method()) + + if (detectedMethod === "brew") { + const formula = yield* getBrewFormula() + if (formula.includes("/")) { + const infoJson = yield* text(["brew", "info", "--json=v2", formula]) + const info = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(BrewInfoV2))(infoJson) + return info.formulae[0].versions.stable } + const response = yield* httpOk.execute( + HttpClientRequest.get("https://formulae.brew.sh/api/formula/opencode.json").pipe( + HttpClientRequest.acceptJson, + ), + ) + const data = yield* HttpClientResponse.schemaBodyJson(BrewFormula)(response) + return data.versions.stable + } - if (detectedMethod === "choco") { - const response = yield* httpOk.execute( - HttpClientRequest.get( - "https://community.chocolatey.org/api/v2/Packages?$filter=Id%20eq%20%27kilo%27%20and%20IsLatestVersion&$select=Version", // kilocode_change - ).pipe(HttpClientRequest.setHeaders({ Accept: "application/json;odata=verbose" })), - ) - const data = yield* HttpClientResponse.schemaBodyJson(ChocoPackage)(response) - return data.d.results[0].Version - } + if (detectedMethod === "npm" || detectedMethod === "bun" || detectedMethod === "pnpm") { + const response = yield* httpOk.execute( + HttpClientRequest.get( + `${yield* NpmConfig.registry(process.cwd())}/opencode-ai/${InstallationChannel}`, + ).pipe(HttpClientRequest.acceptJson), + ) + const data = yield* HttpClientResponse.schemaBodyJson(NpmPackage)(response) + return data.version + } - if (detectedMethod === "scoop") { - const response = yield* httpOk.execute( - HttpClientRequest.get( - "https://raw.githubusercontent.com/ScoopInstaller/Main/master/bucket/kilo.json", // kilocode_change - ).pipe(HttpClientRequest.setHeaders({ Accept: "application/json" })), - ) - const data = yield* HttpClientResponse.schemaBodyJson(ScoopManifest)(response) - return data.version - } + if (detectedMethod === "choco") { + const response = yield* httpOk.execute( + HttpClientRequest.get( + "https://community.chocolatey.org/api/v2/Packages?$filter=Id%20eq%20%27opencode%27%20and%20IsLatestVersion&$select=Version", + ).pipe(HttpClientRequest.setHeaders({ Accept: "application/json;odata=verbose" })), + ) + const data = yield* HttpClientResponse.schemaBodyJson(ChocoPackage)(response) + return data.d.results[0].Version + } + if (detectedMethod === "scoop") { const response = yield* httpOk.execute( - HttpClientRequest.get("https://api.github.com/repos/Kilo-Org/kilocode/releases/latest").pipe( - // kilocode_change - HttpClientRequest.acceptJson, - ), + HttpClientRequest.get( + "https://raw.githubusercontent.com/ScoopInstaller/Main/master/bucket/opencode.json", + ).pipe(HttpClientRequest.setHeaders({ Accept: "application/json" })), ) - const data = yield* HttpClientResponse.schemaBodyJson(GitHubRelease)(response) - return data.tag_name.replace(/^v/, "") - }, Effect.orDie), - upgrade: Effect.fn("Installation.upgrade")(function* (m: Method, target: string) { - let upgradeResult: { code: ChildProcessSpawner.ExitCode; stdout: string; stderr: string } | undefined - switch (m) { - case "curl": - upgradeResult = yield* upgradeCurl(target) - break - case "npm": - upgradeResult = yield* run(["npm", "install", "-g", `@kilocode/cli@${target}`]) // kilocode_change - break - case "pnpm": - upgradeResult = yield* run(["pnpm", "install", "-g", `@kilocode/cli@${target}`]) // kilocode_change - break - case "bun": - upgradeResult = yield* run(["bun", "install", "-g", `@kilocode/cli@${target}`]) // kilocode_change - break - case "brew": { - const formula = yield* getBrewFormula() - const env = { HOMEBREW_NO_AUTO_UPDATE: "1" } - if (formula.includes("/")) { - const tap = yield* run(["brew", "tap", "Kilo-Org/tap"], { env }) // kilocode_change - if (tap.code !== 0) { - upgradeResult = tap + const data = yield* HttpClientResponse.schemaBodyJson(ScoopManifest)(response) + return data.version + } + + const response = yield* httpOk.execute( + HttpClientRequest.get("https://api.github.com/repos/anomalyco/opencode/releases/latest").pipe( + HttpClientRequest.acceptJson, + ), + ) + const data = yield* HttpClientResponse.schemaBodyJson(GitHubRelease)(response) + return data.tag_name.replace(/^v/, "") + }, Effect.orDie), + upgrade: Effect.fn("Installation.upgrade")(function* (m: Method, target: string) { + let upgradeResult: { code: number; stdout: string; stderr: string } | undefined + switch (m) { + case "curl": + upgradeResult = yield* upgradeCurl(target) + break + case "npm": + upgradeResult = yield* run(["npm", "install", "-g", `opencode-ai@${target}`]) + break + case "pnpm": + upgradeResult = yield* run(["pnpm", "install", "-g", `opencode-ai@${target}`]) + break + case "bun": + upgradeResult = yield* run(["bun", "install", "-g", `opencode-ai@${target}`]) + break + case "brew": { + const formula = yield* getBrewFormula() + const env = { HOMEBREW_NO_AUTO_UPDATE: "1" } + if (formula.includes("/")) { + const tap = yield* run(["brew", "tap", "anomalyco/tap"], { env }) + if (tap.code !== 0) { + upgradeResult = tap + break + } + const repo = yield* text(["brew", "--repo", "anomalyco/tap"]) + const dir = repo.trim() + if (dir) { + const pull = yield* run(["git", "pull", "--ff-only"], { cwd: dir, env }) + if (pull.code !== 0) { + upgradeResult = pull break } - const repo = yield* text(["brew", "--repo", "Kilo-Org/tap"]) // kilocode_change - const dir = repo.trim() - if (dir) { - const pull = yield* run(["git", "pull", "--ff-only"], { cwd: dir, env }) - if (pull.code !== 0) { - upgradeResult = pull - break - } - } } - upgradeResult = yield* run(["brew", "upgrade", formula], { env }) - break } - case "choco": - upgradeResult = yield* run(["choco", "upgrade", "kilo", `--version=${target}`, "-y"]) // kilocode_change - break - case "scoop": - upgradeResult = yield* run(["scoop", "install", `kilo@${target}`]) // kilocode_change - break - default: - return yield* new UpgradeFailedError({ stderr: `Unknown method: ${m}` }) - } - if (!upgradeResult || upgradeResult.code !== 0) { - const stderr = m === "choco" ? "not running from an elevated command shell" : upgradeResult?.stderr || "" - return yield* new UpgradeFailedError({ stderr }) + upgradeResult = yield* run(["brew", "upgrade", formula], { env }) + break } - log.info("upgraded", { - method: m, - target, - stdout: upgradeResult.stdout, - stderr: upgradeResult.stderr, - }) - yield* text([process.execPath, "--version"]) - }), - } - - return Service.of(result) - }), - ) - -export const defaultLayer = layer.pipe( - Layer.provide(FetchHttpClient.layer), - Layer.provide(CrossSpawnSpawner.defaultLayer), + case "choco": + upgradeResult = yield* run(["choco", "upgrade", "opencode", `--version=${target}`, "-y"]) + break + case "scoop": + upgradeResult = yield* run(["scoop", "install", `opencode@${target}`]) + break + default: + return yield* new UpgradeFailedError({ stderr: `Unknown method: ${m}` }) + } + if (!upgradeResult || upgradeResult.code !== 0) { + const stderr = m === "choco" ? "not running from an elevated command shell" : upgradeResult?.stderr || "" + return yield* new UpgradeFailedError({ stderr }) + } + log.info("upgraded", { + method: m, + target, + stdout: upgradeResult.stdout, + stderr: upgradeResult.stderr, + }) + yield* text([process.execPath, "--version"]) + }), + } + + return Service.of(result) + }), ) +export const defaultLayer = layer.pipe(Layer.provide(FetchHttpClient.layer), Layer.provide(AppProcess.defaultLayer)) + const { runPromise } = makeRuntime(Service, defaultLayer) export const latest = (...args: Parameters) => runPromise((s) => s.latest(...args)) diff --git a/packages/opencode/src/kilocode/agent/index.ts b/packages/opencode/src/kilocode/agent/index.ts index c6760f96547..941209cd734 100644 --- a/packages/opencode/src/kilocode/agent/index.ts +++ b/packages/opencode/src/kilocode/agent/index.ts @@ -6,7 +6,7 @@ import * as Truncate from "../../tool/truncate" import { Config } from "../../config/config" import { Instance } from "../../project/instance" import { makeRuntime } from "@/effect/run-service" -import z from "zod" +import { Schema } from "effect" import path from "path" import { Global } from "@opencode-ai/core/global" @@ -448,13 +448,10 @@ export function patchAgents( } } -export const RemoveError = NamedError.create( - "AgentRemoveError", - z.object({ - name: z.string(), - message: z.string(), - }), -) +export const RemoveError = NamedError.create("AgentRemoveError", { + name: Schema.String, + message: Schema.String, +}) /** * Remove a custom agent by deleting its markdown source file and/or diff --git a/packages/opencode/src/kilocode/cli/cmd/tui/context/tui-config.tsx b/packages/opencode/src/kilocode/cli/cmd/tui/context/tui-config.tsx index 1416aeb2f57..5a667fe6938 100644 --- a/packages/opencode/src/kilocode/cli/cmd/tui/context/tui-config.tsx +++ b/packages/opencode/src/kilocode/cli/cmd/tui/context/tui-config.tsx @@ -8,6 +8,9 @@ import { createContext, useContext, type ParentProps } from "solid-js" import { createStore, reconcile } from "solid-js/store" import { TuiConfig } from "@/cli/cmd/tui/config/tui" +import { TuiKeybind } from "@/cli/cmd/tui/config/keybind" +import { KeymapLeaderTimeoutDefault } from "@/cli/cmd/tui/config/tui-schema" +import { createBindingLookup } from "@opentui/keymap/extras" export type SetTuiConfig = (next: TuiConfig.Info) => void @@ -19,7 +22,23 @@ export namespace KiloTuiConfig { export function makeStore(initial: TuiConfig.Resolved) { const [store, setStore] = createStore(initial) const set: SetTuiConfig = (next) => { - const config = TuiConfig.resolve(next) + const keybinds = TuiKeybind.parse(next.keybinds ?? {}) + const config: TuiConfig.Resolved = { + ...next, + attention: { + enabled: next.attention?.enabled ?? false, + notifications: next.attention?.notifications ?? true, + sound: next.attention?.sound ?? true, + volume: next.attention?.volume ?? 0.4, + sound_pack: next.attention?.sound_pack ?? "opencode.default", + sounds: next.attention?.sounds ?? {}, + }, + keybinds: createBindingLookup(TuiKeybind.toBindingConfig(keybinds), { + commandMap: TuiKeybind.CommandMap, + bindingDefaults: TuiKeybind.bindingDefaults(), + }), + leader_timeout: next.leader_timeout ?? KeymapLeaderTimeoutDefault, + } if (JSON.stringify(config.keybinds.bindings) === JSON.stringify(store.keybinds.bindings)) { config.keybinds = store.keybinds } diff --git a/packages/opencode/src/kilocode/commands.ts b/packages/opencode/src/kilocode/commands.ts index 89e0cd929c7..d8cefd2d55b 100644 --- a/packages/opencode/src/kilocode/commands.ts +++ b/packages/opencode/src/kilocode/commands.ts @@ -8,15 +8,18 @@ import { AttachCommand } from "../cli/cmd/tui/attach" import { RunCommand } from "../cli/cmd/run" import { GenerateCommand } from "../cli/cmd/generate" import { DebugCommand } from "../cli/cmd/debug" +import { ConsoleCommand } from "../cli/cmd/account" import { ProvidersCommand } from "../cli/cmd/providers" // kilocode_change — upstream renamed auth → providers import { AgentCommand } from "../cli/cmd/agent" import { UpgradeCommand } from "../cli/cmd/upgrade" import { UninstallCommand } from "../cli/cmd/uninstall" import { ServeCommand } from "../cli/cmd/serve" +import { WebCommand } from "../cli/cmd/web" import { ModelsCommand } from "../cli/cmd/models" import { StatsCommand } from "../cli/cmd/stats" import { ExportCommand } from "../cli/cmd/export" import { ImportCommand } from "../cli/cmd/import" +import { GithubCommand } from "../cli/cmd/github" import { PrCommand } from "../cli/cmd/pr" import { SessionCommand } from "../cli/cmd/session" import { RemoteCommand } from "../cli/cmd/remote" @@ -53,17 +56,20 @@ export const commands = [ RunCommand, GenerateCommand, DebugCommand, + ConsoleCommand, ProvidersCommand, // kilocode_change — upstream renamed AuthCommand → ProvidersCommand AgentCommand, UpgradeCommand, UninstallCommand, ServeCommand, + WebCommand, ModelsCommand, RollCallCommand, ProfileCommand, StatsCommand, ExportCommand, ImportCommand, + GithubCommand, PrCommand, SessionCommand, RemoteCommand, diff --git a/packages/opencode/src/kilocode/components/session-indexing.tsx b/packages/opencode/src/kilocode/components/session-indexing.tsx deleted file mode 100644 index fb31f610fe8..00000000000 --- a/packages/opencode/src/kilocode/components/session-indexing.tsx +++ /dev/null @@ -1,31 +0,0 @@ -// kilocode_change - new file -import { createMemo, Show } from "solid-js" -import { useSync } from "@tui/context/sync" -import { useTheme } from "@tui/context/theme" -import { indexingEnabled } from "../indexing-feature" -import { formatIndexingLabel } from "../indexing-label" -import type { IndexingStatusState } from "@kilocode/kilo-indexing/status" - -function tone(state: IndexingStatusState, theme: ReturnType["theme"]) { - if (state === "Complete") return theme.success - if (state === "Error") return theme.error - if (state === "In Progress") return theme.warning - if (state === "Standby") return theme.textMuted - return theme.textMuted -} - -export function SessionIndexing() { - const { theme } = useTheme() - const sync = useSync() - const enabled = createMemo(() => indexingEnabled(sync.data.config)) - const indexing = createMemo(() => sync.data.indexing) - const label = createMemo(() => formatIndexingLabel(indexing())) - - return ( - - - {label().slice(0, 48)} - - - ) -} diff --git a/packages/opencode/src/kilocode/config-validation.ts b/packages/opencode/src/kilocode/config-validation.ts index 0b7a7f2ba6a..39eddb67180 100644 --- a/packages/opencode/src/kilocode/config-validation.ts +++ b/packages/opencode/src/kilocode/config-validation.ts @@ -2,9 +2,9 @@ import path from "path" import { type ParseError, parse, printParseErrorCode } from "jsonc-parser" import { Schema } from "effect" -import z from "zod" import { ConfigProtection } from "./permission/config-paths" import { ConfigMarkdown } from "@/config/markdown" +import { ConfigParse } from "@/config/parse" import { Config } from "@/config/config" import { ConfigAgent } from "@/config/agent" import { ConfigCommand } from "@/config/command" @@ -48,9 +48,16 @@ export namespace ConfigValidation { return `\n\n\nERROR: Config file at ${label(filepath)} is not valid JSON(C)\n ${detail}\n` } - const result = Config.Info.zod.safeParse(data) - if (!result.success) { - const issues = result.error.issues.map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n") + const issues = (() => { + try { + ConfigParse.schema(Config.Info, data, filepath) + return undefined + } catch (err) { + if (err instanceof Error) return err.message + return String(err) + } + })() + if (issues) { return `\n\n\nWARNING: Configuration is invalid at ${label(filepath)}\n${issues}\n` } @@ -84,11 +91,8 @@ export namespace ConfigValidation { return `\n\n\nWARNING: Configuration is invalid at ${label(filepath)}\n${issues}\n` } } else { - const result = ConfigAgent.Info.zod.safeParse(config) - if (!result.success) { - const issues = result.error.issues - .map((i: z.core.$ZodIssue) => ` ${i.path.join(".")}: ${i.message}`) - .join("\n") + const issues = validateEffectSchema(ConfigAgent.Info, config) + if (issues) { return `\n\n\nWARNING: Configuration is invalid at ${label(filepath)}\n${issues}\n` } } diff --git a/packages/opencode/src/kilocode/config/config.ts b/packages/opencode/src/kilocode/config/config.ts index 6fd9f9a73e3..660b29ea482 100644 --- a/packages/opencode/src/kilocode/config/config.ts +++ b/packages/opencode/src/kilocode/config/config.ts @@ -1,7 +1,6 @@ import path from "path" import { pathToFileURL } from "url" import { existsSync } from "fs" -import z from "zod" import { Effect, Schema } from "effect" import { applyEdits, modify, parse as parseJsonc } from "jsonc-parser" import { mergeDeep } from "remeda" @@ -155,8 +154,10 @@ export namespace KilocodeConfig { return undefined } - /** Format Zod issues into a human-readable string. */ - export function formatIssues(issues: z.core.$ZodIssue[]) { + type Issue = { readonly message: string; readonly path: readonly string[]; readonly [key: string]: unknown } + + /** Format schema issues into a human-readable string. */ + export function formatIssues(issues: readonly Issue[]) { return issues .map((issue) => { const loc = issue.path.map(String).join(".") @@ -170,7 +171,7 @@ export namespace KilocodeConfig { export async function handleInvalid( kind: "agent" | "command", item: string, - issues: z.core.$ZodIssue[], + issues: readonly Issue[], cause: Error, warnings?: Config.Warning[], ) { @@ -342,15 +343,21 @@ export namespace KilocodeConfig { // no global config → new user, they'll get the new bash:ask default if (existing.length === 0 && !hasLegacy) return + const configs: Array<{ file: string; data: Record }> = [] // check if any config file already has an explicit bash permission for (const file of existing) { const text = await Bun.file(file) .text() .catch(() => "") const data = parseJsonc(text) ?? {} - if (data.permission?.bash) return + configs.push({ file, data }) + if (isRecord(data.permission) && data.permission.bash) return } + // A schema-only file is generated for editor completion. It does not mean + // the user predates the bash permission default. + if (!hasLegacy && configs.every((item) => Object.keys(item.data).every((key) => key === "$schema"))) return + // also check legacy TOML config for bash permission if (hasLegacy) { const toml = await import(pathToFileURL(legacy).href, { with: { type: "toml" } }).catch(() => undefined) diff --git a/packages/opencode/src/kilocode/config/default-plugins.ts b/packages/opencode/src/kilocode/config/default-plugins.ts index f17aafcf62c..6829a9efb7f 100644 --- a/packages/opencode/src/kilocode/config/default-plugins.ts +++ b/packages/opencode/src/kilocode/config/default-plugins.ts @@ -1,5 +1,5 @@ import { createRequire } from "module" -import type { ConfigPlugin } from "@/config/plugin" +import { ConfigPlugin } from "@/config/plugin" import { isIndexingPlugin } from "@kilocode/kilo-indexing/detect" import { ensureAtomicChatPlugin, resolveAtomicChatPlugin } from "@/kilocode/atomic-chat-feature" import { ensureIndexingPlugin, resolveIndexingPlugin } from "@/kilocode/indexing-feature" @@ -24,7 +24,19 @@ export namespace KilocodeDefaultPlugins { cfg.plugin = plugins // Built-in indexing is not loaded through external plugins and must not wait for their setup. - cfg.plugin_origins = cfg.plugin_origins?.filter((item) => !isIndexingPlugin(item.spec)) + const origins = cfg.plugin_origins?.filter((item) => !isIndexingPlugin(item.spec)) + if (!origins) return cfg + if (opts.disabled) { + cfg.plugin_origins = origins + return cfg + } + const known = new Set(origins.map((item) => ConfigPlugin.pluginSpecifier(item.spec))) + cfg.plugin_origins = [ + ...origins, + ...plugins + .filter((spec) => !known.has(ConfigPlugin.pluginSpecifier(spec))) + .map((spec) => ({ spec, source: "builtin", scope: "global" as const })), + ] return cfg } } diff --git a/packages/opencode/src/kilocode/config/overlay.ts b/packages/opencode/src/kilocode/config/overlay.ts index f6c47573fe0..4751786c687 100644 --- a/packages/opencode/src/kilocode/config/overlay.ts +++ b/packages/opencode/src/kilocode/config/overlay.ts @@ -1,5 +1,6 @@ import path from "path" import { existsSync } from "fs" +import { Schema } from "effect" import z from "zod" import { Global } from "@opencode-ai/core/global" import { ConfigAgent } from "@/config/agent" @@ -46,9 +47,9 @@ export namespace KilocodeConfigOverlay { export const Result = z.object({ scope: Scope, - effective: Config.Info.zod, - global: Config.Info.zod, - project: Config.Info.zod, + effective: z.custom(Schema.is(Config.Info)), + global: z.custom(Schema.is(Config.Info)), + project: z.custom(Schema.is(Config.Info)), sources: z.array(KilocodeConfigSources.Source), targets: z.object({ global: z.string().optional(), @@ -176,7 +177,7 @@ export namespace KilocodeConfigOverlay { const expanded = await ConfigVariable.substitute({ text, type: "path", path: file }) const parsed = ConfigParse.jsonc(expanded, file) if (!isRecord(parsed)) return {} - return ConfigParse.effectSchema(Config.Info, parsed, file) as Config.Info + return ConfigParse.schema(Config.Info, parsed, file) as Config.Info } function field( diff --git a/packages/opencode/src/kilocode/fn.ts b/packages/opencode/src/kilocode/fn.ts new file mode 100644 index 00000000000..797f1a976f5 --- /dev/null +++ b/packages/opencode/src/kilocode/fn.ts @@ -0,0 +1,8 @@ +import { z } from "zod" + +export function fn(schema: T, cb: (input: z.infer) => Result) { + const result = (input: z.infer) => cb(schema.parse(input)) + result.force = (input: z.infer) => cb(input) + result.schema = schema + return result +} diff --git a/packages/opencode/src/kilocode/indexing-label.ts b/packages/opencode/src/kilocode/indexing-label.ts index 96f77ea21c2..ba4316812e5 100644 --- a/packages/opencode/src/kilocode/indexing-label.ts +++ b/packages/opencode/src/kilocode/indexing-label.ts @@ -2,17 +2,13 @@ import type { IndexingStatus } from "@kilocode/kilo-indexing/status" export function formatIndexingLabel(status: IndexingStatus): string { if (status.state === "In Progress") { - if (status.totalFiles <= 0) return "IDX In Progress" - return `IDX ${status.percent}% ${status.processedFiles}/${status.totalFiles}` + if (status.totalFiles <= 0) return status.percent > 0 ? `${status.percent}%` : "In progress" + return `${status.percent}% (${status.processedFiles}/${status.totalFiles} files)` } if (status.state === "Error") { - return `IDX ${status.message}` + return status.message || "Failed" } - if (status.state === "Standby") { - return "IDX Standby" - } - - return `IDX ${status.state}` + return status.state } diff --git a/packages/opencode/src/kilocode/kilo-errors.ts b/packages/opencode/src/kilocode/kilo-errors.ts index 6409f66bb7a..27425c8c0e1 100644 --- a/packages/opencode/src/kilocode/kilo-errors.ts +++ b/packages/opencode/src/kilocode/kilo-errors.ts @@ -1,4 +1,5 @@ import type { NamedError } from "@opencode-ai/core/util/error" +import { isRecord } from "@/util/record" export const KILO_ERROR_CODES = { PAID_MODEL_AUTH_REQUIRED: "PAID_MODEL_AUTH_REQUIRED", @@ -69,7 +70,7 @@ export function showKiloErrorToast( */ export function parseKiloErrorCode(error: ReturnType): KiloErrorCode | undefined { if (error.name !== "APIError") return undefined - const responseBody = error.data?.responseBody + const responseBody = isRecord(error.data) ? error.data.responseBody : undefined if (typeof responseBody !== "string") return undefined try { const body = JSON.parse(responseBody) diff --git a/packages/opencode/src/kilocode/plan-file.ts b/packages/opencode/src/kilocode/plan-file.ts index da8beb32699..2f10868e6f5 100644 --- a/packages/opencode/src/kilocode/plan-file.ts +++ b/packages/opencode/src/kilocode/plan-file.ts @@ -7,9 +7,7 @@ export namespace PlanFile { export function latest(messages: MessageV2.WithParts[]) { const exit = messages .flatMap((m) => m.parts) - .findLast( - (part) => part.type === "tool" && part.tool === "plan_exit" && part.state.status === "completed", - ) + .findLast((part) => part.type === "tool" && part.tool === "plan_exit" && part.state.status === "completed") if (exit?.type !== "tool" || exit.state.status !== "completed") return const meta = exit.state.metadata ?? {} const input = exit.state.input ?? {} diff --git a/packages/opencode/src/kilocode/plan-followup.ts b/packages/opencode/src/kilocode/plan-followup.ts index 57cfdfa3708..73098ccd377 100644 --- a/packages/opencode/src/kilocode/plan-followup.ts +++ b/packages/opencode/src/kilocode/plan-followup.ts @@ -17,7 +17,7 @@ import { MessageV2 } from "@/session/message-v2" import { SessionStatus } from "@/session/status" import { Todo } from "@/session/todo" import { makeRuntime } from "@/effect/run-service" -import { Effect } from "effect" +import { Effect, Schema } from "effect" import * as Log from "@opencode-ai/core/util/log" import { KiloSessionPromptQueue } from "@/kilocode/session/prompt-queue" import { lazy } from "@/util/lazy" @@ -178,7 +178,12 @@ export namespace PlanFollowup { const ModelState = z .object({ - model: z.record(z.string(), z.object({ providerID: ProviderID.zod, modelID: ModelID.zod })).optional(), + model: z + .record( + z.string(), + z.object({ providerID: z.custom(Schema.is(ProviderID)), modelID: z.custom(Schema.is(ModelID)) }), + ) + .optional(), variant: z.record(z.string(), z.string().optional()).optional(), }) .passthrough() @@ -238,7 +243,9 @@ export namespace PlanFollowup { const session = await PlanFollowupRuntime.session((svc) => svc.get(SessionID.make(input.sessionID))) const file = PlanFile.resolve(PlanFile.latest(input.messages), Instance.current) ?? Session.plan(session, Instance.current) - const plan = await Bun.file(file).text().catch(() => "") + const plan = await Bun.file(file) + .text() + .catch(() => "") return plan.trim() } @@ -379,7 +386,9 @@ export namespace PlanFollowup { // The section order is fixed so the initial and final renders stay // aligned; only the handover block grows in between. const compose = (handover: string) => { - const sections = [`Plan file: ${file}\nRead this file first and treat it as the source of truth for implementation.`] + const sections = [ + `Plan file: ${file}\nRead this file first and treat it as the source of truth for implementation.`, + ] if (handover) sections.push(`## Handover from Planning Session\n\n${handover}`) if (todoList) sections.push(`## Todo List\n\n${todoList}`) return sections.join("\n\n") diff --git a/packages/opencode/src/kilocode/plugins/home-footer.tsx b/packages/opencode/src/kilocode/plugins/home-footer.tsx index 4554bb660e3..6b1ce02450d 100644 --- a/packages/opencode/src/kilocode/plugins/home-footer.tsx +++ b/packages/opencode/src/kilocode/plugins/home-footer.tsx @@ -9,9 +9,6 @@ import type { TuiPlugin, TuiPluginApi, TuiPluginModule } from "@kilocode/plugin/tui" import { createMemo, createSignal, Match, onCleanup, onMount, Show, Switch } from "solid-js" import { Global } from "@opencode-ai/core/global" -import { indexingEnabled } from "../indexing-feature" -import { formatIndexingLabel } from "../indexing-label" -import { useSync } from "@/cli/cmd/tui/context/sync" const id = "internal:kilo-home-footer" @@ -108,18 +105,6 @@ function Version(props: { api: TuiPluginApi }) { function View(props: { api: TuiPluginApi }) { const kilo = createMemo(() => props.api.state.provider.some((p) => p.id === "kilo")) - const theme = () => props.api.theme.current - const sync = useSync() - const indexingOn = createMemo(() => indexingEnabled(sync.data.config)) - const indexing = createMemo(() => sync.data.indexing) - const indexingLabel = createMemo(() => formatIndexingLabel(indexing())) - const indexingColor = createMemo(() => { - if (indexing().state === "Complete") return theme().success - if (indexing().state === "Error") return theme().error - if (indexing().state === "In Progress") return theme().warning - if (indexing().state === "Standby") return theme().textMuted - return theme().textMuted - }) return ( - - {indexingLabel().slice(0, 48)} - diff --git a/packages/opencode/src/kilocode/plugins/sidebar-indexing.tsx b/packages/opencode/src/kilocode/plugins/sidebar-indexing.tsx new file mode 100644 index 00000000000..f8667007ab9 --- /dev/null +++ b/packages/opencode/src/kilocode/plugins/sidebar-indexing.tsx @@ -0,0 +1,101 @@ +import type { TuiPlugin, TuiPluginApi, TuiPluginModule } from "@kilocode/plugin/tui" +import { createEffect, createMemo, createSignal, onCleanup, onMount, Show } from "solid-js" +import type { IndexingStatus, IndexingStatusState } from "@kilocode/kilo-indexing/status" +import { useSync } from "@/cli/cmd/tui/context/sync" +import { formatIndexingLabel } from "../indexing-label" +import { indexingEnabled } from "../indexing-feature" + +const id = "internal:kilo-sidebar-indexing" + +function tone(state: IndexingStatusState, api: TuiPluginApi) { + const theme = api.theme.current + if (state === "Complete") return theme.success + if (state === "Error") return theme.error + if (state === "In Progress") return theme.warning + return theme.textMuted +} + +function message(status: IndexingStatus) { + const label = formatIndexingLabel(status) + const msg = status.message.trim() + if (!msg || msg === label) return undefined + const plain = msg + .replace(/^codebase indexing (is )?/i, "") + .replace(/^indexing (is )?/i, "") + .replace(/[.!]+$/, "") + .toLowerCase() + if (plain === label.toLowerCase()) return undefined + return msg +} + +function View(props: { api: TuiPluginApi }) { + const theme = () => props.api.theme.current + const sync = useSync() + const enabled = createMemo(() => indexingEnabled(sync.data.config)) + const configured = createMemo( + () => sync.data.config.indexing?.enabled === true || sync.data.globalConfig.indexing?.enabled === true, + ) + const [status, setStatus] = createSignal(sync.data.indexing) + const label = createMemo(() => formatIndexingLabel(status())) + const msg = createMemo(() => message(status())) + const refresh = () => { + if (!enabled() || !configured()) return + const params = props.api.state.path.directory ? { directory: props.api.state.path.directory } : undefined + void props.api.client.indexing + .status(params) + .then((res) => { + if (res.data) setStatus(res.data) + }) + .catch(() => undefined) + } + + createEffect(() => { + setStatus(sync.data.indexing) + }) + + onMount(() => { + refresh() + const timer = setInterval(() => { + if (status().state === "Complete" || status().state === "Error") return + refresh() + }, 1000) + onCleanup(() => clearInterval(timer)) + }) + + return ( + + + + Code Indexing + + + + • + + + {label()} + + + {(text) => {text()}} + + + ) +} + +const tui: TuiPlugin = async (api) => { + api.slots.register({ + order: 225, + slots: { + sidebar_content() { + return + }, + }, + }) +} + +const plugin: TuiPluginModule & { id: string } = { + id, + tui, +} + +export default plugin diff --git a/packages/opencode/src/kilocode/session/cost-propagation.ts b/packages/opencode/src/kilocode/session/cost-propagation.ts index 2eb3daef7ff..631d665de39 100644 --- a/packages/opencode/src/kilocode/session/cost-propagation.ts +++ b/packages/opencode/src/kilocode/session/cost-propagation.ts @@ -58,7 +58,7 @@ export namespace KiloCostPropagation { Effect.promise(() => acquire(`${sid}:${mid}`)), () => Effect.gen(function* () { - const parent = yield* Effect.sync(() => MessageV2.get({ sessionID: sid, messageID: mid })) + const parent = yield* MessageV2.get({ sessionID: sid, messageID: mid }) if (parent.info.role !== "assistant") return parent.info.cost += amount yield* sessions.updateMessage(parent.info) diff --git a/packages/opencode/src/kilocode/session/index.ts b/packages/opencode/src/kilocode/session/index.ts index d970c108618..3db9fd383e4 100644 --- a/packages/opencode/src/kilocode/session/index.ts +++ b/packages/opencode/src/kilocode/session/index.ts @@ -5,7 +5,6 @@ import { BusEvent } from "@/bus/bus-event" import { EffectBridge } from "@/effect/bridge" import { Session } from "@/session/session" import { MessageID, SessionID } from "@/session/schema" -import { fn } from "@/util/fn" import { Database, eq, and, gte, isNull, desc, like, inArray, lt, or } from "@/storage/db" import type { SQL } from "@/storage/db" import { ProjectTable } from "@/project/project.sql" @@ -17,6 +16,7 @@ import type { LanguageModelUsage, ProviderMetadata } from "ai" import type { Provider } from "@/provider/provider" import { zod as toZod } from "@opencode-ai/core/effect-zod" import { ENV_FEATURE } from "@kilocode/kilo-gateway" +import { fn } from "@/kilocode/fn" export namespace KiloSession { const log = Log.create({ service: "session.kilo" }) @@ -270,16 +270,24 @@ export namespace KiloSession { // These helpers catch that specific error and log a warning instead. // --------------------------------------------------------------------------- - export function runSyncSafe(run: () => void, context: { type: string; id: string; sessionID: string }): void { - try { - run() - } catch (e: any) { - if (e?.code === "SQLITE_CONSTRAINT_FOREIGNKEY") { - log.warn(`skipping ${context.type} for deleted session`, { id: context.id, sessionID: context.sessionID }) - return - } - throw e - } + export function runSyncSafe( + run: Effect.Effect, + context: { type: string; id: string; sessionID: string }, + ) { + return run.pipe( + Effect.catchCause((cause) => { + const err = Cause.squash(cause) + if (typeof err === "object" && err !== null && "code" in err && err.code === "SQLITE_CONSTRAINT_FOREIGNKEY") { + return Effect.sync(() => + log.warn(`skipping ${context.type} for deleted session`, { + id: context.id, + sessionID: context.sessionID, + }), + ) + } + return Effect.failCause(cause) + }), + ) } // --------------------------------------------------------------------------- @@ -289,7 +297,7 @@ export namespace KiloSession { /** Schema for project summary returned by listGlobal. */ export const ProjectInfo = z .object({ - id: ProjectID.zod, + id: z.custom(Schema.is(ProjectID)), name: z.string().optional(), worktree: z.string(), }) diff --git a/packages/opencode/src/kilocode/tool/registry.ts b/packages/opencode/src/kilocode/tool/registry.ts index d1c842c5dcb..230a3db587a 100644 --- a/packages/opencode/src/kilocode/tool/registry.ts +++ b/packages/opencode/src/kilocode/tool/registry.ts @@ -55,9 +55,7 @@ export namespace KiloToolRegistry { function semanticTool(deps: Deps, loaders: Loaders) { return Effect.gen(function* () { const indexing = loaders.indexing ?? (() => import("@/kilocode/indexing")) - const ready = yield* Effect.tryPromise(() => - indexing().then((mod) => mod.KiloIndexing.ready()), - ).pipe( + const ready = yield* Effect.tryPromise(() => indexing().then((mod) => mod.KiloIndexing.ready())).pipe( Effect.catch((err) => Effect.sync(() => { log.warn("semantic search unavailable", { err }) diff --git a/packages/opencode/src/kilocode/tool/task.ts b/packages/opencode/src/kilocode/tool/task.ts index c7fae25e18c..5f2be34b315 100644 --- a/packages/opencode/src/kilocode/tool/task.ts +++ b/packages/opencode/src/kilocode/tool/task.ts @@ -1,5 +1,5 @@ // kilocode_change - new file -import { Effect } from "effect" +import { Effect, Schema } from "effect" import path from "path" import { Permission } from "@/permission" import { Flag } from "@opencode-ai/core/flag/flag" @@ -17,7 +17,12 @@ const log = Log.create({ service: "kilocode-task-model" }) // RATIONALE: Mirror narrow state slice Task tool consumes and ignore unrelated TUI fields. const ModelState = z .object({ - model: z.record(z.string(), z.object({ providerID: ProviderID.zod, modelID: ModelID.zod })).optional(), + model: z + .record( + z.string(), + z.object({ providerID: z.custom(Schema.is(ProviderID)), modelID: z.custom(Schema.is(ModelID)) }), + ) + .optional(), variant: z.record(z.string(), z.string().optional()).optional(), }) .passthrough() @@ -130,7 +135,7 @@ export namespace KiloTask { if (!choice) continue if (choice.direct) return { model: choice.model, variant: choice.variant } const full = yield* input.provider.getModel(choice.model.providerID, choice.model.modelID).pipe( - Effect.catchDefect((err) => + Effect.catchTag("ProviderModelNotFoundError", (err) => Effect.sync(() => { log.debug("skipping unavailable task subagent model", { providerID: choice.model.providerID, diff --git a/packages/opencode/src/kilocode/tui/config.ts b/packages/opencode/src/kilocode/tui/config.ts index d0988f12f60..a5d8beeeec0 100644 --- a/packages/opencode/src/kilocode/tui/config.ts +++ b/packages/opencode/src/kilocode/tui/config.ts @@ -1,6 +1,6 @@ import path from "path" import z from "zod" -import { Effect, Layer } from "effect" +import { Effect, Layer, Schema } from "effect" import { applyEdits, modify } from "jsonc-parser" import { mergeDeep } from "remeda" import { Global } from "@opencode-ai/core/global" @@ -8,6 +8,7 @@ import { ConfigParse } from "@/config/parse" import { CurrentWorkingDirectory } from "@/cli/cmd/tui/config/cwd" import { TuiConfig } from "@/cli/cmd/tui/config/tui" import { TuiInfo } from "@/cli/cmd/tui/config/tui-schema" +import { KilocodeKeybinds } from "./keybinds" import { Filesystem } from "@/util/filesystem" import { isRecord } from "@/util/record" import { GlobalBus } from "@/bus/global" @@ -18,7 +19,7 @@ export namespace KilocodeTuiConfig { export type Scope = z.infer export const Patch = TuiInfo - export type Patch = z.output + export type Patch = Schema.Schema.Type export type Editable = Omit & { keybinds?: Record } const files = ["tui.jsonc", "tui.json"] as const @@ -108,20 +109,20 @@ export namespace KilocodeTuiConfig { } function merge(base: Patch, patch: Patch): Patch { - return writable(mergeDeep(base, patch)) + return writable(mergeDeep(base, patch), false) } - function writable(config: TuiConfig.Info): Editable { + function writable(config: Patch | TuiConfig.Info, defaults = true): Editable { const result = { ...config } as Record delete result.plugin_origins - const keybinds = Object.fromEntries( - Object.entries(config.keybinds ?? {}).flatMap(([key, value]) => { - if (typeof value === "string") return [[key, value]] - if (value === false) return [[key, "none"]] - return [] - }), - ) - if (config.keybinds) result.keybinds = keybinds + const keybinds: Record = defaults + ? Object.fromEntries(KilocodeKeybinds.list().map((item) => [item.id, item.default])) + : {} + for (const [key, value] of Object.entries(config.keybinds ?? {})) { + if (typeof value === "string") keybinds[key] = value + if (value === false) keybinds[key] = "none" + } + if (defaults || config.keybinds) result.keybinds = keybinds else delete result.keybinds for (const key of Object.keys(result)) { diff --git a/packages/opencode/src/kilocode/tui/keybinds.ts b/packages/opencode/src/kilocode/tui/keybinds.ts index 00acdca61be..ec1f335ae8f 100644 --- a/packages/opencode/src/kilocode/tui/keybinds.ts +++ b/packages/opencode/src/kilocode/tui/keybinds.ts @@ -60,12 +60,12 @@ export namespace KilocodeKeybinds { } export function list(): Info[] { - return Object.entries(TuiKeybind.Keybinds.shape).map(([id, schema]) => ({ + return Object.entries(TuiKeybind.Definitions).map(([id, definition]) => ({ id, label: label(id), group: group(id), - default: fallback(id, schema.parse(undefined)), - description: TuiKeybind.Descriptions[id as keyof typeof TuiKeybind.Descriptions] ?? label(id), + default: fallback(id, definition.default), + description: definition.description, })) } } diff --git a/packages/opencode/src/lsp/client.ts b/packages/opencode/src/lsp/client.ts index 809ea95091b..30577a8f116 100644 --- a/packages/opencode/src/lsp/client.ts +++ b/packages/opencode/src/lsp/client.ts @@ -7,10 +7,8 @@ import type { Diagnostic as VSCodeDiagnostic } from "vscode-languageserver-types import * as Log from "@opencode-ai/core/util/log" import { Process } from "@/util/process" import { LANGUAGE_EXTENSIONS } from "./language" -import z from "zod" import { Schema } from "effect" import type * as LSPServer from "./server" -import { NamedError } from "@opencode-ai/core/util/error" import { withTimeout } from "../util/timeout" import { Filesystem } from "@/util/filesystem" @@ -32,12 +30,10 @@ export type Info = NonNullable>> export type Diagnostic = VSCodeDiagnostic -export const InitializeError = NamedError.create( - "LSPInitializeError", - z.object({ - serverID: z.string(), - }), -) +export class InitializeError extends Schema.TaggedErrorClass()("LSPInitializeError", { + serverID: Schema.String, + cause: Schema.optional(Schema.Defect), +}) {} export const Event = { Diagnostics: BusEvent.define( @@ -279,12 +275,7 @@ export async function create(input: { serverID: string; server: LSPServer.Handle INITIALIZE_TIMEOUT_MS, ).catch((err) => { logger.error("initialize error", { error: err }) - throw new InitializeError( - { serverID: input.serverID }, - { - cause: err, - }, - ) + throw new InitializeError({ serverID: input.serverID, cause: err }) }) const syncKind = getSyncKind(initialized.capabilities) diff --git a/packages/opencode/src/lsp/lsp.ts b/packages/opencode/src/lsp/lsp.ts index dadd02a5c8f..165bb4477ba 100644 --- a/packages/opencode/src/lsp/lsp.ts +++ b/packages/opencode/src/lsp/lsp.ts @@ -5,17 +5,15 @@ import * as LSPClient from "./client" import path from "path" import { pathToFileURL, fileURLToPath } from "url" import * as LSPServer from "./server" -import z from "zod" import { Config } from "@/config/config" -import { Flag } from "@opencode-ai/core/flag/flag" import { Process } from "@/util/process" import { spawn as lspspawn } from "./launch" import { Effect, Layer, Context, Schema } from "effect" import { InstanceState } from "@/effect/instance-state" import { containsPath } from "@/project/instance-context" import { TsClient } from "../kilocode/ts-client" // kilocode_change -import { NonNegativeInt, withStatics } from "@opencode-ai/core/schema" -import { zod, ZodOverride } from "@opencode-ai/core/effect-zod" +import { NonNegativeInt } from "@opencode-ai/core/schema" +import { RuntimeFlags } from "@/effect/runtime-flags" const log = Log.create({ service: "lsp" }) @@ -31,9 +29,7 @@ const Position = Schema.Struct({ export const Range = Schema.Struct({ start: Position, end: Position, -}) - .annotate({ identifier: "Range" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "Range" }) export type Range = typeof Range.Type export const Symbol = Schema.Struct({ @@ -43,9 +39,7 @@ export const Symbol = Schema.Struct({ uri: Schema.String, range: Range, }), -}) - .annotate({ identifier: "Symbol" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "Symbol" }) export type Symbol = typeof Symbol.Type export const DocumentSymbol = Schema.Struct({ @@ -54,21 +48,15 @@ export const DocumentSymbol = Schema.Struct({ kind: NonNegativeInt, range: Range, selectionRange: Range, -}) - .annotate({ identifier: "DocumentSymbol" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "DocumentSymbol" }) export type DocumentSymbol = typeof DocumentSymbol.Type export const Status = Schema.Struct({ id: Schema.String, name: Schema.String, root: Schema.String, - status: Schema.Literals(["connected", "error"]).annotate({ - [ZodOverride]: z.union([z.literal("connected"), z.literal("error")]), - }), -}) - .annotate({ identifier: "LSPStatus" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) + status: Schema.Literals(["connected", "error"]), +}).annotate({ identifier: "LSPStatus" }) export type Status = typeof Status.Type enum SymbolKind { @@ -111,8 +99,8 @@ const kinds = [ SymbolKind.Enum, ] -const filterExperimentalServers = (servers: Record) => { - if (Flag.KILO_EXPERIMENTAL_LSP_TY) { +const filterExperimentalServers = (servers: Record, flags: RuntimeFlags.Info) => { + if (flags.experimentalLspTy) { if (servers["pyright"]) { log.info("LSP server pyright is disabled because KILO_EXPERIMENTAL_LSP_TY is enabled") delete servers["pyright"] @@ -156,6 +144,7 @@ export const layer = Layer.effect( Service, Effect.gen(function* () { const config = yield* Config.Service + const flags = yield* RuntimeFlags.Service const state = yield* InstanceState.make( Effect.fn("LSP.state")(function* (ctx) { @@ -170,7 +159,7 @@ export const layer = Layer.effect( servers[server.id] = server } - filterExperimentalServers(servers) + filterExperimentalServers(servers, flags) if (cfg.lsp !== true) { for (const [name, item] of Object.entries(cfg.lsp)) { @@ -230,7 +219,7 @@ export const layer = Layer.effect( async function schedule(server: LSPServer.Info, root: string, key: string) { const handle = await server - .spawn(root, ctx) + .spawn(root, ctx, flags) .then((value) => { if (!value) s.broken.add(key) return value @@ -276,7 +265,7 @@ export const layer = Layer.effect( if (s.broken.has(root + server.id)) continue // kilocode_change start - use lightweight tsgo-based client when persistent LSP is not enabled - if (server.id === "typescript" && !Flag.KILO_EXPERIMENTAL_LSP_TOOL) { + if (server.id === "typescript" && !flags.experimentalLspTool) { const existing = s.clients.find((x) => x.root === root && x.serverID === server.id) if (existing) { result.push(existing) @@ -526,7 +515,7 @@ export const layer = Layer.effect( }), ) -export const defaultLayer = layer.pipe(Layer.provide(Config.defaultLayer)) +export const defaultLayer = layer.pipe(Layer.provide(Config.defaultLayer), Layer.provide(RuntimeFlags.defaultLayer)) export * as Diagnostic from "./diagnostic" diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index 00927954a5b..ba4344026ca 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -15,6 +15,7 @@ import { Module } from "@opencode-ai/core/util/module" import { spawn } from "./launch" import { Npm } from "@opencode-ai/core/npm" import { TsCheck } from "../kilocode/ts-check" // kilocode_change +import type { RuntimeFlags } from "@/effect/runtime-flags" const log = Log.create({ service: "lsp.server" }) const pathExists = async (p: string) => @@ -61,7 +62,7 @@ export interface Info { extensions: string[] global?: boolean root: RootFunction - spawn(root: string, ctx: InstanceContext): Promise + spawn(root: string, ctx: InstanceContext, flags: RuntimeFlags.Info): Promise } export const Deno: Info = { @@ -429,8 +430,8 @@ export const Ty: Info = { "Pipfile", "pyrightconfig.json", ]), - async spawn(root) { - if (!Flag.KILO_EXPERIMENTAL_LSP_TY) { + async spawn(root, _ctx, flags) { + if (!flags.experimentalLspTy) { return undefined } diff --git a/packages/opencode/src/mcp/auth.ts b/packages/opencode/src/mcp/auth.ts index b07d59870bc..be19be0af04 100644 --- a/packages/opencode/src/mcp/auth.ts +++ b/packages/opencode/src/mcp/auth.ts @@ -1,33 +1,35 @@ import path from "path" -import z from "zod" import { Global } from "@opencode-ai/core/global" -import { Effect, Layer, Context } from "effect" +import { Effect, Layer, Context, Option, Schema } from "effect" import { AppFileSystem } from "@opencode-ai/core/filesystem" -export const Tokens = z.object({ - accessToken: z.string(), - refreshToken: z.string().optional(), - expiresAt: z.number().optional(), - scope: z.string().optional(), +export const Tokens = Schema.Struct({ + accessToken: Schema.mutableKey(Schema.String), + refreshToken: Schema.mutableKey(Schema.optional(Schema.String)), + expiresAt: Schema.mutableKey(Schema.optional(Schema.Number)), + scope: Schema.mutableKey(Schema.optional(Schema.String)), }) -export type Tokens = z.infer +export type Tokens = Schema.Schema.Type -export const ClientInfo = z.object({ - clientId: z.string(), - clientSecret: z.string().optional(), - clientIdIssuedAt: z.number().optional(), - clientSecretExpiresAt: z.number().optional(), +export const ClientInfo = Schema.Struct({ + clientId: Schema.mutableKey(Schema.String), + clientSecret: Schema.mutableKey(Schema.optional(Schema.String)), + clientIdIssuedAt: Schema.mutableKey(Schema.optional(Schema.Number)), + clientSecretExpiresAt: Schema.mutableKey(Schema.optional(Schema.Number)), }) -export type ClientInfo = z.infer - -export const Entry = z.object({ - tokens: Tokens.optional(), - clientInfo: ClientInfo.optional(), - codeVerifier: z.string().optional(), - oauthState: z.string().optional(), - serverUrl: z.string().optional(), +export type ClientInfo = Schema.Schema.Type + +export const Entry = Schema.Struct({ + tokens: Schema.mutableKey(Schema.optional(Tokens)), + clientInfo: Schema.mutableKey(Schema.optional(ClientInfo)), + codeVerifier: Schema.mutableKey(Schema.optional(Schema.String)), + oauthState: Schema.mutableKey(Schema.optional(Schema.String)), + serverUrl: Schema.mutableKey(Schema.optional(Schema.String)), }) -export type Entry = z.infer +export type Entry = Schema.Schema.Type + +const decodeAuthData = Schema.decodeUnknownOption(Schema.Record(Schema.String, Entry)) +type AuthData = Record const filepath = path.join(Global.Path.data, "mcp-auth.json") @@ -56,8 +58,8 @@ export const layer = Layer.effect( const all = Effect.fn("McpAuth.all")(function* () { return yield* fs.readJson(filepath).pipe( - Effect.map((data) => data as Record), - Effect.catch(() => Effect.succeed({} as Record)), + Effect.map((data): AuthData => Option.getOrElse(decodeAuthData(data), () => ({}) as AuthData) as AuthData), + Effect.catch(() => Effect.succeed({} as AuthData)), ) }) @@ -93,7 +95,7 @@ export const layer = Layer.effect( yield* set(mcpName, entry, serverUrl) }) - const clearField = (field: K, spanName: string) => + const clearField = (field: keyof Entry, spanName: string) => Effect.fn(`McpAuth.${spanName}`)(function* (mcpName: string) { const entry = yield* get(mcpName) if (entry) { diff --git a/packages/opencode/src/mcp/index.ts b/packages/opencode/src/mcp/index.ts index 31cbbb828aa..6d40218d47c 100644 --- a/packages/opencode/src/mcp/index.ts +++ b/packages/opencode/src/mcp/index.ts @@ -15,6 +15,7 @@ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js" import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js" import { CallToolResultSchema, + ListToolsResultSchema, ToolSchema, type Tool as MCPToolDef, ToolListChangedNotificationSchema, @@ -23,7 +24,6 @@ import { Config } from "@/config/config" import { ConfigMCP } from "../config/mcp" import * as Log from "@opencode-ai/core/util/log" import { NamedError } from "@opencode-ai/core/util/error" -import z from "zod/v4" import { Installation } from "../installation" import { InstallationVersion } from "@opencode-ai/core/installation/version" import { withTimeout } from "@/util/timeout" @@ -40,8 +40,6 @@ import { EffectBridge } from "@/effect/bridge" import { InstanceState } from "@/effect/instance-state" import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" -import { zod as effectZod } from "@opencode-ai/core/effect-zod" -import { withStatics } from "@opencode-ai/core/schema" const log = Log.create({ service: "mcp" }) const DEFAULT_TIMEOUT = 30_000 @@ -60,13 +58,8 @@ export function ensureDockerRm(cmd: string, args: string[]): string[] { } // kilocode_change end -const TolerantToolSchema = ToolSchema.extend({ - outputSchema: z.unknown().optional(), -}) - -const TolerantListToolsResultSchema = z.looseObject({ - tools: z.array(TolerantToolSchema), - nextCursor: z.string().optional(), +const TolerantListToolsResultSchema = ListToolsResultSchema.extend({ + tools: ToolSchema.omit({ outputSchema: true }).array(), }) export const Resource = Schema.Struct({ @@ -75,9 +68,7 @@ export const Resource = Schema.Struct({ description: Schema.optional(Schema.String), mimeType: Schema.optional(Schema.String), client: Schema.String, -}) - .annotate({ identifier: "McpResource" }) - .pipe(withStatics((s) => ({ zod: effectZod(s) }))) +}).annotate({ identifier: "McpResource" }) export type Resource = Schema.Schema.Type export const ToolsChanged = BusEvent.define( @@ -95,12 +86,9 @@ export const BrowserOpenFailed = BusEvent.define( }), ) -export const Failed = NamedError.create( - "MCPFailed", - z.object({ - name: z.string(), - }), -) +export const Failed = NamedError.create("MCPFailed", { + name: Schema.String, +}) type MCPClient = Client @@ -127,9 +115,7 @@ export const Status = Schema.Union([ StatusFailed, StatusNeedsAuth, StatusNeedsClientRegistration, -]) - .annotate({ identifier: "MCPStatus", discriminator: "status" }) - .pipe(withStatics((s) => ({ zod: effectZod(s) }))) +]).annotate({ identifier: "MCPStatus", discriminator: "status" }) export type Status = Schema.Schema.Type // Store transports for OAuth servers to allow finishing auth @@ -169,7 +155,10 @@ function listTools(key: string, client: MCPClient, timeout: number) { log.warn("failed to validate MCP tool output schemas, retrying without output schema validation", { key, error }) return Effect.tryPromise({ - try: () => client.request({ method: "tools/list" }, TolerantListToolsResultSchema, { timeout }), + try: () => + client.request({ method: "tools/list" }, TolerantListToolsResultSchema, { + timeout, + }), catch: (err) => (err instanceof Error ? err : new Error(String(err))), }).pipe( Effect.map((result) => diff --git a/packages/opencode/src/patch/index.ts b/packages/opencode/src/patch/index.ts index b7d0989bb2b..42b26fe9630 100644 --- a/packages/opencode/src/patch/index.ts +++ b/packages/opencode/src/patch/index.ts @@ -1,18 +1,16 @@ -import z from "zod" +import { Effect, Schema } from "effect" import * as path from "path" -import * as fs from "fs/promises" +import { AppFileSystem } from "@opencode-ai/core/filesystem" import * as Log from "@opencode-ai/core/util/log" -import * as Encoding from "../kilocode/encoding" // kilocode_change import * as Bom from "../util/bom" const log = Log.create({ service: "patch" }) -// Schema definitions -export const PatchSchema = z.object({ - patchText: z.string().describe("The full patch text that describes all changes to be made"), +export const PatchSchema = Schema.Struct({ + patchText: Schema.String.annotate({ description: "The full patch text that describes all changes to be made" }), }) -export type PatchParams = z.infer +export type PatchParams = Schema.Schema.Type // Core types matching the Rust implementation export interface ApplyPatchArgs { @@ -307,23 +305,14 @@ interface ApplyPatchFileUpdate { unified_diff: string content: string bom: boolean - encoding: string // kilocode_change } -export function deriveNewContentsFromChunks(filePath: string, chunks: UpdateFileChunk[]): ApplyPatchFileUpdate { - // Read original file content - let originalContent: ReturnType - let encoding: string // kilocode_change - track detected encoding for round-trip write - try { - // kilocode_change start - encoding-aware read replaces readFileSync(filePath, "utf-8"). - // Encoding.readSync strips UTF-8 BOMs so the BOM flag is derived from the encoding label. - const result = Encoding.readSync(filePath) - originalContent = { bom: result.encoding === "utf-8-bom", text: result.text } - encoding = result.encoding - // kilocode_change end - } catch (error) { - throw new Error(`Failed to read file ${filePath}: ${error}`, { cause: error }) - } +export function deriveNewContentsFromChunks( + filePath: string, + chunks: UpdateFileChunk[], + originalText: string, +): ApplyPatchFileUpdate { + const originalContent = Bom.split(originalText) let originalLines = originalContent.text.split("\n") @@ -350,7 +339,6 @@ export function deriveNewContentsFromChunks(filePath: string, chunks: UpdateFile unified_diff: unifiedDiff, content: newContent, bom: originalContent.bom || next.bom, - encoding, // kilocode_change - include detected encoding for round-trip write } } @@ -432,11 +420,11 @@ function applyReplacements(lines: string[], replacements: Array<[number, number, // Normalize Unicode punctuation to ASCII equivalents (like Rust's normalize_unicode) function normalizeUnicode(str: string): string { return str - .replace(/[\u2018\u2019\u201A\u201B]/g, "'") // single quotes - .replace(/[\u201C\u201D\u201E\u201F]/g, '"') // double quotes - .replace(/[\u2010\u2011\u2012\u2013\u2014\u2015]/g, "-") // dashes - .replace(/\u2026/g, "...") // ellipsis - .replace(/\u00A0/g, " ") // non-breaking space + .replace(/[‘’‚‛]/g, "'") // single quotes + .replace(/[“”„‟]/g, '"') // double quotes + .replace(/[‐‑‒–—―]/g, "-") // dashes + .replace(/…/g, "...") // ellipsis + .replace(/ /g, " ") // non-breaking space } type Comparator = (a: string, b: string) => boolean @@ -526,66 +514,71 @@ function generateUnifiedDiff(oldContent: string, newContent: string): string { } // Apply hunks to filesystem -export async function applyHunksToFiles(hunks: Hunk[]): Promise { +export const applyHunksToFiles = Effect.fn("Patch.applyHunksToFiles")(function* (hunks: Hunk[]) { if (hunks.length === 0) { - throw new Error("No files were modified.") + return yield* Effect.fail(new Error("No files were modified.")) } + const fs = yield* AppFileSystem.Service + const added: string[] = [] const modified: string[] = [] const deleted: string[] = [] for (const hunk of hunks) { switch (hunk.type) { - case "add": - await Encoding.write(hunk.path, hunk.contents) // kilocode_change - encoding-aware write (mkdirs) + case "add": { + yield* fs.writeWithDirs(hunk.path, hunk.contents) added.push(hunk.path) log.info(`Added file: ${hunk.path}`) break + } - case "delete": - await fs.unlink(hunk.path) + case "delete": { + yield* fs.remove(hunk.path) deleted.push(hunk.path) log.info(`Deleted file: ${hunk.path}`) break + } - case "update": - const fileUpdate = deriveNewContentsFromChunks(hunk.path, hunk.chunks) + case "update": { + const originalText = yield* fs.readFileString(hunk.path) + const fileUpdate = deriveNewContentsFromChunks(hunk.path, hunk.chunks, originalText) if (hunk.move_path) { - // Handle file move - await Encoding.write(hunk.move_path, Bom.join(fileUpdate.content, fileUpdate.bom), fileUpdate.encoding) // kilocode_change - encoding-aware write (mkdirs) replaces fs.mkdir + fs.writeFile - await fs.unlink(hunk.path) + yield* fs.writeWithDirs(hunk.move_path, Bom.join(fileUpdate.content, fileUpdate.bom)) + yield* fs.remove(hunk.path) modified.push(hunk.move_path) log.info(`Moved file: ${hunk.path} -> ${hunk.move_path}`) } else { - // Regular update - await Encoding.write(hunk.path, Bom.join(fileUpdate.content, fileUpdate.bom), fileUpdate.encoding) // kilocode_change - encoding-aware write replaces fs.writeFile + yield* fs.writeWithDirs(hunk.path, Bom.join(fileUpdate.content, fileUpdate.bom)) modified.push(hunk.path) log.info(`Updated file: ${hunk.path}`) } break + } } } - return { added, modified, deleted } -} + return { added, modified, deleted } satisfies AffectedPaths +}) // Main patch application function -export async function applyPatch(patchText: string): Promise { +export const applyPatch = Effect.fn("Patch.applyPatch")(function* (patchText: string) { const { hunks } = parsePatch(patchText) - return applyHunksToFiles(hunks) -} + return yield* applyHunksToFiles(hunks) +}) -// Async version of maybeParseApplyPatchVerified -export async function maybeParseApplyPatchVerified( - argv: string[], - cwd: string, -): Promise< +type MaybeApplyPatchVerifiedResult = | { type: MaybeApplyPatchVerified.Body; action: ApplyPatchAction } | { type: MaybeApplyPatchVerified.CorrectnessError; error: Error } | { type: MaybeApplyPatchVerified.NotApplyPatch } -> { + +// Effectful verified-parse: needs AppFileSystem.Service to read existing files +export const maybeParseApplyPatchVerified = Effect.fn("Patch.maybeParseApplyPatchVerified")(function* ( + argv: string[], + cwd: string, +) { // Detect implicit patch invocation (raw patch without apply_patch command) if (argv.length === 1) { try { @@ -593,7 +586,7 @@ export async function maybeParseApplyPatchVerified( return { type: MaybeApplyPatchVerified.CorrectnessError, error: new Error(ApplyPatchError.ImplicitInvocation), - } + } satisfies MaybeApplyPatchVerifiedResult } catch { // Not a patch, continue } @@ -602,8 +595,9 @@ export async function maybeParseApplyPatchVerified( const result = maybeParseApplyPatch(argv) switch (result.type) { - case MaybeApplyPatch.Body: - const { args } = result + case MaybeApplyPatch.Body: { + const fs = yield* AppFileSystem.Service + const args = result.args const effectiveCwd = args.workdir ? path.resolve(cwd, args.workdir) : cwd const changes = new Map() @@ -621,27 +615,39 @@ export async function maybeParseApplyPatchVerified( }) break - case "delete": - // For delete, we need to read the current content + case "delete": { const deletePath = path.resolve(effectiveCwd, hunk.path) - try { - const content = (await Encoding.read(deletePath)).text // kilocode_change - encoding-aware read - changes.set(resolvedPath, { - type: "delete", - content, - }) - } catch { + const content = yield* fs.readFileString(deletePath).pipe(Effect.catch(() => Effect.succeed(undefined))) + if (content === undefined) { return { type: MaybeApplyPatchVerified.CorrectnessError, error: new Error(`Failed to read file for deletion: ${deletePath}`), - } + } satisfies MaybeApplyPatchVerifiedResult } + changes.set(resolvedPath, { + type: "delete", + content, + }) break + } - case "update": + case "update": { const updatePath = path.resolve(effectiveCwd, hunk.path) + const originalText = yield* fs + .readFileString(updatePath) + .pipe( + Effect.catch((cause) => + Effect.succeed(new Error(`Failed to read file ${updatePath}: ${cause}`, { cause })), + ), + ) + if (originalText instanceof Error) { + return { + type: MaybeApplyPatchVerified.CorrectnessError, + error: originalText, + } satisfies MaybeApplyPatchVerifiedResult + } try { - const fileUpdate = deriveNewContentsFromChunks(updatePath, hunk.chunks) + const fileUpdate = deriveNewContentsFromChunks(updatePath, hunk.chunks, originalText) changes.set(resolvedPath, { type: "update", unified_diff: fileUpdate.unified_diff, @@ -652,9 +658,10 @@ export async function maybeParseApplyPatchVerified( return { type: MaybeApplyPatchVerified.CorrectnessError, error: error as Error, - } + } satisfies MaybeApplyPatchVerifiedResult } break + } } } @@ -665,17 +672,18 @@ export async function maybeParseApplyPatchVerified( patch: args.patch, cwd: effectiveCwd, }, - } + } satisfies MaybeApplyPatchVerifiedResult + } case MaybeApplyPatch.PatchParseError: return { type: MaybeApplyPatchVerified.CorrectnessError, error: result.error, - } + } satisfies MaybeApplyPatchVerifiedResult case MaybeApplyPatch.NotApplyPatch: - return { type: MaybeApplyPatchVerified.NotApplyPatch } + return { type: MaybeApplyPatchVerified.NotApplyPatch } satisfies MaybeApplyPatchVerifiedResult } -} +}) export * as Patch from "." diff --git a/packages/opencode/src/permission/index.ts b/packages/opencode/src/permission/index.ts index acb016ba71f..e11d5713067 100644 --- a/packages/opencode/src/permission/index.ts +++ b/packages/opencode/src/permission/index.ts @@ -8,13 +8,12 @@ import { MessageID, SessionID } from "@/session/schema" import { PermissionTable } from "@/session/session.sql" import { Database } from "@/storage/db" import { eq } from "drizzle-orm" -import { zod } from "@opencode-ai/core/effect-zod" import * as Log from "@opencode-ai/core/util/log" -import { withStatics } from "@opencode-ai/core/schema" import { Wildcard } from "@/util/wildcard" import { Deferred, Effect, Layer, Schema, Context } from "effect" import os from "os" import z from "zod" // kilocode_change +import { zod } from "@opencode-ai/core/effect-zod" // kilocode_change import { evaluate as evalRule } from "./evaluate" import { PermissionID } from "./schema" // kilocode_change start @@ -26,23 +25,17 @@ import { ExternalDirectoryPermission } from "@/kilocode/permission/external-dire const log = Log.create({ service: "permission" }) -export const Action = Schema.Literals(["allow", "deny", "ask"]) - .annotate({ identifier: "PermissionAction" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +export const Action = Schema.Literals(["allow", "deny", "ask"]).annotate({ identifier: "PermissionAction" }) export type Action = Schema.Schema.Type export const Rule = Schema.Struct({ permission: Schema.String, pattern: Schema.String, action: Action, -}) - .annotate({ identifier: "PermissionRule" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "PermissionRule" }) export type Rule = Schema.Schema.Type -export const Ruleset = Schema.mutable(Schema.Array(Rule)) - .annotate({ identifier: "PermissionRuleset" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +export const Ruleset = Schema.mutable(Schema.Array(Rule)).annotate({ identifier: "PermissionRuleset" }) export type Ruleset = Schema.Schema.Type export class Request extends Schema.Class("PermissionRequest")({ @@ -58,11 +51,9 @@ export class Request extends Schema.Class("PermissionRequest")({ callID: Schema.String, }), ), -}) { - static readonly zod = zod(this) -} +}) {} -export const Reply = Schema.Literals(["once", "always", "reject"]).pipe(withStatics((s) => ({ zod: zod(s) }))) +export const Reply = Schema.Literals(["once", "always", "reject"]) export type Reply = Schema.Schema.Type const reply = { @@ -70,17 +61,13 @@ const reply = { message: Schema.optional(Schema.String), } -export const ReplyBody = Schema.Struct(reply) - .annotate({ identifier: "PermissionReplyBody" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +export const ReplyBody = Schema.Struct(reply).annotate({ identifier: "PermissionReplyBody" }) export type ReplyBody = Schema.Schema.Type export class Approval extends Schema.Class("PermissionApproval")({ projectID: ProjectID, patterns: Schema.Array(Schema.String), -}) { - static readonly zod = zod(this) -} +}) {} export const Event = { Asked: BusEvent.define("permission.asked", Request), @@ -123,17 +110,13 @@ export const AskInput = Schema.Struct({ id: Schema.optional(PermissionID), ruleset: Ruleset, hardRuleset: Schema.optional(Ruleset), // kilocode_change -}) - .annotate({ identifier: "PermissionAskInput" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "PermissionAskInput" }) export type AskInput = Schema.Schema.Type export const ReplyInput = Schema.Struct({ requestID: PermissionID, ...reply, -}) - .annotate({ identifier: "PermissionReplyInput" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "PermissionReplyInput" }) export type ReplyInput = Schema.Schema.Type // kilocode_change start diff --git a/packages/opencode/src/permission/schema.ts b/packages/opencode/src/permission/schema.ts index f7c6e2c5b75..58ef0a8a767 100644 --- a/packages/opencode/src/permission/schema.ts +++ b/packages/opencode/src/permission/schema.ts @@ -1,7 +1,6 @@ import { Schema } from "effect" import { Identifier } from "@/id/id" -import { zod } from "@opencode-ai/core/effect-zod" import { Newtype } from "@opencode-ai/core/schema" export class PermissionID extends Newtype()( @@ -11,6 +10,4 @@ export class PermissionID extends Newtype()( static ascending(id?: string): PermissionID { return this.make(Identifier.ascending("permission", id)) } - - static readonly zod = zod(this) } diff --git a/packages/opencode/src/plugin/digitalocean.ts b/packages/opencode/src/plugin/digitalocean.ts new file mode 100644 index 00000000000..e94cee93cb6 --- /dev/null +++ b/packages/opencode/src/plugin/digitalocean.ts @@ -0,0 +1,411 @@ +import type { Hooks, PluginInput } from "@kilocode/plugin" +import type { Model } from "@kilocode/sdk/v2" +import * as Log from "@opencode-ai/core/util/log" +import { InstallationVersion } from "@opencode-ai/core/installation/version" +import { createServer } from "http" + +const log = Log.create({ service: "plugin.digitalocean" }) + +const DO_OAUTH_CLIENT_ID = "b1a6c5158156caac821fd1b30253ca8acb52454a48fa744420e41889cb589f82" +const DO_AUTHORIZE_URL = "https://cloud.digitalocean.com/v1/oauth/authorize" +const DO_API_BASE = "https://api.digitalocean.com" +const DO_INFERENCE_BASE = "https://inference.do-ai.run/v1" +const OAUTH_PORT = 1456 +const OAUTH_REDIRECT_PATH = "/auth/callback" +const OAUTH_TOKEN_PATH = "/auth/token" +const ROUTER_REFRESH_INTERVAL_MS = 5 * 60 * 1000 +const MAK_NAME_PREFIX = "opencode-oauth" + +interface ImplicitTokenPayload { + access_token: string + expires_in: number + state: string +} + +interface PendingOAuth { + state: string + resolve: (tokens: ImplicitTokenPayload) => void + reject: (error: Error) => void +} + +interface ApiKeyInfo { + uuid: string + name: string + secret_key: string +} + +interface RouterEntry { + name: string + uuid?: string + description?: string +} + +let oauthServer: ReturnType | undefined +let pendingOAuth: PendingOAuth | undefined + +function generateState(): string { + const bytes = crypto.getRandomValues(new Uint8Array(32)) + return Array.from(bytes) + .map((b) => b.toString(16).padStart(2, "0")) + .join("") +} + +function redirectUri(): string { + return `http://localhost:${OAUTH_PORT}${OAUTH_REDIRECT_PATH}` +} + +function buildAuthorizeUrl(state: string): string { + const params = new URLSearchParams({ + response_type: "token", + client_id: DO_OAUTH_CLIENT_ID, + redirect_uri: redirectUri(), + scope: "genai:create genai:read", + state, + }) + return `${DO_AUTHORIZE_URL}?${params.toString()}` +} + +const HTML_CALLBACK = ` + + + + OpenCode - DigitalOcean Authorization + + + +
+

Finishing sign-in...

+

You can close this window once it says you're signed in.

+
+ + +` + +async function startOAuthServer(): Promise { + if (oauthServer) return + oauthServer = createServer((req, res) => { + const url = new URL(req.url || "/", `http://localhost:${OAUTH_PORT}`) + + if (req.method === "GET" && url.pathname === OAUTH_REDIRECT_PATH) { + res.writeHead(200, { "Content-Type": "text/html" }) + res.end(HTML_CALLBACK) + return + } + + if (req.method === "POST" && url.pathname === OAUTH_TOKEN_PATH) { + const chunks: Buffer[] = [] + req.on("data", (chunk: Buffer) => chunks.push(chunk)) + req.on("end", () => { + const raw = Buffer.concat(chunks).toString("utf8") + let body: Record = {} + try { + body = raw ? JSON.parse(raw) : {} + } catch { + body = {} + } + if (!pendingOAuth) { + res.writeHead(409, { "Content-Type": "application/json" }) + res.end(JSON.stringify({ error: "no_pending_oauth" })) + return + } + if (body.error) { + const message = body.error_description || body.error || "OAuth error" + pendingOAuth.reject(new Error(String(message))) + pendingOAuth = undefined + res.writeHead(200, { "Content-Type": "application/json" }) + res.end(JSON.stringify({ ok: true })) + return + } + if (!body.access_token) { + pendingOAuth.reject(new Error("Missing access_token in callback")) + pendingOAuth = undefined + res.writeHead(400, { "Content-Type": "application/json" }) + res.end(JSON.stringify({ error: "missing_access_token" })) + return + } + if (body.state !== pendingOAuth.state) { + pendingOAuth.reject(new Error("Invalid state - potential CSRF attack")) + pendingOAuth = undefined + res.writeHead(400, { "Content-Type": "application/json" }) + res.end(JSON.stringify({ error: "invalid_state" })) + return + } + const expires = parseInt(body.expires_in || "0", 10) + pendingOAuth.resolve({ + access_token: body.access_token, + expires_in: Number.isFinite(expires) && expires > 0 ? expires : 60 * 60 * 24 * 30, + state: body.state, + }) + pendingOAuth = undefined + res.writeHead(200, { "Content-Type": "application/json" }) + res.end(JSON.stringify({ ok: true })) + }) + return + } + + res.writeHead(404) + res.end("Not found") + }) + + await new Promise((resolve, reject) => { + oauthServer!.listen(OAUTH_PORT, () => { + log.info("digitalocean oauth server started", { port: OAUTH_PORT }) + resolve() + }) + oauthServer!.on("error", reject) + }) +} + +function stopOAuthServer() { + if (!oauthServer) return + oauthServer.close(() => log.info("digitalocean oauth server stopped")) + oauthServer = undefined +} + +function waitForOAuthCallback(state: string): Promise { + return new Promise((resolve, reject) => { + const timeout = setTimeout( + () => { + if (pendingOAuth) { + pendingOAuth = undefined + reject(new Error("OAuth callback timeout - authorization took too long")) + } + }, + 5 * 60 * 1000, + ) + pendingOAuth = { + state, + resolve: (tokens) => { + clearTimeout(timeout) + resolve(tokens) + }, + reject: (error) => { + clearTimeout(timeout) + reject(error) + }, + } + }) +} + +async function createModelAccessKey(bearer: string): Promise { + // Suffix-on-collision strategy keeps re-`/connect` non-destructive. + const name = `${MAK_NAME_PREFIX}-${Math.floor(Date.now() / 1000)}` + const res = await fetch(`${DO_API_BASE}/v2/gen-ai/models/api_keys`, { + method: "POST", + headers: { + Authorization: `Bearer ${bearer}`, + "Content-Type": "application/json", + "User-Agent": `opencode/${InstallationVersion}`, + }, + body: JSON.stringify({ name }), + }) + if (!res.ok) { + const body = await res.text().catch(() => "") + throw new Error(`Failed to create Model Access Key (${res.status}): ${body}`) + } + const data = (await res.json()) as { api_key_info?: ApiKeyInfo } + if (!data.api_key_info?.secret_key) throw new Error("Model Access Key response missing secret_key") + return data.api_key_info +} + +async function listRouters( + bearer: string, +): Promise<{ ok: true; routers: RouterEntry[] } | { ok: false; status: number }> { + const res = await fetch(`${DO_API_BASE}/v2/gen-ai/models/routers`, { + headers: { + Authorization: `Bearer ${bearer}`, + Accept: "application/json", + "User-Agent": `opencode/${InstallationVersion}`, + }, + signal: AbortSignal.timeout(10_000), + }).catch(() => undefined) + if (!res) return { ok: false, status: 0 } + if (!res.ok) return { ok: false, status: res.status } + const body = (await res.json().catch(() => undefined)) as { model_routers?: RouterEntry[] } | undefined + return { ok: true, routers: body?.model_routers ?? [] } +} + +function routerModel(router: RouterEntry, providerID: string): Model { + const id = `router:${router.name}` + return { + id, + providerID, + name: router.name, + family: "digitalocean-inference-routers", + api: { id, url: DO_INFERENCE_BASE, npm: "@ai-sdk/openai-compatible" }, + status: "active", + headers: {}, + options: {}, + cost: { input: 0, output: 0, cache: { read: 0, write: 0 } }, + limit: { context: 128_000, output: 8_192 }, + capabilities: { + temperature: true, + reasoning: false, + attachment: false, + toolcall: true, + input: { text: true, audio: false, image: false, video: false, pdf: false }, + output: { text: true, audio: false, image: false, video: false, pdf: false }, + interleaved: false, + }, + release_date: "", + variants: {}, + } +} + +function parseRoutersJSON(raw: string | undefined): RouterEntry[] { + if (!raw) return [] + try { + const parsed = JSON.parse(raw) + if (!Array.isArray(parsed)) return [] + return parsed.flatMap((r) => + r && typeof r.name === "string" ? [{ name: r.name, uuid: r.uuid, description: r.description }] : [], + ) + } catch { + return [] + } +} + +export async function DigitalOceanAuthPlugin(input: PluginInput): Promise { + return { + provider: { + id: "digitalocean", + async models(provider, ctx) { + const baseModels = provider.models + if (ctx.auth?.type !== "api") return baseModels + + const metadata = ctx.auth.metadata ?? {} + const oauthAccess = metadata["oauth_access"] + const oauthExpires = parseInt(metadata["oauth_expires"] || "0", 10) + const fetchedAt = parseInt(metadata["routers_fetched_at"] || "0", 10) + const cached = parseRoutersJSON(metadata["routers"]) + + let routers = cached + const stale = Date.now() - fetchedAt > ROUTER_REFRESH_INTERVAL_MS + const bearerValid = oauthAccess && oauthExpires > Date.now() + + if (bearerValid && stale) { + const result = await listRouters(oauthAccess) + if (result.ok) { + routers = result.routers + const updated: Record = { + ...metadata, + routers: JSON.stringify(routers.map((r) => ({ name: r.name, uuid: r.uuid, description: r.description }))), + routers_fetched_at: String(Date.now()), + } + await input.client.auth + .set({ + path: { id: "digitalocean" }, + body: { type: "api", key: ctx.auth.key, metadata: updated }, + }) + .catch((err) => log.warn("failed to persist refreshed routers", { error: err })) + } else if (result.status === 401 || result.status === 403) { + log.warn("digitalocean oauth bearer rejected; using cached routers", { status: result.status }) + } else if (result.status !== 0) { + log.warn("digitalocean router refresh failed", { status: result.status }) + } + } + + const merged: Record = { ...baseModels } + for (const router of routers) { + const id = `router:${router.name}` + if (merged[id]) continue + merged[id] = routerModel(router, "digitalocean") + } + return merged + }, + }, + auth: { + provider: "digitalocean", + methods: [ + { + type: "oauth", + label: "Login with DigitalOcean", + async authorize() { + await startOAuthServer() + const state = generateState() + const callbackPromise = waitForOAuthCallback(state) + return { + url: buildAuthorizeUrl(state), + instructions: + "Sign in to DigitalOcean in your browser. OpenCode will create a Model Access Key named opencode-oauth-* and load your Inference Routers. Re-run /connect to refresh routers later.", + method: "auto" as const, + async callback() { + try { + const tokens = await callbackPromise + const apiKeyInfo = await createModelAccessKey(tokens.access_token) + const routerResult = await listRouters(tokens.access_token) + const routers = routerResult.ok ? routerResult.routers : [] + if (!routerResult.ok) { + log.warn("digitalocean initial router fetch failed", { status: routerResult.status }) + } + return { + type: "success" as const, + provider: "digitalocean", + key: apiKeyInfo.secret_key, + metadata: { + mak_uuid: apiKeyInfo.uuid, + mak_name: apiKeyInfo.name, + oauth_access: tokens.access_token, + oauth_expires: String(Date.now() + tokens.expires_in * 1000), + routers: JSON.stringify( + routers.map((r) => ({ name: r.name, uuid: r.uuid, description: r.description })), + ), + routers_fetched_at: String(Date.now()), + }, + } + } catch (err) { + log.error("digitalocean oauth callback failed", { error: err }) + return { type: "failed" as const } + } finally { + stopOAuthServer() + } + }, + } + }, + }, + { + type: "api", + label: "Paste Model Access Key", + }, + ], + }, + } +} diff --git a/packages/opencode/src/plugin/github-copilot/models.ts b/packages/opencode/src/plugin/github-copilot/models.ts index 286e7dc59e0..5a24c6f4dbb 100644 --- a/packages/opencode/src/plugin/github-copilot/models.ts +++ b/packages/opencode/src/plugin/github-copilot/models.ts @@ -1,50 +1,51 @@ -import { z } from "zod" import type { Model } from "@kilocode/sdk/v2" +import { Schema } from "effect" -export const schema = z.object({ - data: z.array( - z.object({ - model_picker_enabled: z.boolean(), - id: z.string(), - name: z.string(), +export const schema = Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + model_picker_enabled: Schema.Boolean, + id: Schema.String, + name: Schema.String, // every version looks like: `{model.id}-YYYY-MM-DD` - version: z.string(), - supported_endpoints: z.array(z.string()).optional(), - policy: z - .object({ - state: z.string().optional(), - }) - .optional(), - capabilities: z.object({ - family: z.string(), - limits: z.object({ - max_context_window_tokens: z.number(), - max_output_tokens: z.number(), - max_prompt_tokens: z.number(), - vision: z - .object({ - max_prompt_image_size: z.number(), - max_prompt_images: z.number(), - supported_media_types: z.array(z.string()), - }) - .optional(), + version: Schema.String, + supported_endpoints: Schema.optional(Schema.Array(Schema.String)), + policy: Schema.optional( + Schema.Struct({ + state: Schema.optional(Schema.String), }), - supports: z.object({ - adaptive_thinking: z.boolean().optional(), - max_thinking_budget: z.number().optional(), - min_thinking_budget: z.number().optional(), - reasoning_effort: z.array(z.string()).optional(), - streaming: z.boolean(), - structured_outputs: z.boolean().optional(), - tool_calls: z.boolean(), - vision: z.boolean().optional(), + ), + capabilities: Schema.Struct({ + family: Schema.String, + limits: Schema.Struct({ + max_context_window_tokens: Schema.Number, + max_output_tokens: Schema.Number, + max_prompt_tokens: Schema.Number, + vision: Schema.optional( + Schema.Struct({ + max_prompt_image_size: Schema.Number, + max_prompt_images: Schema.Number, + supported_media_types: Schema.Array(Schema.String), + }), + ), + }), + supports: Schema.Struct({ + adaptive_thinking: Schema.optional(Schema.Boolean), + max_thinking_budget: Schema.optional(Schema.Number), + min_thinking_budget: Schema.optional(Schema.Number), + reasoning_effort: Schema.optional(Schema.Array(Schema.String)), + streaming: Schema.Boolean, + structured_outputs: Schema.optional(Schema.Boolean), + tool_calls: Schema.Boolean, + vision: Schema.optional(Schema.Boolean), }), }), }), ), }) -type Item = z.infer["data"][number] +type Item = Schema.Schema.Type["data"][number] +const decodeModels = Schema.decodeUnknownSync(schema) function build(key: string, remote: Item, url: string, prev?: Model): Model { const reasoning = @@ -169,7 +170,7 @@ export async function get( if (!res.ok) { throw new Error(`Failed to fetch models: ${res.status}`) } - return schema.parse(await res.json()) + return decodeModels(await res.json()) }) const result = { ...existing } diff --git a/packages/opencode/src/plugin/index.ts b/packages/opencode/src/plugin/index.ts index e65c9714d58..f55e4ef6c44 100644 --- a/packages/opencode/src/plugin/index.ts +++ b/packages/opencode/src/plugin/index.ts @@ -9,7 +9,6 @@ import { Config } from "@/config/config" import { Bus } from "../bus" import * as Log from "@opencode-ai/core/util/log" import { createKiloClient } from "@kilocode/sdk" -import { Flag } from "@opencode-ai/core/flag/flag" import { ServerAuth } from "@/server/auth" import { CodexAuthPlugin } from "./codex" import { Session } from "@/session/session" @@ -20,6 +19,7 @@ import { PoeAuthPlugin } from "opencode-poe-auth" import { CloudflareAIGatewayAuthPlugin, CloudflareWorkersAuthPlugin } from "./cloudflare" import { AzureAuthPlugin } from "./azure" import { XaiAuthPlugin } from "./xai" // kilocode_change +import { DigitalOceanAuthPlugin } from "./digitalocean" import { Effect, Layer, Context, Stream } from "effect" import { EffectBridge } from "@/effect/bridge" import { InstanceState } from "@/effect/instance-state" @@ -29,6 +29,7 @@ import { parsePluginSpecifier, readPluginId, readV1Plugin, resolvePluginId } fro import { KiloAuthPlugin } from "@kilocode/kilo-gateway" // kilocode_change import { registerAdapter } from "@/control-plane/adapters" import type { WorkspaceAdapter } from "@/control-plane/types" +import { RuntimeFlags } from "@/effect/runtime-flags" const log = Log.create({ service: "plugin" }) @@ -70,6 +71,7 @@ const INTERNAL_PLUGINS: PluginInstance[] = [ CloudflareAIGatewayAuthPlugin, AzureAuthPlugin, XaiAuthPlugin, + DigitalOceanAuthPlugin, ] // kilocode_change end @@ -118,6 +120,7 @@ export const layer = Layer.effect( Effect.gen(function* () { const bus = yield* Bus.Service const config = yield* Config.Service + const flags = yield* RuntimeFlags.Service const state = yield* InstanceState.make( Effect.fn("Plugin.state")(function* (ctx) { @@ -154,7 +157,7 @@ export const layer = Layer.effect( $: typeof Bun === "undefined" ? undefined : Bun.$, } - for (const plugin of INTERNAL_PLUGINS) { + for (const plugin of flags.disableDefaultPlugins ? [] : INTERNAL_PLUGINS) { log.info("loading internal plugin", { name: plugin.name }) const init = yield* Effect.tryPromise({ try: () => plugin(input), @@ -165,8 +168,8 @@ export const layer = Layer.effect( if (init._tag === "Some") hooks.push(init.value) } - const plugins = Flag.KILO_PURE ? [] : (cfg.plugin_origins ?? []) - if (Flag.KILO_PURE && cfg.plugin_origins?.length) { + const plugins = flags.pure ? [] : (cfg.plugin_origins ?? []) + if (flags.pure && cfg.plugin_origins?.length) { log.info("skipping external plugins in pure mode", { count: cfg.plugin_origins.length }) } if (plugins.length) yield* config.waitForDependencies() @@ -291,6 +294,10 @@ export const layer = Layer.effect( }), ) -export const defaultLayer = layer.pipe(Layer.provide(Bus.layer), Layer.provide(Config.defaultLayer)) +export const defaultLayer = layer.pipe( + Layer.provide(Bus.layer), + Layer.provide(Config.defaultLayer), + Layer.provide(RuntimeFlags.defaultLayer), +) export * as Plugin from "." diff --git a/packages/opencode/src/project/bootstrap.ts b/packages/opencode/src/project/bootstrap.ts index 3fed2dd6073..d2a50a3f083 100644 --- a/packages/opencode/src/project/bootstrap.ts +++ b/packages/opencode/src/project/bootstrap.ts @@ -43,7 +43,7 @@ export const layer = Layer.effect( const run = Effect.gen(function* () { const ctx = yield* InstanceState.context - yield* Effect.logDebug("bootstrapping", { directory: ctx.directory }) // kilocode_change - was logInfo; downgraded to avoid printing to TUI on every startup + yield* Effect.logDebug("bootstrapping").pipe(Effect.annotateLogs("directory", ctx.directory)) // kilocode_change - was logInfo; downgraded to avoid printing to TUI on every startup // everything depends on config so eager load it for nice traces yield* config.get() // Plugin can mutate config so it has to be initialized before anything else. diff --git a/packages/opencode/src/project/instance-store.ts b/packages/opencode/src/project/instance-store.ts index 81fcfbcfe8e..eb65772daef 100644 --- a/packages/opencode/src/project/instance-store.ts +++ b/packages/opencode/src/project/instance-store.ts @@ -89,7 +89,7 @@ export const layer: Layer.Layer runDisposers(ctx.directory)) yield* emitDisposed({ directory: ctx.directory, project: ctx.project.id }) }) @@ -112,7 +112,7 @@ export const layer: Layer.Layer() } cache.set(directory, entry) yield* Effect.gen(function* () { - yield* Effect.logInfo("creating instance", { directory }) + yield* Effect.logInfo("creating instance").pipe(Effect.annotateLogs("directory", directory)) yield* completeLoad(directory, input, entry) }).pipe(Effect.forkIn(scope, { startImmediately: true })) return yield* restore(Deferred.await(entry.deferred)) @@ -128,7 +128,7 @@ export const layer: Layer.Layer() } cache.set(directory, entry) yield* Effect.gen(function* () { - yield* Effect.logInfo("reloading instance", { directory }) + yield* Effect.logInfo("reloading instance").pipe(Effect.annotateLogs("directory", directory)) if (previous) { yield* Deferred.await(previous.deferred).pipe(Effect.ignore) yield* Effect.promise(() => runDisposers(directory)) @@ -159,7 +159,9 @@ export const layer: Layer.Layer ({ zod: zod(s) }))) +}).annotate({ identifier: "Project" }) export type Info = Types.DeepMutable> export const Event = { @@ -89,21 +86,19 @@ export function fromRow(row: Row): Info { } } -export const UpdateInput = z.object({ - projectID: ProjectID.zod, - name: z.string().optional(), - icon: zod(ProjectIcon).optional(), - commands: zod(ProjectCommands).optional(), +export const UpdateInput = Schema.Struct({ + projectID: ProjectID, + name: Schema.optional(Schema.String), + icon: Schema.optional(ProjectIcon), + commands: Schema.optional(ProjectCommands), }) -export type UpdateInput = z.infer +export type UpdateInput = Types.DeepMutable> export const UpdatePayload = Schema.Struct({ name: Schema.optional(Schema.String), icon: Schema.optional(ProjectIcon), commands: Schema.optional(ProjectCommands), -}) - .annotate({ identifier: "ProjectUpdateInput" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "ProjectUpdateInput" }) export type UpdatePayload = Types.DeepMutable> // --------------------------------------------------------------------------- @@ -136,7 +131,7 @@ type GitResult = { code: number; text: string; stderr: string } export const layer: Layer.Layer< Service, never, - AppFileSystem.Service | Path.Path | ChildProcessSpawner.ChildProcessSpawner | Bus.Service + AppFileSystem.Service | Path.Path | ChildProcessSpawner.ChildProcessSpawner | Bus.Service | RuntimeFlags.Service > = Layer.effect( Service, Effect.gen(function* () { @@ -144,6 +139,7 @@ export const layer: Layer.Layer< const pathSvc = yield* Path.Path const spawner = yield* ChildProcessSpawner.ChildProcessSpawner const bus = yield* Bus.Service + const flags = yield* RuntimeFlags.Service const git = Effect.fnUntraced( function* (args: string[], opts?: { cwd?: string }) { @@ -290,7 +286,7 @@ export const layer: Layer.Layer< time: { created: Date.now(), updated: Date.now() }, } - if (Flag.KILO_EXPERIMENTAL_ICON_DISCOVERY) yield* discover(existing).pipe(Effect.ignore, Effect.forkIn(scope)) + if (flags.experimentalIconDiscovery) yield* discover(existing).pipe(Effect.ignore, Effect.forkIn(scope)) const result: Info = { ...existing, @@ -513,6 +509,7 @@ export const defaultLayer = layer.pipe( Layer.provide(CrossSpawnSpawner.defaultLayer), Layer.provide(AppFileSystem.defaultLayer), Layer.provide(NodePath.layer), + Layer.provide(RuntimeFlags.defaultLayer), ) export const use = serviceUse(Service) diff --git a/packages/opencode/src/project/schema.ts b/packages/opencode/src/project/schema.ts index c6cff94fde6..e511a75ffa2 100644 --- a/packages/opencode/src/project/schema.ts +++ b/packages/opencode/src/project/schema.ts @@ -1,6 +1,5 @@ import { Schema } from "effect" -import { zod } from "@opencode-ai/core/effect-zod" import { withStatics } from "@opencode-ai/core/schema" const projectIdSchema = Schema.String.pipe(Schema.brand("ProjectID")) @@ -10,6 +9,5 @@ export type ProjectID = typeof projectIdSchema.Type export const ProjectID = projectIdSchema.pipe( withStatics((schema: typeof projectIdSchema) => ({ global: schema.make("global"), - zod: zod(schema), })), ) diff --git a/packages/opencode/src/project/vcs.ts b/packages/opencode/src/project/vcs.ts index 092444c444c..5a477e02b3e 100644 --- a/packages/opencode/src/project/vcs.ts +++ b/packages/opencode/src/project/vcs.ts @@ -6,8 +6,6 @@ import { InstanceState } from "@/effect/instance-state" import { FileWatcher } from "@/file/watcher" import { Git } from "@/git" import * as Log from "@opencode-ai/core/util/log" -import { zod, zodObject } from "@opencode-ai/core/effect-zod" -import { NonNegativeInt, withStatics } from "@opencode-ai/core/schema" const log = Log.create({ service: "vcs" }) const PATCH_CONTEXT_LINES = 2_147_483_647 @@ -208,7 +206,7 @@ const track = Effect.fnUntraced(function* (git: Git.Interface, cwd: string, ref: return yield* diffAgainstRef(git, cwd, ref) }) -export const Mode = Schema.Literals(["git", "branch"]).pipe(withStatics((s) => ({ zod: zod(s) }))) +export const Mode = Schema.Literals(["git", "branch"]) export type Mode = Schema.Schema.Type export const Event = { @@ -223,9 +221,7 @@ export const Event = { export const Info = Schema.Struct({ branch: Schema.optional(Schema.String), default_branch: Schema.optional(Schema.String), -}) - .annotate({ identifier: "VcsInfo" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "VcsInfo" }) export type Info = Schema.Schema.Type export const FileDiff = Schema.Struct({ @@ -237,9 +233,7 @@ export const FileDiff = Schema.Struct({ additions: Schema.Finite, deletions: Schema.Finite, status: Schema.optional(Schema.Literals(["added", "deleted", "modified"])), -}) - .annotate({ identifier: "VcsFileDiff" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "VcsFileDiff" }) export type FileDiff = Schema.Schema.Type export const FileStatus = Schema.Struct({ @@ -247,19 +241,17 @@ export const FileStatus = Schema.Struct({ additions: Schema.Finite, deletions: Schema.Finite, status: Schema.Literals(["added", "deleted", "modified"]), -}) - .annotate({ identifier: "VcsFileStatus" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "VcsFileStatus" }) export type FileStatus = Schema.Schema.Type export const ApplyInput = Schema.Struct({ patch: Schema.String, -}).pipe(withStatics((s) => ({ zod: zod(s), zodObject: zodObject(s) }))) +}) export type ApplyInput = Schema.Schema.Type export const ApplyResult = Schema.Struct({ applied: Schema.Boolean, -}).pipe(withStatics((s) => ({ zod: zod(s) }))) +}) export type ApplyResult = Schema.Schema.Type export class PatchApplyError extends Schema.TaggedErrorClass()("VcsPatchApplyError", { diff --git a/packages/opencode/src/provider/auth.ts b/packages/opencode/src/provider/auth.ts index 297e3ee8846..fff966c5714 100644 --- a/packages/opencode/src/provider/auth.ts +++ b/packages/opencode/src/provider/auth.ts @@ -1,9 +1,7 @@ import type { AuthOAuthResult, Hooks } from "@kilocode/plugin" import { Auth } from "@/auth" import { InstanceState } from "@/effect/instance-state" -import { zod } from "@opencode-ai/core/effect-zod" -import { namedSchemaError } from "@/util/named-schema-error" -import { optionalOmitUndefined, withStatics } from "@opencode-ai/core/schema" +import { optionalOmitUndefined } from "@opencode-ai/core/schema" import { Plugin } from "../plugin" import { ProviderID } from "./schema" import { Array as Arr, Effect, Layer, Record, Result, Context, Schema } from "effect" @@ -47,50 +45,48 @@ export class Method extends Schema.Class("ProviderAuthMethod")({ type: Schema.Literals(["oauth", "api"]), label: Schema.String, prompts: optionalOmitUndefined(Schema.Array(Prompt)), -}) { - static readonly zod = zod(this) -} +}) {} -export const Methods = Schema.Record(Schema.String, Schema.Array(Method)).pipe(withStatics((s) => ({ zod: zod(s) }))) +export const Methods = Schema.Record(Schema.String, Schema.Array(Method)) export type Methods = typeof Methods.Type export class Authorization extends Schema.Class("ProviderAuthAuthorization")({ url: Schema.String, method: Schema.Literals(["auto", "code"]), instructions: Schema.String, -}) { - static readonly zod = zod(this) -} +}) {} export const AuthorizeInput = Schema.Struct({ method: Schema.Finite.annotate({ description: "Auth method index" }), inputs: Schema.optional(Schema.Record(Schema.String, Schema.String)).annotate({ description: "Prompt inputs" }), -}).pipe(withStatics((s) => ({ zod: zod(s) }))) +}) export type AuthorizeInput = Schema.Schema.Type export const CallbackInput = Schema.Struct({ method: Schema.Finite.annotate({ description: "Auth method index" }), code: Schema.optional(Schema.String).annotate({ description: "OAuth authorization code" }), -}).pipe(withStatics((s) => ({ zod: zod(s) }))) +}) export type CallbackInput = Schema.Schema.Type -export const OauthMissing = namedSchemaError("ProviderAuthOauthMissing", { providerID: ProviderID }) +export class OauthMissing extends Schema.TaggedErrorClass()("ProviderAuthOauthMissing", { + providerID: ProviderID, +}) {} -export const OauthCodeMissing = namedSchemaError("ProviderAuthOauthCodeMissing", { providerID: ProviderID }) +export class OauthCodeMissing extends Schema.TaggedErrorClass()("ProviderAuthOauthCodeMissing", { + providerID: ProviderID, +}) {} -export const OauthCallbackFailed = namedSchemaError("ProviderAuthOauthCallbackFailed", {}) +export class OauthCallbackFailed extends Schema.TaggedErrorClass()( + "ProviderAuthOauthCallbackFailed", + {}, +) {} -export const ValidationFailed = namedSchemaError("ProviderAuthValidationFailed", { +export class ValidationFailed extends Schema.TaggedErrorClass()("ProviderAuthValidationFailed", { field: Schema.String, message: Schema.String, -}) +}) {} -export type Error = - | Auth.AuthError - | InstanceType - | InstanceType - | InstanceType - | InstanceType +export type Error = Auth.AuthError | OauthMissing | OauthCodeMissing | OauthCallbackFailed | ValidationFailed type Hook = NonNullable @@ -179,7 +175,7 @@ export const layer: Layer.Layer match.method === "code" ? match.callback(input.code!) : match.callback(), ) - if (!result || result.type !== "success") return yield* Effect.fail(new OauthCallbackFailed({})) + if (!result || result.type !== "success") return yield* new OauthCallbackFailed({}) if ("key" in result) { yield* auth.set(input.providerID, { type: "api", key: result.key, + ...(result.metadata ? { metadata: result.metadata } : {}), }) } diff --git a/packages/opencode/src/provider/model-cache.ts b/packages/opencode/src/provider/model-cache.ts index 750acb91b4f..67148057892 100644 --- a/packages/opencode/src/provider/model-cache.ts +++ b/packages/opencode/src/provider/model-cache.ts @@ -4,7 +4,7 @@ import { Context, Duration, Effect, Layer, Schema } from "effect" import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http" import { Config } from "../config/config" import { Auth } from "../auth" -import type { Provider } from "./models" +import type { Provider } from "@opencode-ai/core/models" import * as Log from "@opencode-ai/core/util/log" type Models = Provider["models"] diff --git a/packages/opencode/src/provider/model-status.ts b/packages/opencode/src/provider/model-status.ts index 468b59ce39d..7e1ca56e28d 100644 --- a/packages/opencode/src/provider/model-status.ts +++ b/packages/opencode/src/provider/model-status.ts @@ -1,7 +1,6 @@ import { Schema } from "effect" -export const CatalogModelStatus = Schema.Literals(["alpha", "beta", "deprecated"]) -export type CatalogModelStatus = typeof CatalogModelStatus.Type +export { CatalogModelStatus } from "@opencode-ai/core/models" export const ModelStatus = Schema.Literals(["alpha", "beta", "deprecated", "active"]) export type ModelStatus = typeof ModelStatus.Type diff --git a/packages/opencode/src/provider/models.ts b/packages/opencode/src/provider/models.ts index 61efa1d7392..b5c0bb1ebf3 100644 --- a/packages/opencode/src/provider/models.ts +++ b/packages/opencode/src/provider/models.ts @@ -1,315 +1,110 @@ -import { Global } from "@opencode-ai/core/global" -import path from "path" -import { Context, Duration, Effect, Layer, Option, Schedule, Schema } from "effect" -import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http" -import { Installation } from "../installation" -import { Flag } from "@opencode-ai/core/flag/flag" -import { Flock } from "@opencode-ai/core/util/flock" -import { Hash } from "@opencode-ai/core/util/hash" -import { AppFileSystem } from "@opencode-ai/core/filesystem" -import { withTransientReadRetry } from "@/util/effect-http-client" -import { CatalogModelStatus } from "./model-status" -// kilocode_change start -import { Config } from "../config/config" +// kilocode_change - adapt Kilo model assembly to the upstream core models service +import { Config } from "@/config/config" +import { Auth } from "@/auth" import { ModelCache } from "./model-cache" -import { Auth } from "../auth" +import * as Core from "@opencode-ai/core/models" +import { Context, Effect, Layer } from "effect" import { AI_SDK_PROVIDERS, KILO_OPENROUTER_BASE, PROMPTS } from "@kilocode/kilo-gateway" -// kilocode_change end -// kilocode_change start -const normalizeKiloBaseURL = (baseURL: string | undefined, org: string | undefined): string | undefined => { - if (!baseURL) return undefined - const trimmed = baseURL.replace(/\/+$/, "") - if (org) { - if (trimmed.includes("/api/organizations/")) return trimmed - if (trimmed.endsWith("/api")) return `${trimmed}/organizations/${org}` - return `${trimmed}/api/organizations/${org}` - } - if (trimmed.includes("/openrouter")) return trimmed - if (trimmed.endsWith("/api")) return `${trimmed}/openrouter` - return `${trimmed}/api/openrouter` -} -// kilocode_change end - -const Cost = Schema.Struct({ - input: Schema.Finite, - output: Schema.Finite, - cache_read: Schema.optional(Schema.Finite), - cache_write: Schema.optional(Schema.Finite), - context_over_200k: Schema.optional( - Schema.Struct({ - input: Schema.Finite, - output: Schema.Finite, - cache_read: Schema.optional(Schema.Finite), - cache_write: Schema.optional(Schema.Finite), - }), - ), -}) - -export const Model = Schema.Struct({ - id: Schema.String, - name: Schema.String, - family: Schema.optional(Schema.String), - release_date: Schema.String, - attachment: Schema.Boolean, - reasoning: Schema.Boolean, - temperature: Schema.Boolean, - tool_call: Schema.Boolean, - interleaved: Schema.optional( - Schema.Union([ - Schema.Literal(true), - Schema.Struct({ - field: Schema.Literals(["reasoning_content", "reasoning_details"]), - }), - ]), - ), - cost: Schema.optional(Cost), - limit: Schema.Struct({ - context: Schema.Finite, - input: Schema.optional(Schema.Finite), - output: Schema.Finite, - }), - modalities: Schema.optional( - Schema.Struct({ - input: Schema.Array(Schema.Literals(["text", "audio", "image", "video", "pdf"])), - output: Schema.Array(Schema.Literals(["text", "audio", "image", "video", "pdf"])), - }), - ), - // kilocode_change start - recommendedIndex: Schema.optional(Schema.Finite), - prompt: Schema.optional(Schema.Literals(PROMPTS)), - isFree: Schema.optional(Schema.Boolean), - ai_sdk_provider: Schema.optional(Schema.Literals(AI_SDK_PROVIDERS)), - // kilocode_change end - experimental: Schema.optional( - Schema.Struct({ - modes: Schema.optional( - Schema.Record( - Schema.String, - Schema.Struct({ - cost: Schema.optional(Cost), - provider: Schema.optional( - Schema.Struct({ - body: Schema.optional(Schema.Record(Schema.String, Schema.MutableJson)), - headers: Schema.optional(Schema.Record(Schema.String, Schema.String)), - }), - ), - }), - ), - ), - }), - ), - status: Schema.optional(CatalogModelStatus), - provider: Schema.optional( - Schema.Struct({ npm: Schema.optional(Schema.String), api: Schema.optional(Schema.String) }), - ), -}) -export type Model = Schema.Schema.Type +export const Model = Core.Model +export type Model = Core.Model +export const Provider = Core.Provider +export type Provider = Core.Provider +export const CatalogModelStatus = Core.CatalogModelStatus +export type CatalogModelStatus = Core.CatalogModelStatus -export const Provider = Schema.Struct({ - api: Schema.optional(Schema.String), - name: Schema.String, - env: Schema.Array(Schema.String), - id: Schema.String, - npm: Schema.optional(Schema.String), - models: Schema.Record(Schema.String, Model), -}) - -export type Provider = Schema.Schema.Type - -export interface Interface { - readonly get: () => Effect.Effect> - readonly refresh: (force?: boolean) => Effect.Effect -} +export interface Interface extends Core.Interface {} export class Service extends Context.Service()("@opencode/ModelsDev") {} -type Requirements = AppFileSystem.Service | HttpClient.HttpClient | Config.Service | Auth.Service | ModelCache.Service // kilocode_change +function baseURL(url: string | undefined, org: string | undefined) { + if (!url) return + const base = url.replace(/\/+$/, "") + if (org) { + if (base.includes("/api/organizations/")) return base + if (base.endsWith("/api")) return `${base}/organizations/${org}` + return `${base}/api/organizations/${org}` + } + if (base.includes("/openrouter")) return base + if (base.endsWith("/api")) return `${base}/openrouter` + return `${base}/api/openrouter` +} -export const layer: Layer.Layer = Layer.effect( +export const layer: Layer.Layer< + Service, + never, + Core.Service | Config.Service | Auth.Service | ModelCache.Service +> = Layer.effect( Service, Effect.gen(function* () { - const fs = yield* AppFileSystem.Service - const cfg = yield* Config.Service // kilocode_change - const auth = yield* Auth.Service // kilocode_change - const cache = yield* ModelCache.Service // kilocode_change - const http = HttpClient.filterStatusOk(withTransientReadRetry(yield* HttpClient.HttpClient)) - - const source = Flag.KILO_MODELS_URL || "https://models.dev" - const filepath = path.join( - Global.Path.cache, - source === "https://models.dev" ? "models.json" : `models-${Hash.fast(source)}.json`, - ) - const ttl = Duration.minutes(5) - const lockKey = `models-dev:${filepath}` - - const fresh = Effect.fnUntraced(function* () { - const stat = yield* fs.stat(filepath).pipe(Effect.catch(() => Effect.succeed(undefined))) - if (!stat) return false - const mtime = Option.getOrElse(stat.mtime, () => new Date(0)).getTime() - return Date.now() - mtime < Duration.toMillis(ttl) - }) - - const fetchApi = Effect.fn("ModelsDev.fetchApi")(function* () { - return yield* HttpClientRequest.get(`${source}/api.json`).pipe( - HttpClientRequest.setHeader("User-Agent", Installation.USER_AGENT), - http.execute, - Effect.flatMap((res) => res.text), - Effect.timeout("10 seconds"), - ) - }) - - const loadFromDisk = fs.readJson(Flag.KILO_MODELS_PATH ?? filepath).pipe( - Effect.catch(() => Effect.succeed(undefined)), - Effect.map((v) => v as Record | undefined), - ) - - // kilocode_change start - // Bundled at build time; absent in dev — `tryPromise` covers both. - const loadSnapshot = Effect.tryPromise({ - try: () => - import("../kilocode/provider/models-snapshot").then( - (m) => m.loadModelsSnapshot() as Promise | undefined>, - ), - catch: () => undefined, - }).pipe(Effect.catch(() => Effect.succeed(undefined))) - // kilocode_change end - - const fetchAndWrite = Effect.fn("ModelsDev.fetchAndWrite")(function* () { - const text = yield* fetchApi() - yield* fs.writeWithDirs(filepath, text) - return text - }) + const core = yield* Core.Service + const config = yield* Config.Service + const auth = yield* Auth.Service + const cache = yield* ModelCache.Service - const populate = Effect.gen(function* () { - const fromDisk = yield* loadFromDisk - if (fromDisk) return fromDisk - const snapshot = yield* loadSnapshot - if (snapshot) return snapshot - if (Flag.KILO_DISABLE_MODELS_FETCH) return {} - // Flock is cross-process: concurrent opencode CLIs can race on this cache file. - const text = yield* Effect.scoped( - Effect.gen(function* () { - yield* Flock.effect(lockKey) - return yield* fetchAndWrite() - }), - ) - return JSON.parse(text) as Record - }).pipe(Effect.withSpan("ModelsDev.populate"), Effect.orDie) - - const [cachedGet, invalidate] = yield* Effect.cachedInvalidateWithTTL(populate, Duration.infinity) - - // kilocode_change start const get = Effect.fn("ModelsDev.get")(function* () { - const providers = { ...(yield* cachedGet) } - delete providers["kilo"] - - const config = yield* cfg.get() - const disabled = new Set(config.disabled_providers ?? []) - const enabled = config.enabled_providers ? new Set(config.enabled_providers) : undefined - const kiloAllowed = (!enabled || enabled.has("kilo")) && !disabled.has("kilo") - const apt = config.provider?.apertis?.options - const aptBase = apt?.baseURL ?? "https://api.apertis.ai/v1" - const aptFetch = { - ...(apt?.baseURL ? { baseURL: apt.baseURL } : {}), - } - - if (kiloAllowed) { - const opts = config.provider?.kilo?.options - const info = yield* auth.get("kilo").pipe(Effect.catch(() => Effect.succeed(undefined))) - const org = opts?.kilocodeOrganizationId ?? (info?.type === "oauth" ? info.accountId : undefined) - const base = normalizeKiloBaseURL(opts?.baseURL, org) - const fetch = { - ...(base ? { baseURL: base } : {}), - ...(org ? { kilocodeOrganizationId: org } : {}), - } - const [kilo, apertis] = yield* Effect.all( - [ - cache.fetch("kilo", fetch).pipe(Effect.catch(() => Effect.succeed({}))), - providers["apertis"] - ? Effect.succeed(null) - : cache.fetch("apertis", aptFetch).pipe(Effect.catch(() => Effect.succeed({}))), - ], - { concurrency: 2 }, - ) - - providers["kilo"] = { - id: "kilo", - name: "Kilo Gateway", - env: ["KILO_API_KEY"], - api: KILO_OPENROUTER_BASE.endsWith("/") ? KILO_OPENROUTER_BASE : `${KILO_OPENROUTER_BASE}/`, - npm: "@kilocode/kilo-gateway", - models: kilo, - } - if (Object.keys(kilo).length === 0) { - yield* cache.refresh("kilo", fetch).pipe(Effect.ignore, Effect.forkDetach) - } - if (!providers["apertis"] && apertis !== null) { - providers["apertis"] = { - id: "apertis", - name: "Apertis", - env: ["APERTIS_API_KEY"], - api: aptBase, - npm: "@ai-sdk/openai-compatible", - models: apertis, - } - if (Object.keys(apertis).length === 0) { - yield* cache.refresh("apertis", aptFetch).pipe(Effect.ignore, Effect.forkDetach) - } - } - return providers - } - - if (!providers["apertis"]) { - const apertis = yield* cache.fetch("apertis", aptFetch).pipe(Effect.catch(() => Effect.succeed({}))) - providers["apertis"] = { + const providers = { ...(yield* core.get()) } + delete providers.kilo + + const cfg = yield* config.get() + const disabled = new Set(cfg.disabled_providers ?? []) + const enabled = cfg.enabled_providers ? new Set(cfg.enabled_providers) : undefined + const allowed = (!enabled || enabled.has("kilo")) && !disabled.has("kilo") + const apt = cfg.provider?.apertis?.options + const aptURL = apt?.baseURL ?? "https://api.apertis.ai/v1" + const aptOpts = apt?.baseURL ? { baseURL: apt.baseURL } : {} + + const addApertis = Effect.fnUntraced(function* () { + if (providers.apertis) return + const models = yield* cache.fetch("apertis", aptOpts).pipe(Effect.catch(() => Effect.succeed({}))) + providers.apertis = { id: "apertis", name: "Apertis", env: ["APERTIS_API_KEY"], - api: aptBase, + api: aptURL, npm: "@ai-sdk/openai-compatible", - models: apertis, - } - if (Object.keys(apertis).length === 0) { - yield* cache.refresh("apertis", aptFetch).pipe(Effect.ignore, Effect.forkDetach) + models, } + if (Object.keys(models).length === 0) yield* cache.refresh("apertis", aptOpts).pipe(Effect.ignore, Effect.forkDetach) + }) + + if (!allowed) { + yield* addApertis() + return providers } - return providers - }) - // kilocode_change end - const refresh = Effect.fn("ModelsDev.refresh")(function* (force = false) { - if (!force && (yield* fresh())) return - yield* Effect.scoped( - Effect.gen(function* () { - yield* Flock.effect(lockKey) - // Re-check under the lock: another process may have refreshed between - // our outer check and lock acquisition. - if (!force && (yield* fresh())) return - yield* fetchAndWrite() - yield* invalidate - }), - ).pipe( - Effect.tapCause((cause) => Effect.logError("Failed to fetch models.dev", { cause })), - Effect.ignore, - ) + const opts = cfg.provider?.kilo?.options + const info = yield* auth.get("kilo").pipe(Effect.catch(() => Effect.succeed(undefined))) + const org = opts?.kilocodeOrganizationId ?? (info?.type === "oauth" ? info.accountId : undefined) + const url = baseURL(opts?.baseURL, org) + const fetch = { + ...(url ? { baseURL: url } : {}), + ...(org ? { kilocodeOrganizationId: org } : {}), + } + const models = yield* cache.fetch("kilo", fetch).pipe(Effect.catch(() => Effect.succeed({}))) + providers.kilo = { + id: "kilo", + name: "Kilo Gateway", + env: ["KILO_API_KEY"], + api: KILO_OPENROUTER_BASE.endsWith("/") ? KILO_OPENROUTER_BASE : `${KILO_OPENROUTER_BASE}/`, + npm: "@kilocode/kilo-gateway", + models, + } + if (Object.keys(models).length === 0) yield* cache.refresh("kilo", fetch).pipe(Effect.ignore, Effect.forkDetach) + yield* addApertis() + return providers }) - if (!Flag.KILO_DISABLE_MODELS_FETCH && !process.argv.includes("--get-yargs-completions")) { - // Schedule.spaced runs the effect once, then waits between completions. - yield* Effect.forkScoped(refresh().pipe(Effect.repeat(Schedule.spaced("60 minutes")), Effect.ignore)) - } - - return Service.of({ get, refresh }) + return Service.of({ get, refresh: core.refresh }) }), ) -export const defaultLayer: Layer.Layer = layer.pipe( - Layer.provide(FetchHttpClient.layer), - Layer.provide(AppFileSystem.defaultLayer), - Layer.provide(Config.defaultLayer), // kilocode_change - Layer.provide(Auth.defaultLayer), // kilocode_change - Layer.provide(ModelCache.defaultLayer), // kilocode_change +export const defaultLayer = layer.pipe( + Layer.provide(Core.defaultLayer), + Layer.provide(Config.defaultLayer), + Layer.provide(Auth.defaultLayer), + Layer.provide(ModelCache.defaultLayer), ) +export { AI_SDK_PROVIDERS, PROMPTS } export * as ModelsDev from "./models" diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 74b283aaa9b..7e373a4b0ca 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -8,13 +8,10 @@ import { Npm } from "@opencode-ai/core/npm" import { Hash } from "@opencode-ai/core/util/hash" import { Plugin } from "../plugin" import { type LanguageModelV3 } from "@ai-sdk/provider" -import * as ModelsDev from "./models" +import * as ModelsDev from "./models" // kilocode_change - assemble dynamic Kilo models around upstream core catalog import { Auth } from "../auth" import { Env } from "../env" import { InstallationVersion } from "@opencode-ai/core/installation/version" -import { Flag } from "@opencode-ai/core/flag/flag" -import { zod } from "@opencode-ai/core/effect-zod" -import { namedSchemaError } from "@/util/named-schema-error" import { iife } from "@/util/iife" import { Global } from "@opencode-ai/core/global" import path from "path" @@ -22,13 +19,14 @@ import { pathToFileURL } from "url" import { Effect, Layer, Context, Schema, Types } from "effect" import { EffectBridge } from "@/effect/bridge" import { InstanceState } from "@/effect/instance-state" +import { EffectPromise } from "@/effect/promise" import { AppFileSystem } from "@opencode-ai/core/filesystem" import { isRecord } from "@/util/record" -import { optionalOmitUndefined, withStatics } from "@opencode-ai/core/schema" - +import { optionalOmitUndefined } from "@opencode-ai/core/schema" import * as ProviderTransform from "./transform" import { ModelID, ProviderID } from "./schema" import { ModelStatus } from "./model-status" +import { RuntimeFlags } from "@/effect/runtime-flags" // kilocode_change start import { KILO_BUNDLED_PROVIDERS, @@ -126,7 +124,8 @@ const BUNDLED_PROVIDERS: Record Promise<(opts: any) => BundledSDK> "@ai-sdk/vercel": () => import("@ai-sdk/vercel").then((m) => m.createVercel), "@ai-sdk/alibaba": () => import("@ai-sdk/alibaba").then((m) => m.createAlibaba), "gitlab-ai-provider": () => import("gitlab-ai-provider").then((m) => m.createGitLab), - "@ai-sdk/github-copilot": () => import("./sdk/copilot/copilot-provider").then((m) => m.createOpenaiCompatible), + "@ai-sdk/github-copilot": () => + import("@opencode-ai/core/github-copilot/copilot-provider").then((m) => m.createOpenaiCompatible), "venice-ai-sdk-provider": () => import("venice-ai-sdk-provider").then((m) => m.createVenice), ...KILO_BUNDLED_PROVIDERS, // kilocode_change } @@ -455,9 +454,9 @@ function custom(dep: CustomDep): Record { }, }, }), - nvidia: () => + nvidia: (provider) => Effect.succeed({ - autoload: false, + autoload: provider.source === "config", options: { headers: { "HTTP-Referer": "https://kilo.ai/", // kilocode_change @@ -478,8 +477,14 @@ function custom(dep: CustomDep): Record { }), "google-vertex": Effect.fnUntraced(function* (provider: Info) { const env = yield* dep.env() + // models.dev advertises GOOGLE_VERTEX_PROJECT for Vertex; keep the wider + // Google Cloud project env names as fallbacks for existing ADC setups. const project = - provider.options?.project ?? env["GOOGLE_CLOUD_PROJECT"] ?? env["GCP_PROJECT"] ?? env["GCLOUD_PROJECT"] + provider.options?.project ?? + env["GOOGLE_VERTEX_PROJECT"] ?? + env["GOOGLE_CLOUD_PROJECT"] ?? + env["GCP_PROJECT"] ?? + env["GCLOUD_PROJECT"] const location = String( provider.options?.location ?? @@ -768,6 +773,7 @@ function custom(dep: CustomDep): Record { const auth = yield* dep.auth(input.id) const env = yield* dep.env() const accountId = env["CLOUDFLARE_ACCOUNT_ID"] || (auth?.type === "api" ? auth.metadata?.accountId : undefined) + // The Cloudflare auth prompt stores this value as gatewayId metadata. const gateway = env["CLOUDFLARE_GATEWAY_ID"] || (auth?.type === "api" ? auth.metadata?.gatewayId : undefined) if (!accountId || !gateway) { @@ -898,10 +904,21 @@ const ProviderCacheCost = Schema.Struct({ write: Schema.Finite, }) +const ProviderCostTier = Schema.Struct({ + input: Schema.Finite, + output: Schema.Finite, + cache: ProviderCacheCost, + tier: Schema.Struct({ + type: Schema.Literal("context"), + size: Schema.Finite, + }), +}) + const ProviderCost = Schema.Struct({ input: Schema.Finite, output: Schema.Finite, cache: ProviderCacheCost, + tiers: optionalOmitUndefined(Schema.Array(ProviderCostTier)), experimentalOver200K: optionalOmitUndefined( Schema.Struct({ input: Schema.Finite, @@ -932,9 +949,7 @@ export const Model = Schema.Struct({ release_date: Schema.String, variants: optionalOmitUndefined(Schema.Record(Schema.String, Schema.Record(Schema.String, Schema.Any))), ...KILO_MODEL_SCHEMA_EXTENSIONS, // kilocode_change -}) - .annotate({ identifier: "Model" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "Model" }) export type Model = Types.DeepMutable> export const Info = Schema.Struct({ @@ -945,9 +960,7 @@ export const Info = Schema.Struct({ key: optionalOmitUndefined(Schema.String), options: Schema.Record(Schema.String, Schema.Any), models: Schema.Record(Schema.String, Model), -}) - .annotate({ identifier: "Provider" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "Provider" }) export type Info = Types.DeepMutable> const DefaultModelIDs = Schema.Record(Schema.String, Schema.String) @@ -957,13 +970,13 @@ export const ListResult = Schema.Struct({ default: DefaultModelIDs, connected: Schema.Array(Schema.String), failed: Schema.Array(Schema.String), // kilocode_change -}).pipe(withStatics((s) => ({ zod: zod(s) }))) +}) export type ListResult = Types.DeepMutable> export const ConfigProvidersResult = Schema.Struct({ providers: Schema.Array(Info), default: DefaultModelIDs, -}).pipe(withStatics((s) => ({ zod: zod(s) }))) +}) export type ConfigProvidersResult = Types.DeepMutable> export function toPublicInfo(provider: Info): Info { @@ -980,22 +993,45 @@ export function defaultModelIDs sort(Object.values(item.models))[0].id) } +export class ModelNotFoundError extends Schema.TaggedErrorClass()("ProviderModelNotFoundError", { + providerID: ProviderID, + modelID: ModelID, + suggestions: Schema.optional(Schema.Array(Schema.String)), + cause: Schema.optional(Schema.Defect), +}) { + static isInstance(input: unknown): input is ModelNotFoundError { + return input instanceof ModelNotFoundError + } +} + +export class InitError extends Schema.TaggedErrorClass()("ProviderInitError", { + providerID: ProviderID, + cause: Schema.optional(Schema.Defect), +}) { + static isInstance(input: unknown): input is InitError { + return input instanceof InitError + } +} + +export type Error = ModelNotFoundError | InitError + export interface Interface { readonly list: () => Effect.Effect> readonly getProvider: (providerID: ProviderID) => Effect.Effect - readonly getModel: (providerID: ProviderID, modelID: ModelID) => Effect.Effect - readonly getLanguage: (model: Model) => Effect.Effect + readonly getModel: (providerID: ProviderID, modelID: ModelID) => Effect.Effect + readonly getLanguage: (model: Model) => Effect.Effect readonly closest: ( providerID: ProviderID, query: string[], ) => Effect.Effect<{ providerID: ProviderID; modelID: string } | undefined> - readonly getSmallModel: (providerID: ProviderID) => Effect.Effect + readonly getSmallModel: (providerID: ProviderID) => Effect.Effect readonly defaultModel: () => Effect.Effect<{ providerID: ProviderID; modelID: ModelID }> } interface State { models: Map providers: Record + catalog: Record sdk: Map modelLoaders: Record varsLoaders: Record @@ -1012,6 +1048,17 @@ function cost(c: ModelsDev.Model["cost"]): Model["cost"] { write: c?.cache_write ?? 0, }, } + if (c?.tiers) { + result.tiers = c.tiers.map((item) => ({ + input: item.input, + output: item.output, + cache: { + read: item.cache_read ?? 0, + write: item.cache_write ?? 0, + }, + tier: item.tier, + })) + } if (c?.context_over_200k) { result.experimentalOver200K = { cache: { @@ -1111,11 +1158,39 @@ export function fromModelsDevProvider(provider: ModelsDev.Provider): Info { } } -const layer: Layer.Layer< - Service, - never, - Config.Service | Auth.Service | Plugin.Service | AppFileSystem.Service | Env.Service | ModelsDev.Service -> = Layer.effect( +function suggestionModelIDs(provider: Info | undefined, enableExperimentalModels: boolean) { + if (!provider) return [] + return Object.keys(provider.models).filter((id) => { + const model = provider.models[id] + if (model.status === "deprecated") return false + if (model.status === "alpha" && !enableExperimentalModels) return false + return true + }) +} + +function modelSuggestions(provider: Info | undefined, modelID: ModelID, enableExperimentalModels: boolean) { + const available = suggestionModelIDs(provider, enableExperimentalModels) + const fuzzy = fuzzysort.go(modelID, available, { limit: 3, threshold: -10000 }).map((m) => m.target) + if (fuzzy.length) return fuzzy + const query = modelID + .toLowerCase() + .split(/[^a-z0-9]+/) + .filter((part) => part.length > 1) + return sortBy( + available + .map((id) => ({ + id, + score: query.filter((part) => id.toLowerCase().includes(part)).length, + })) + .filter((item) => item.score > 0), + [(item) => item.score, "desc"], + [(item) => item.id, "asc"], + ) + .slice(0, 3) + .map((item) => item.id) +} + +export const layer = Layer.effect( Service, Effect.gen(function* () { const fs = yield* AppFileSystem.Service @@ -1124,6 +1199,7 @@ const layer: Layer.Layer< const env = yield* Env.Service const plugin = yield* Plugin.Service const modelsDevSvc = yield* ModelsDev.Service + const runtimeFlags = yield* RuntimeFlags.Service const state = yield* InstanceState.make(() => Effect.gen(function* () { @@ -1131,7 +1207,8 @@ const layer: Layer.Layer< const bridge = yield* EffectBridge.make() const cfg = yield* config.get() const modelsDev = yield* modelsDevSvc.get() - const database = mapValues(modelsDev, fromModelsDevProvider) + const catalog = mapValues(modelsDev, fromModelsDevProvider) + const database = mapValues(catalog, toPublicInfo) const providers: Record = {} as Record const languages = new Map() @@ -1428,11 +1505,16 @@ const layer: Layer.Layer< for (const [modelID, model] of Object.entries(provider.models)) { model.api.id = model.api.id ?? model.id ?? modelID if ( - modelID === "gpt-5-chat-latest" || + // These chat aliases are invalid for the special handling in the + // built-in providers below, but custom providers may support them. + (modelID === "gpt-5-chat-latest" && + (providerID === ProviderID.openai || + providerID === ProviderID.githubCopilot || + providerID === ProviderID.openrouter)) || (providerID === ProviderID.openrouter && modelID === "openai/gpt-5-chat") ) delete provider.models[modelID] - if (model.status === "alpha" && !Flag.KILO_ENABLE_EXPERIMENTAL_MODELS) delete provider.models[modelID] + if (model.status === "alpha" && !runtimeFlags.enableExperimentalModels) delete provider.models[modelID] if (model.status === "deprecated") delete provider.models[modelID] if ( (configProvider?.blacklist && configProvider.blacklist.includes(modelID)) || @@ -1465,6 +1547,7 @@ const layer: Layer.Layer< return { models: languages, providers, + catalog, sdk, modelLoaders, varsLoaders, @@ -1622,7 +1705,7 @@ const layer: Layer.Layer< s.sdk.set(key, loaded) return loaded as SDK } catch (e) { - throw new InitError({ providerID: model.providerID }, { cause: e }) + throw new InitError({ providerID: model.providerID, cause: e }) } } @@ -1634,16 +1717,22 @@ const layer: Layer.Layer< const s = yield* InstanceState.get(state) const provider = s.providers[providerID] if (!provider) { - const available = Object.keys(s.providers) - const matches = fuzzysort.go(providerID, available, { limit: 3, threshold: -10000 }) - throw new ModelNotFoundError({ providerID, modelID, suggestions: matches.map((m) => m.target) }) + const catalogProvider = s.catalog[providerID] + const suggestions = catalogProvider + ? modelSuggestions(catalogProvider, modelID, runtimeFlags.enableExperimentalModels) + : fuzzysort + .go(providerID, Object.keys({ ...s.catalog, ...s.providers }), { limit: 3, threshold: -10000 }) + .map((m) => m.target) + return yield* new ModelNotFoundError({ providerID, modelID, suggestions }) } const info = provider.models[modelID] if (!info) { - const available = Object.keys(provider.models) - const matches = fuzzysort.go(modelID, available, { limit: 3, threshold: -10000 }) - throw new ModelNotFoundError({ providerID, modelID, suggestions: matches.map((m) => m.target) }) + const current = modelSuggestions(provider, modelID, runtimeFlags.enableExperimentalModels) + const suggestions = current.length + ? current + : modelSuggestions(s.catalog[providerID], modelID, runtimeFlags.enableExperimentalModels) + return yield* new ModelNotFoundError({ providerID, modelID, suggestions }) } return info }) @@ -1654,11 +1743,10 @@ const layer: Layer.Layer< const key = `${model.providerID}/${model.id}` if (s.models.has(key)) return s.models.get(key)! - return yield* Effect.promise(async () => { - const provider = s.providers[model.providerID] - const sdk = await resolveSDK(model, s, envs) - - try { + const provider = s.providers[model.providerID] + return yield* EffectPromise.refineRejection( + async () => { + const sdk = await resolveSDK(model, s, envs) const language = s.modelLoaders[model.providerID] ? await s.modelLoaders[model.providerID](sdk, model.api.id, { ...provider.options, @@ -1667,18 +1755,12 @@ const layer: Layer.Layer< : sdk.languageModel(model.api.id) s.models.set(key, language) return language - } catch (e) { - if (e instanceof NoSuchModelError) - throw new ModelNotFoundError( - { - modelID: model.id, - providerID: model.providerID, - }, - { cause: e }, - ) - throw e - } - }) + }, + (cause) => + cause instanceof NoSuchModelError + ? new ModelNotFoundError({ modelID: model.id, providerID: model.providerID, cause }) + : undefined, + ) }) const closest = Effect.fn("Provider.closest")(function* (providerID: ProviderID, query: string[]) { @@ -1698,7 +1780,9 @@ const layer: Layer.Layer< if (cfg.small_model) { const parsed = parseModel(cfg.small_model) - return yield* getModel(parsed.providerID, parsed.modelID) + return yield* getModel(parsed.providerID, parsed.modelID).pipe( + Effect.catchTag("ProviderModelNotFoundError", () => Effect.succeed(undefined)), + ) } const s = yield* InstanceState.get(state) @@ -1730,22 +1814,22 @@ const layer: Layer.Layer< const candidates = Object.keys(provider.models).filter((m) => m.includes(item)) const globalMatch = candidates.find((m) => m.startsWith("global.")) - if (globalMatch) return yield* getModel(providerID, ModelID.make(globalMatch)) + if (globalMatch) return provider.models[globalMatch] const region = provider.options?.region if (region) { const regionPrefix = region.split("-")[0] if (regionPrefix === "us" || regionPrefix === "eu") { const regionalMatch = candidates.find((m) => m.startsWith(`${regionPrefix}.`)) - if (regionalMatch) return yield* getModel(providerID, ModelID.make(regionalMatch)) + if (regionalMatch) return provider.models[regionalMatch] } } const unprefixed = candidates.find((m) => !crossRegionPrefixes.some((p) => m.startsWith(p))) - if (unprefixed) return yield* getModel(providerID, ModelID.make(unprefixed)) + if (unprefixed) return provider.models[unprefixed] } else { for (const model of Object.keys(provider.models)) { - if (model.includes(item)) return yield* getModel(providerID, ModelID.make(model)) + if (model.includes(item)) return provider.models[model] } } } @@ -1806,6 +1890,7 @@ export const defaultLayer = Layer.suspend(() => Layer.provide(Auth.defaultLayer), Layer.provide(Plugin.defaultLayer), Layer.provide(ModelsDev.defaultLayer), + Layer.provide(RuntimeFlags.defaultLayer), ), ) @@ -1827,14 +1912,4 @@ export function parseModel(model: string) { } } -export const ModelNotFoundError = namedSchemaError("ProviderModelNotFoundError", { - providerID: ProviderID, - modelID: ModelID, - suggestions: Schema.optional(Schema.Array(Schema.String)), -}) - -export const InitError = namedSchemaError("ProviderInitError", { - providerID: ProviderID, -}) - export * as Provider from "./provider" diff --git a/packages/opencode/src/provider/schema.ts b/packages/opencode/src/provider/schema.ts index 82169e4bbad..c7d0fe3b250 100644 --- a/packages/opencode/src/provider/schema.ts +++ b/packages/opencode/src/provider/schema.ts @@ -1,6 +1,5 @@ import { Schema } from "effect" -import { zod } from "@opencode-ai/core/effect-zod" import { withStatics } from "@opencode-ai/core/schema" const providerIdSchema = Schema.String.pipe(Schema.brand("ProviderID")) @@ -9,7 +8,6 @@ export type ProviderID = typeof providerIdSchema.Type export const ProviderID = providerIdSchema.pipe( withStatics((schema: typeof providerIdSchema) => ({ - zod: zod(schema), // Well-known providers kilo: schema.make("kilo"), // kilocode_change opencode: schema.make("opencode"), @@ -30,8 +28,4 @@ const modelIdSchema = Schema.String.pipe(Schema.brand("ModelID")) export type ModelID = typeof modelIdSchema.Type -export const ModelID = modelIdSchema.pipe( - withStatics((schema: typeof modelIdSchema) => ({ - zod: zod(schema), - })), -) +export const ModelID = modelIdSchema diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 22ad11bf0ea..8249178e4a4 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -1,9 +1,8 @@ import type { ModelMessage, ToolResultPart } from "ai" import { mergeDeep, unique } from "remeda" import type { JSONSchema7 } from "@ai-sdk/provider" -import type { JSONSchema } from "zod/v4/core" import type * as Provider from "./provider" -import type * as ModelsDev from "./models" +import type * as ModelsDev from "@opencode-ai/core/models" import { iife } from "@/util/iife" import { Flag } from "@opencode-ai/core/flag/flag" import { kiloProviderOptions } from "@/kilocode/provider-options" @@ -630,14 +629,6 @@ function googleThinkingBudgetMax(apiId: string) { return 24_576 } -function googleSmallThinkingConfig(apiId: string) { - const levels = googleThinkingLevelEfforts(apiId) - if (apiId.toLowerCase().includes("gemini-3")) { - return { thinkingLevel: levels.includes("minimal") ? "minimal" : levels.includes("low") ? "low" : "high" } - } - return { thinkingBudget: googleThinkingBudgetMax(apiId) === 32_768 ? 128 : 0 } -} - export function variants(model: Provider.Model): Record> { // kilocode_change start if ( @@ -1191,6 +1182,11 @@ export function options(input: { result["enable_thinking"] = true } + if (input.model.api.npm === "@ai-sdk/azure" && input.model.api.id.includes("gpt-5.5")) { + result["reasoningSummary"] = "auto" + return result + } + if (input.model.api.id.includes("gpt-5") && !input.model.api.id.includes("gpt-5-chat")) { if (!input.model.api.id.includes("gpt-5-pro")) { result["reasoningEffort"] = "medium" @@ -1245,27 +1241,14 @@ export function options(input: { } export function smallOptions(model: Provider.Model) { + const small = Object.values(model.variants ?? {})[0] ?? {} if ( model.providerID === "openai" || model.api.npm === "@ai-sdk/openai" || model.api.npm === "@ai-sdk/github-copilot" ) { - if (model.api.id.includes("gpt-5")) { - if (model.api.id.includes("-chat")) { - if (gpt5Version(model.api.id) === undefined) return { store: false } - return { store: false, reasoningEffort: "medium" } - } - if (model.api.id.includes("search-api")) return { store: false } - if (model.api.id.includes("5.") || model.api.id.includes("5-mini")) { - return { store: false, reasoningEffort: "low" } - } - return { store: false, reasoningEffort: "minimal" } - } - return { store: false } - } - if (model.providerID === "google") { - // gemini-3 uses thinkingLevel, gemini-2.5 uses thinkingBudget - return { thinkingConfig: googleSmallThinkingConfig(model.api.id) } + const base = { store: false } + return mergeDeep(base, small) } if ( model.providerID === "openrouter" || @@ -1277,10 +1260,11 @@ export function smallOptions(model: Provider.Model) { } if (model.providerID === "venice") { + if (Object.keys(small).length > 0) return small return { veniceParameters: { disableThinking: true } } } - return {} + return small } // Maps model ID prefix to provider slug used in providerOptions. @@ -1350,7 +1334,7 @@ export function maxOutputTokens(model: Provider.Model): number { return Math.min(model.limit.output, OUTPUT_TOKEN_MAX) || OUTPUT_TOKEN_MAX } -export function schema(model: Provider.Model, schema: JSONSchema.BaseSchema | JSONSchema7): JSONSchema7 { +export function schema(model: Provider.Model, schema: JSONSchema7): JSONSchema7 { /* if (["openai", "azure"].includes(providerID)) { if (schema.type === "object" && schema.properties) { @@ -1381,7 +1365,10 @@ export function schema(model: Provider.Model, schema: JSONSchema.BaseSchema | JS return result } - schema = sanitizeMoonshot(schema) as JSONSchema.BaseSchema | JSONSchema7 + const sanitized = sanitizeMoonshot(schema) + if (typeof sanitized === "object" && sanitized !== null && !Array.isArray(sanitized)) { + schema = sanitized + } } // Convert integer enums to string enums for Google/Gemini @@ -1463,7 +1450,7 @@ export function schema(model: Provider.Model, schema: JSONSchema.BaseSchema | JS schema = sanitizeGemini(schema) } - return schema as JSONSchema7 + return schema } export * as ProviderTransform from "./transform" diff --git a/packages/opencode/src/pty/index.ts b/packages/opencode/src/pty/index.ts index fd29d6982d0..4bba4c0729d 100644 --- a/packages/opencode/src/pty/index.ts +++ b/packages/opencode/src/pty/index.ts @@ -11,8 +11,7 @@ import type { Proc } from "#pty" import * as Log from "@opencode-ai/core/util/log" import { PtyID } from "./schema" import { Effect, Layer, Context, Schema, Types } from "effect" -import { zod } from "@opencode-ai/core/effect-zod" -import { NonNegativeInt, PositiveInt, withStatics } from "@opencode-ai/core/schema" +import { NonNegativeInt, PositiveInt } from "@opencode-ai/core/schema" import { SessionID } from "@/session/schema" // kilocode_change const log = Log.create({ service: "pty" }) @@ -65,9 +64,7 @@ export const Info = Schema.Struct({ status: Schema.Literals(["running", "exited"]), pid: PositiveInt, sessionID: Schema.optional(Schema.NullOr(SessionID)), // kilocode_change -}) - .annotate({ identifier: "Pty" }) - .pipe(withStatics((s) => ({ zod: zod(s) }))) +}).annotate({ identifier: "Pty" }) export type Info = Types.DeepMutable> @@ -77,7 +74,7 @@ export const CreateInput = Schema.Struct({ cwd: Schema.optional(Schema.String), title: Schema.optional(Schema.String), env: Schema.optional(Schema.Record(Schema.String, Schema.String)), -}).pipe(withStatics((s) => ({ zod: zod(s) }))) +}) export type CreateInput = Types.DeepMutable> @@ -90,7 +87,7 @@ export const UpdateInput = Schema.Struct({ cols: PositiveInt, }), ), -}).pipe(withStatics((s) => ({ zod: zod(s) }))) +}) export type UpdateInput = Types.DeepMutable> diff --git a/packages/opencode/src/pty/schema.ts b/packages/opencode/src/pty/schema.ts index fadb0457e71..c86ae8c7382 100644 --- a/packages/opencode/src/pty/schema.ts +++ b/packages/opencode/src/pty/schema.ts @@ -1,7 +1,6 @@ import { Schema } from "effect" import { Identifier } from "@/id/id" -import { zod } from "@opencode-ai/core/effect-zod" import { withStatics } from "@opencode-ai/core/schema" const ptyIdSchema = Schema.String.check(Schema.isStartsWith("pty")).pipe(Schema.brand("PtyID")) @@ -11,6 +10,5 @@ export type PtyID = typeof ptyIdSchema.Type export const PtyID = ptyIdSchema.pipe( withStatics((schema: typeof ptyIdSchema) => ({ ascending: (id?: string) => schema.make(Identifier.ascending("pty", id)), - zod: zod(schema), })), ) diff --git a/packages/opencode/src/question/index.ts b/packages/opencode/src/question/index.ts index d062eafe965..11b73be9009 100644 --- a/packages/opencode/src/question/index.ts +++ b/packages/opencode/src/question/index.ts @@ -3,9 +3,7 @@ import { Bus } from "@/bus" import { BusEvent } from "@/bus/bus-event" import { InstanceState } from "@/effect/instance-state" import { SessionID, MessageID } from "@/session/schema" -import { zod } from "@opencode-ai/core/effect-zod" import * as Log from "@opencode-ai/core/util/log" -import { withStatics } from "@opencode-ai/core/schema" import { QuestionID } from "./schema" import { KiloQuestion } from "@/kilocode/question" // kilocode_change @@ -35,9 +33,7 @@ export class Option extends Schema.Class
Effect.Effect -export const NotFoundError = NamedError.create( - "NotFoundError", - z.object({ - message: z.string(), - }), -) +export class NotFoundError extends Schema.TaggedErrorClass()("NotFoundError", { + message: Schema.String, +}) { + static isInstance(input: unknown): input is NotFoundError { + return input instanceof NotFoundError + } +} -export type Error = AppFileSystem.Error | InstanceType +export type Error = AppFileSystem.Error | NotFoundError const RootFile = Schema.Struct({ path: Schema.optional( @@ -249,7 +248,7 @@ export const layer = Layer.effect( }), ) - const fail = (target: string): Effect.Effect> => + const fail = (target: string): Effect.Effect => Effect.fail(new NotFoundError({ message: `Resource not found: ${target}` })) const wrap = (target: string, body: Effect.Effect) => diff --git a/packages/opencode/src/sync/README.md b/packages/opencode/src/sync/README.md index cb7e8756486..eec71d30011 100644 --- a/packages/opencode/src/sync/README.md +++ b/packages/opencode/src/sync/README.md @@ -55,9 +55,9 @@ There are now "sync events" which are different than "bus events". Bus events ar ```ts const Diff = BusEvent.define( "session.diff", - z.object({ - sessionID: SessionID.zod, - diff: Snapshot.FileDiff.array(), + Schema.Struct({ + sessionID: SessionID, + diff: Schema.Array(Snapshot.FileDiff), }), ) ``` @@ -71,8 +71,8 @@ const Created = SyncEvent.define({ type: "session.created", version: 1, aggregate: "sessionID", - schema: z.object({ - sessionID: SessionID.zod, + schema: Schema.Struct({ + sessionID: SessionID, info: Info, }), }) @@ -114,7 +114,7 @@ This allows you to "reshape" an event from the sync system before it's published The only time we use this is the `session.updated` event. Previously this event contained the entire session object. The sync event only contains the fields updated. We convert the event to contain the full object for backwards compatibility (but ideally we'd remove this). -It's very important that types are correct when working with events. Event definitions have a `schema` which carries the definition of the event shape (provided by a zod schema, inferred into a TypeScript type). Examples: +It's very important that types are correct when working with events. Event definitions have a `schema` which carries the definition of the event shape. Examples: ```ts // The schema from `Updated` typechecks the object correctly @@ -149,12 +149,12 @@ const Update = SyncEvent.define({ type: "session.updated", version: 1, aggregate: "sessionID", - schema: z.object({ - sessionID: SessionID.zod, + schema: Schema.Struct({ + sessionID: SessionID, info: partialSchema(Info), }), - busSchema: z.object({ - sessionID: SessionID.zod, + busSchema: Schema.Struct({ + sessionID: SessionID, info: Info, }), }) diff --git a/packages/opencode/src/sync/index.ts b/packages/opencode/src/sync/index.ts index 8a50c8f16d8..7f9b8eeef16 100644 --- a/packages/opencode/src/sync/index.ts +++ b/packages/opencode/src/sync/index.ts @@ -7,12 +7,11 @@ import type { InstanceContext } from "@/project/instance" import { EventSequenceTable, EventTable } from "./event.sql" import type { WorkspaceID } from "@/control-plane/schema" import { EventID } from "./schema" -import { Flag } from "@opencode-ai/core/flag/flag" import { Context, Effect, Layer, Schema as EffectSchema } from "effect" import type { DeepMutable } from "@opencode-ai/core/schema" -import { makeRuntime } from "@/effect/run-service" import { serviceUse } from "@/effect/service-use" import { InstanceState } from "@/effect/instance-state" +import { RuntimeFlags } from "@/effect/runtime-flags" // Keep `Event["data"]` mutable because projectors mutate the persisted shape // when writing to the database. Bus payloads (`Properties`) stay readonly — @@ -63,12 +62,15 @@ export interface Interface { options?: { publish: boolean; ownerID?: string }, ) => Effect.Effect readonly remove: (aggregateID: string) => Effect.Effect + readonly claim: (aggregateID: string, ownerID: string) => Effect.Effect } export class Service extends Context.Service()("@opencode/SyncEvent") {} export const layer = Layer.effect(Service)( Effect.gen(function* () { + const flags = yield* RuntimeFlags.Service + const replay: Interface["replay"] = Effect.fn("SyncEvent.replay")(function* (event, options) { const def = registry.get(event.type) if (!def) { @@ -104,7 +106,12 @@ export const layer = Layer.effect(Service)( workspace: yield* InstanceState.workspaceID, } : undefined - process(def, event, { publish, context, ownerID: options?.ownerID }) + process(def, event, { + publish, + context, + ownerID: options?.ownerID, + experimentalWorkspaces: flags.experimentalWorkspaces, + }) }) const replayAll: Interface["replayAll"] = Effect.fn("SyncEvent.replayAll")(function* (events, options) { @@ -160,7 +167,7 @@ export const layer = Layer.effect(Service)( const seq = row?.seq != null ? row.seq + 1 : 0 const event = { id, seq, aggregateID: agg, data } - process(def, event, { publish, context }) + process(def, event, { publish, context, experimentalWorkspaces: flags.experimentalWorkspaces }) }, { behavior: "immediate", @@ -175,21 +182,32 @@ export const layer = Layer.effect(Service)( }) }) + const claim: Interface["claim"] = Effect.fn("SyncEvent.claim")((aggregateID, ownerID) => + Effect.sync(() => + Database.use((db) => + db + .update(EventSequenceTable) + .set({ owner_id: ownerID }) + .where(eq(EventSequenceTable.aggregate_id, aggregateID)) + .run(), + ), + ), + ) + return Service.of({ run, replay, replayAll, remove, + claim, }) }), ) -export const defaultLayer = layer +export const defaultLayer = layer.pipe(Layer.provide(RuntimeFlags.defaultLayer)) export const use = serviceUse(Service) -const runtime = makeRuntime(Service, defaultLayer) - export const registry = new Map() let projectors: Map | undefined const versions = new Map() @@ -268,7 +286,7 @@ export function project( function process( def: Def, event: Event, - options: { publish: boolean; context?: PublishContext; ownerID?: string }, + options: { publish: boolean; context?: PublishContext; ownerID?: string; experimentalWorkspaces: boolean }, ) { if (projectors == null) { throw new Error("No projectors available. Call `SyncEvent.init` to install projectors") @@ -282,7 +300,7 @@ function process( Database.transaction((tx) => { projector(tx, event.data, event) - if (Flag.KILO_EXPERIMENTAL_WORKSPACES) { + if (options.experimentalWorkspaces) { tx.insert(EventSequenceTable) .values({ aggregate_id: event.aggregateID, @@ -336,32 +354,6 @@ function process( }) } -export function replay(event: SerializedEvent, options?: { publish: boolean; ownerID?: string }) { - return runtime.runSync((sync) => sync.replay(event, options)) -} - -export function replayAll(events: SerializedEvent[], options?: { publish: boolean; ownerID?: string }) { - return runtime.runSync((sync) => sync.replayAll(events, options)) -} - -export function run(def: Def, data: Event["data"], options?: { publish?: boolean }) { - return runtime.runSync((sync) => sync.run(def, data, options)) -} - -export function remove(aggregateID: string) { - return runtime.runSync((sync) => sync.remove(aggregateID)) -} - -export function claim(aggregateID: string, ownerID: string) { - Database.use((db) => - db - .update(EventSequenceTable) - .set({ owner_id: ownerID }) - .where(eq(EventSequenceTable.aggregate_id, aggregateID)) - .run(), - ) -} - export function effectPayloads() { return registry .entries() diff --git a/packages/opencode/src/sync/schema.ts b/packages/opencode/src/sync/schema.ts index dde2e53d17a..4ff9d2fc335 100644 --- a/packages/opencode/src/sync/schema.ts +++ b/packages/opencode/src/sync/schema.ts @@ -1,13 +1,11 @@ import { Schema } from "effect" import { Identifier } from "@/id/id" -import { zod } from "@opencode-ai/core/effect-zod" import { withStatics } from "@opencode-ai/core/schema" export const EventID = Schema.String.check(Schema.isStartsWith("evt")).pipe( Schema.brand("EventID"), withStatics((s) => ({ ascending: (id?: string) => s.make(Identifier.ascending("event", id)), - zod: zod(s), })), ) diff --git a/packages/opencode/src/tool/apply_patch.ts b/packages/opencode/src/tool/apply_patch.ts index e94eae9d98e..32ae0483ed7 100644 --- a/packages/opencode/src/tool/apply_patch.ts +++ b/packages/opencode/src/tool/apply_patch.ts @@ -138,10 +138,13 @@ export const ApplyPatchTool = Tool.define( // Apply the update chunks to get new content try { - const fileUpdate = Patch.deriveNewContentsFromChunks(filePath, hunk.chunks) + const fileUpdate = Patch.deriveNewContentsFromChunks( + filePath, + hunk.chunks, + Bom.join(source.text, source.bom), + ) newContent = fileUpdate.content bom = fileUpdate.bom - encoding = fileUpdate.encoding // kilocode_change } catch (error) { return yield* Effect.fail(new Error(`apply_patch verification failed: ${error}`)) } diff --git a/packages/opencode/src/tool/codesearch.ts b/packages/opencode/src/tool/codesearch.ts index 4616d5900a7..ceabb7cb168 100644 --- a/packages/opencode/src/tool/codesearch.ts +++ b/packages/opencode/src/tool/codesearch.ts @@ -1,9 +1,15 @@ +// kilocode_change - retained Kilo tool removed upstream import { Effect, Schema } from "effect" import { HttpClient } from "effect/unstable/http" import * as Tool from "./tool" import * as McpWebSearch from "./mcp-websearch" import DESCRIPTION from "./codesearch.txt" +const CodeArgs = Schema.Struct({ + query: Schema.String, + tokensNum: Schema.Number, +}) + export const Parameters = Schema.Struct({ query: Schema.String.annotate({ description: @@ -42,7 +48,7 @@ export const CodeSearchTool = Tool.define( http, McpWebSearch.EXA_URL, "get_code_context_exa", - McpWebSearch.CodeArgs, + CodeArgs, { query: params.query, tokensNum: params.tokensNum, diff --git a/packages/opencode/src/tool/grep.ts b/packages/opencode/src/tool/grep.ts index 4e89198dffd..01aa6a0b72b 100644 --- a/packages/opencode/src/tool/grep.ts +++ b/packages/opencode/src/tool/grep.ts @@ -54,19 +54,20 @@ export const GrepTool = Tool.define( }) const ins = yield* InstanceState.context - const search = AppFileSystem.resolve( - path.isAbsolute(params.path ?? ins.directory) - ? (params.path ?? ins.directory) - : path.join(ins.directory, params.path ?? "."), - ) - yield* reference.ensure(search) + const requested = path.isAbsolute(params.path ?? ins.directory) + ? (params.path ?? ins.directory) + : path.join(ins.directory, params.path ?? ".") + yield* reference.ensure(requested) + const requestedInfo = yield* fs.stat(requested).pipe(Effect.catch(() => Effect.succeed(undefined))) + yield* assertExternalDirectoryEffect(ctx, requested, { + bypass: yield* reference.contains(requested), + kind: requestedInfo?.type === "Directory" ? "directory" : "file", + }) + + const search = AppFileSystem.resolve(requested) const info = yield* fs.stat(search).pipe(Effect.catch(() => Effect.succeed(undefined))) const cwd = info?.type === "Directory" ? search : path.dirname(search) const file = info?.type === "Directory" ? undefined : [path.relative(cwd, search)] - yield* assertExternalDirectoryEffect(ctx, search, { - bypass: yield* reference.contains(search), - kind: info?.type === "Directory" ? "directory" : "file", - }) const result = yield* rg.search({ cwd, diff --git a/packages/opencode/src/tool/json-schema.ts b/packages/opencode/src/tool/json-schema.ts new file mode 100644 index 00000000000..edb43e11ca8 --- /dev/null +++ b/packages/opencode/src/tool/json-schema.ts @@ -0,0 +1,164 @@ +import type { JSONSchema7 } from "@ai-sdk/provider" +import { JsonSchema, Schema } from "effect" +import type * as Tool from "./tool" + +type JsonObject = Record +const cache = new WeakMap() + +export function fromSchema(schema: Schema.Top): JSONSchema7 { + const cached = cache.get(schema) + if (cached) return cached + + const document = Schema.toJsonSchemaDocument(schema, { additionalProperties: true }) + const result = normalize({ + $schema: JsonSchema.META_SCHEMA_URI_DRAFT_2020_12, + ...document.schema, + ...(Object.keys(document.definitions).length > 0 ? { $defs: document.definitions } : {}), + }) + const inlined = dropDefinitionsIfResolved(inlineLocalReferences(result)) + if (!isJsonSchema(inlined)) throw new Error("tool JSON Schema helper produced a non-schema value") + cache.set(schema, inlined) + return inlined +} + +export function fromTool(tool: Tool.Def): JSONSchema7 { + return tool.jsonSchema ?? fromSchema(tool.parameters as Schema.Top) +} + +function normalize(value: unknown, options: { stripNull?: boolean } = {}): unknown { + if (Array.isArray(value)) return value.map((item) => normalize(item)) + if (!isRecord(value)) return value + + const required = Array.isArray(value.required) + ? new Set(value.required.filter((item) => typeof item === "string")) + : undefined + const schema = Object.fromEntries( + Object.entries(value).map(([key, item]) => [ + key, + key === "properties" && isRecord(item) + ? Object.fromEntries( + Object.entries(item).map(([name, property]) => [ + name, + normalize(property, { stripNull: !required?.has(name) }), + ]), + ) + : normalize(item), + ]), + ) + + if (schema.additionalProperties === true) delete schema.additionalProperties + + if (options.stripNull && Array.isArray(schema.anyOf)) { + const withoutNull = schema.anyOf.filter((item) => !isRecord(item) || item.type !== "null") + if (withoutNull.length !== schema.anyOf.length) return normalize({ ...schema, anyOf: withoutNull }) + } + + if (Array.isArray(schema.anyOf)) { + const withoutNull = schema.anyOf + const number = withoutNull.find((item) => isRecord(item) && item.type === "number") + const nonFinite = withoutNull.filter( + (item) => isRecord(item) && Array.isArray(item.enum) && item.enum.every((entry) => isNonFiniteNumber(entry)), + ) + if (number && nonFinite.length === withoutNull.length - 1) { + const { anyOf: _, ...rest } = schema + return normalize({ ...number, ...rest }) + } + + if (isEmptyStructUnion(withoutNull)) { + const { anyOf: _, ...rest } = schema + return normalize({ type: "object", properties: {}, ...rest }) + } + + if (withoutNull.length === 1 && isRecord(withoutNull[0])) { + const { anyOf: _, ...rest } = schema + return normalize({ ...withoutNull[0], ...rest }) + } + } + + if (Array.isArray(schema.allOf) && schema.allOf.every(isRecord) && canFlattenAllOf(schema.allOf, schema)) { + const { allOf, ...rest } = schema + return normalize({ ...Object.assign({}, ...allOf), ...rest }) + } + + if (schema.type === "integer" && schema.maximum === undefined) { + return { minimum: Number.MIN_SAFE_INTEGER, ...schema, maximum: Number.MAX_SAFE_INTEGER } + } + + return schema +} + +function isRecord(value: unknown): value is JsonObject { + return typeof value === "object" && value !== null && !Array.isArray(value) +} + +function isJsonSchema(value: unknown): value is JSONSchema7 { + return typeof value === "boolean" || isRecord(value) +} + +function isNonFiniteNumber(value: unknown) { + return value === "NaN" || value === "Infinity" || value === "-Infinity" +} + +function isEmptyStructUnion(items: unknown[]) { + return ( + items.length === 2 && + items.some((item) => isRecord(item) && item.type === "object" && item.properties === undefined) && + items.some((item) => isRecord(item) && item.type === "array" && item.items === undefined) + ) +} + +function canFlattenAllOf(allOf: JsonObject[], parent: JsonObject) { + const keys = new Set(Object.keys(parent).filter((key) => key !== "allOf")) + return allOf.every((item) => + Object.keys(item).every((key) => { + if (keys.has(key)) return false + keys.add(key) + return true + }), + ) +} + +function inlineLocalReferences(value: unknown, definitions?: JsonObject, seen = new Set()): unknown { + if (Array.isArray(value)) return value.map((item) => inlineLocalReferences(item, definitions, seen)) + if (!isRecord(value)) return value + + const localDefinitions = definitions ?? (isRecord(value.$defs) ? value.$defs : undefined) + if (typeof value.$ref === "string" && localDefinitions) { + const name = value.$ref.match(/^#\/\$defs\/(.+)$/)?.[1] ?? value.$ref.match(/^#\/definitions\/(.+)$/)?.[1] + if (name && !seen.has(name)) { + const target = localDefinitions[name] + if (target) { + const { $ref: _, ...rest } = value + return inlineLocalReferences( + { ...(isRecord(target) ? target : {}), ...rest }, + localDefinitions, + new Set(seen).add(name), + ) + } + } + } + + return Object.fromEntries( + Object.entries(value).map(([key, item]) => [key, inlineLocalReferences(item, localDefinitions, seen)]), + ) +} + +function dropDefinitionsIfResolved(value: unknown): unknown { + if (!isRecord(value) || hasLocalReference(value)) return value + const { $defs: _, definitions: __, ...rest } = value + return rest +} + +function hasLocalReference(value: unknown): boolean { + if (Array.isArray(value)) return value.some(hasLocalReference) + if (!isRecord(value)) return false + if ( + typeof value.$ref === "string" && + (value.$ref.startsWith("#/$defs/") || value.$ref.startsWith("#/definitions/")) + ) { + return true + } + return Object.values(value).some(hasLocalReference) +} + +export * as ToolJsonSchema from "./json-schema" diff --git a/packages/opencode/src/tool/mcp-websearch.ts b/packages/opencode/src/tool/mcp-websearch.ts index 42b864c6faf..208924cba5a 100644 --- a/packages/opencode/src/tool/mcp-websearch.ts +++ b/packages/opencode/src/tool/mcp-websearch.ts @@ -48,11 +48,6 @@ export const SearchArgs = Schema.Struct({ contextMaxCharacters: Schema.optional(Schema.Number), }) -export const CodeArgs = Schema.Struct({ - query: Schema.String, - tokensNum: Schema.Number, -}) - export const ParallelSearchArgs = Schema.Struct({ objective: Schema.String, search_queries: Schema.Array(Schema.String), diff --git a/packages/opencode/src/tool/read.ts b/packages/opencode/src/tool/read.ts index b2e9d54d95b..8c8fc214670 100644 --- a/packages/opencode/src/tool/read.ts +++ b/packages/opencode/src/tool/read.ts @@ -1,8 +1,7 @@ -import { lstat } from "fs/promises" // kilocode_change import { Effect, Option, Schema, Scope } from "effect" import { NonNegativeInt } from "@opencode-ai/core/schema" import * as path from "path" -import type { Readable } from "stream" // kilocode_change +import { Readable } from "stream" // kilocode_change import { createInterface } from "readline" import * as Tool from "./tool" import { AppFileSystem } from "@opencode-ai/core/filesystem" @@ -15,7 +14,6 @@ import { isPdfAttachment, sniffAttachmentMime } from "@/util/media" import { Reference } from "@/reference/reference" // kilocode_change start import * as Encoding from "../kilocode/encoding" -import * as TextStream from "../kilocode/text-stream" import * as Extract from "../kilocode/tool/read-extract" // kilocode_change end @@ -112,6 +110,31 @@ export const ReadTool = Tool.define( ) }) + const lines = Effect.fn("ReadTool.lines")((filepath: string, opts: { limit: number; offset: number }) => + // kilocode_change - extracted formats still need their native readers; ordinary text stays on AppFileSystem + Effect.tryPromise({ + try: () => Extract.open(filepath), + catch: (err) => (err instanceof Error ? err : new Error(String(err))), + }).pipe( + Effect.flatMap((extracted) => + extracted + ? Effect.tryPromise({ + try: () => collect(extracted, opts), + catch: (err) => (err instanceof Error ? err : new Error(String(err))), + }) + : fs.readFile(filepath).pipe( + Effect.map((bytes) => Encoding.decode(Buffer.from(bytes), Encoding.detect(Buffer.from(bytes)))), + Effect.flatMap((text) => + Effect.tryPromise({ + try: () => collect(Readable.from([text]), opts), + catch: (err) => (err instanceof Error ? err : new Error(String(err))), + }), + ), + ), + ), + ), + ) + const isBinaryFile = (filepath: string, bytes: Uint8Array) => { const ext = path.extname(filepath).toLowerCase() switch (ext) { @@ -180,13 +203,13 @@ export const ReadTool = Tool.define( items.filter((item) => !item.endsWith("/") && types.get(item) === "file"), Effect.fnUntraced(function* (item) { const child = path.join(filepath, item) - const info = yield* Effect.promise(() => lstat(child)).pipe(Effect.catch(() => Effect.void)) - if (!info?.isFile()) return + const info = yield* fs.stat(child).pipe(Effect.catch(() => Effect.void)) + if (info?.type !== "File") return const sample = yield* readSample(child, Number(info.size), SAMPLE_BYTES).pipe( Effect.catch(() => Effect.succeed(new Uint8Array())), ) if (isBinaryFile(child, sample)) return - const file = yield* Effect.promise(() => lines(child, { limit: DEFAULT_READ_LIMIT, offset: 1 })).pipe( + const file = yield* lines(child, { limit: DEFAULT_READ_LIMIT, offset: 1 }).pipe( Effect.catch(() => Effect.void), ) if (!file) return @@ -309,9 +332,7 @@ export const ReadTool = Tool.define( return yield* Effect.fail(new Error(`Cannot read binary file: ${filepath}`)) } - const file = yield* Effect.promise(() => - lines(filepath, { limit: params.limit ?? DEFAULT_READ_LIMIT, offset: params.offset || 1 }), - ) + const file = yield* lines(filepath, { limit: params.limit ?? DEFAULT_READ_LIMIT, offset: params.offset || 1 }) if (file.count < file.offset && !(file.count === 0 && file.offset === 1)) { return yield* Effect.fail( new Error(`Offset ${file.offset} is out of range for this file (${file.count} lines)`), @@ -360,24 +381,10 @@ export const ReadTool = Tool.define( }), ) -// kilocode_change start - exported (so readDirectoryFiles can reuse it) and -// routed through TextStream.withFallback so non-UTF-8 files are decoded via -// iconv. The body otherwise matches upstream. -export async function lines(filepath: string, opts: { limit: number; offset: number }) { - const extracted = await Extract.open(filepath) - if (extracted) return readLines(extracted, opts) - return TextStream.withFallback(filepath, (stream) => readLines(stream, opts)) -} - -async function readLines(stream: Readable, opts: { limit: number; offset: number }) { +// kilocode_change start - extracted formats use native readers; ordinary text is supplied by AppFileSystem above +async function collect(stream: Readable, opts: { limit: number; offset: number }) { // kilocode_change end - const rl = createInterface({ - input: stream, - // Note: we use the crlfDelay option to recognize all instances of CR LF - // ('\r\n') in file as a single line break. - crlfDelay: Infinity, - }) - + const rl = createInterface({ input: stream, crlfDelay: Infinity }) const start = opts.offset - 1 const raw: string[] = [] let bytes = 0 @@ -388,12 +395,10 @@ async function readLines(stream: Readable, opts: { limit: number; offset: number for await (const text of rl) { count += 1 if (count <= start) continue - if (raw.length >= opts.limit) { more = true continue } - const line = text.length > MAX_LINE_LENGTH ? text.substring(0, MAX_LINE_LENGTH) + MAX_LINE_SUFFIX : text const size = Buffer.byteLength(line, "utf-8") + (raw.length > 0 ? 1 : 0) if (bytes + size > MAX_BYTES) { @@ -401,7 +406,6 @@ async function readLines(stream: Readable, opts: { limit: number; offset: number more = true break } - raw.push(line) bytes += size } @@ -409,6 +413,5 @@ async function readLines(stream: Readable, opts: { limit: number; offset: number rl.close() stream.destroy() } - return { raw, count, cut, more, offset: opts.offset } } diff --git a/packages/opencode/src/tool/registry.ts b/packages/opencode/src/tool/registry.ts index ce81b706efc..82276296563 100644 --- a/packages/opencode/src/tool/registry.ts +++ b/packages/opencode/src/tool/registry.ts @@ -11,6 +11,7 @@ import { GlobTool } from "./glob" import { GrepTool } from "./grep" import { ReadTool } from "./read" import { TaskTool } from "./task" +import { TaskStatusTool } from "./task_status" import { TodoWriteTool } from "./todo" import { WebFetchTool } from "./webfetch" import { WriteTool } from "./write" @@ -19,18 +20,18 @@ import { SkillTool } from "./skill" import * as Tool from "./tool" import { Config } from "@/config/config" import { type ToolContext as PluginToolContext, type ToolDefinition } from "@kilocode/plugin" +import type { JSONSchema7, JSONSchema7Definition } from "@ai-sdk/provider" import { Schema } from "effect" import z from "zod" -import { ZodOverride } from "@opencode-ai/core/effect-zod" import { Plugin } from "../plugin" import { Provider } from "@/provider/provider" import { ProviderID, type ModelID } from "../provider/schema" import { WebSearchTool } from "./websearch" import { KiloToolRegistry } from "../kilocode/tool/registry" // kilocode_change -import { CodeSearchTool } from "./codesearch" +import { CodeSearchTool } from "./codesearch" // kilocode_change import { RepoCloneTool } from "./repo_clone" import { RepoOverviewTool } from "./repo_overview" -import { Flag } from "@opencode-ai/core/flag/flag" +import { Flag } from "@opencode-ai/core/flag/flag" // kilocode_change import * as Log from "@opencode-ai/core/util/log" import { LspTool } from "./lsp" import * as Truncate from "./truncate" @@ -57,6 +58,8 @@ import { Skill } from "../skill" import { Permission } from "@/permission" import { SessionStatus } from "@/session/status" // kilocode_change import { Reference } from "@/reference/reference" +import { BackgroundJob } from "@/background/job" +import { RuntimeFlags } from "@/effect/runtime-flags" const log = Log.create({ service: "tool.registry" }) @@ -96,6 +99,8 @@ export const layer: Layer.Layer< | Agent.Service | Skill.Service | Session.Service + | SessionStatus.Service + | BackgroundJob.Service | Provider.Service | Git.Service | Reference.Service @@ -110,8 +115,8 @@ export const layer: Layer.Layer< | Truncate.Service // kilocode_change start | Command.Service - | SessionStatus.Service // kilocode_change end + | RuntimeFlags.Service > = Layer.effect( Service, Effect.gen(function* () { @@ -120,9 +125,11 @@ export const layer: Layer.Layer< const agents = yield* Agent.Service const skill = yield* Skill.Service const truncate = yield* Truncate.Service + const flags = yield* RuntimeFlags.Service const invalid = yield* InvalidTool const task = yield* TaskTool + const taskStatus = yield* TaskStatusTool const read = yield* ReadTool const question = yield* QuestionTool const todo = yield* TodoWriteTool @@ -130,7 +137,7 @@ export const layer: Layer.Layer< const plan = yield* PlanExitTool const webfetch = yield* WebFetchTool const websearch = yield* WebSearchTool - const codesearch = yield* CodeSearchTool + const codesearch = yield* CodeSearchTool // kilocode_change const repoClone = yield* RepoCloneTool const repoOverview = yield* RepoOverviewTool const shell = yield* ShellTool @@ -151,17 +158,19 @@ export const layer: Layer.Layer< const custom: Tool.Def[] = [] function fromPlugin(id: string, def: ToolDefinition): Tool.Def { - // Plugin tools define their args as a raw Zod shape. Wrap the - // derived Zod object in a `Schema.declare` so it slots into the - // Schema-typed framework, and annotate with `ZodOverride` so the - // walker emits the original Zod object for LLM JSON Schema. - const zodParams = z.object(def.args) - const parameters = Schema.declare((u): u is unknown => zodParams.safeParse(u).success).annotate({ - [ZodOverride]: zodParams, - }) + // Plugin tools still expose Zod args publicly; keep that compatibility + // boxed at the registry boundary and give the LLM the original JSON Schema. + const entries = Object.entries(def.args) + const allZod = entries.every((entry) => isZodType(entry[1])) + const zodParams = allZod ? z.object(def.args) : undefined + const jsonSchema = zodParams ? zodJsonSchema(zodParams) : legacyJsonSchema(entries) + const parameters = zodParams + ? Schema.declare((u): u is unknown => zodParams.safeParse(u).success) + : Schema.Unknown return { id, parameters, + jsonSchema, description: def.description, execute: (args, toolCtx) => Effect.gen(function* () { @@ -174,11 +183,13 @@ export const layer: Layer.Layer< const result = yield* Effect.promise(() => def.execute(args as any, pluginCtx)) const output = typeof result === "string" ? result : result.output const metadata = typeof result === "string" ? {} : (result.metadata ?? {}) + const attachments = typeof result === "string" ? undefined : result.attachments const info = yield* agent.get(toolCtx.agent) const out = yield* truncate.output(output, {}, info) return { - title: "", + title: typeof result === "string" ? "" : (result.title ?? ""), output: out.truncated ? out.content : output, + attachments, metadata: { ...metadata, truncated: out.truncated, @@ -221,8 +232,7 @@ export const layer: Layer.Layer< } const cfg = yield* config.get() // kilocode_change: capture for KiloToolRegistry.extra - const questionEnabled = - ["app", "cli", "desktop", "vscode"].includes(Flag.KILO_CLIENT) || Flag.KILO_ENABLE_QUESTION_TOOL // kilocode_change: add vscode client + KILO_* flag + const questionEnabled = ["app", "cli", "desktop", "vscode"].includes(flags.client) || flags.enableQuestionTool // kilocode_change: add vscode client const tool = yield* Effect.all({ invalid: Tool.init(invalid), @@ -233,10 +243,11 @@ export const layer: Layer.Layer< edit: Tool.init(edit), write: Tool.init(writetool), task: Tool.init(task), + task_status: Tool.init(taskStatus), fetch: Tool.init(webfetch), todo: Tool.init(todo), search: Tool.init(websearch), - code: Tool.init(codesearch), + code: Tool.init(codesearch), // kilocode_change repo_clone: Tool.init(repoClone), repo_overview: Tool.init(repoOverview), skill: Tool.init(skilltool), @@ -263,16 +274,18 @@ export const layer: Layer.Layer< tool.edit, tool.write, tool.task, + ...(flags.experimentalBackgroundSubagents ? [tool.task_status] : []), tool.fetch, tool.todo, tool.search, - ...(Flag.KILO_EXPERIMENTAL_SCOUT ? [tool.code, tool.repo_clone, tool.repo_overview] : []), + tool.code, // kilocode_change + ...(flags.experimentalScout ? [tool.repo_clone, tool.repo_overview] : []), tool.skill, tool.patch, tool.plan, - ...(["cli", "vscode"].includes(Flag.KILO_CLIENT) ? [tool.suggest] : []), + ...(["cli", "vscode"].includes(flags.client) ? [tool.suggest] : []), ...KiloToolRegistry.extra(kilo, cfg), - ...(Flag.KILO_EXPERIMENTAL_LSP_TOOL ? [tool.lsp] : []), + ...(flags.experimentalLspTool ? [tool.lsp] : []), ], kilo, ), @@ -329,7 +342,7 @@ export const layer: Layer.Layer< const tools: Interface["tools"] = Effect.fn("ToolRegistry.tools")(function* (input) { const filtered = (yield* all()).filter((tool) => { if (tool.id === WebSearchTool.id) { - return webSearchEnabled(input.providerID) + return webSearchEnabled(input.providerID, { exa: flags.enableExa, parallel: flags.enableParallel }) } const usePatch = @@ -350,8 +363,13 @@ export const layer: Layer.Layer< const output = { description: tool.description, parameters: tool.parameters, + jsonSchema: tool.jsonSchema, } yield* plugin.trigger("tool.definition", { toolID: tool.id }, output) + const jsonSchema = + output.parameters === tool.parameters || output.jsonSchema !== tool.jsonSchema + ? output.jsonSchema + : undefined return { id: tool.id, description: [ @@ -362,6 +380,7 @@ export const layer: Layer.Layer< .filter(Boolean) .join("\n"), parameters: output.parameters, + jsonSchema, execute: tool.execute, formatValidationError: tool.formatValidationError, } @@ -390,6 +409,7 @@ export const defaultLayer = Layer.suspend( Layer.provide(Skill.defaultLayer), Layer.provide(Agent.defaultLayer), Layer.provide(Session.defaultLayer), + Layer.provide(Layer.mergeAll(SessionStatus.defaultLayer, BackgroundJob.defaultLayer)), Layer.provide(Provider.defaultLayer), Layer.provide(Git.defaultLayer), Layer.provide(Reference.defaultLayer), @@ -404,7 +424,54 @@ export const defaultLayer = Layer.suspend( Layer.provide(Truncate.defaultLayer), ) // kilocode_change start - provide Kilo-owned registry dependencies - .pipe(Layer.provide(Command.defaultLayer), Layer.provide(SessionStatus.defaultLayer)), + .pipe(Layer.provide(Command.defaultLayer), Layer.provide(RuntimeFlags.defaultLayer)), // kilocode_change end ) + +function isZodType(value: unknown): value is z.ZodType { + return typeof value === "object" && value !== null && "_zod" in value +} + +function isJsonSchemaDefinition(value: unknown): value is JSONSchema7Definition { + return typeof value === "boolean" || (typeof value === "object" && value !== null && !Array.isArray(value)) +} + +function legacyJsonSchema(entries: [string, unknown][]): JSONSchema7 { + const properties = Object.fromEntries( + entries.filter((entry): entry is [string, JSONSchema7Definition] => isJsonSchemaDefinition(entry[1])), + ) + return { + type: "object", + properties, + required: Object.keys(properties), + } +} + +function zodJsonSchema(schema: z.ZodType): JSONSchema7 { + const result = normalizeZodJsonSchema(z.toJSONSchema(schema, { io: "input" })) + if (!isJsonSchemaObject(result)) throw new Error("plugin tool Zod schema produced a non-object JSON Schema") + const { $defs, ...rest } = result + return ( + $defs && isJsonSchemaObject($defs) ? { ...rest, definitions: $defs as JSONSchema7["definitions"] } : rest + ) as JSONSchema7 +} + +function normalizeZodJsonSchema(value: unknown): unknown { + if (Array.isArray(value)) return value.map((item) => normalizeZodJsonSchema(item)) + if (typeof value !== "object" || value === null) return value + return Object.fromEntries( + Object.entries(value) + .filter((entry) => + (entry[0] === "exclusiveMaximum" || entry[0] === "exclusiveMinimum") && typeof entry[1] === "boolean" + ? false + : true, + ) + .map(([key, item]) => [key, normalizeZodJsonSchema(item)]), + ) +} + +function isJsonSchemaObject(value: unknown): value is Record { + return typeof value === "object" && value !== null && !Array.isArray(value) +} + export * as ToolRegistry from "./registry" diff --git a/packages/opencode/src/tool/schema.ts b/packages/opencode/src/tool/schema.ts index a80d9151535..cdeadb74d62 100644 --- a/packages/opencode/src/tool/schema.ts +++ b/packages/opencode/src/tool/schema.ts @@ -1,7 +1,6 @@ import { Schema } from "effect" import { Identifier } from "@/id/id" -import { zod } from "@opencode-ai/core/effect-zod" import { withStatics } from "@opencode-ai/core/schema" const toolIdSchema = Schema.String.check(Schema.isStartsWith("tool")).pipe(Schema.brand("ToolID")) @@ -11,6 +10,5 @@ export type ToolID = typeof toolIdSchema.Type export const ToolID = toolIdSchema.pipe( withStatics((schema: typeof toolIdSchema) => ({ ascending: (id?: string) => schema.make(Identifier.ascending("tool", id)), - zod: zod(schema), })), ) diff --git a/packages/opencode/src/tool/shell.ts b/packages/opencode/src/tool/shell.ts index fcfdb5f2e2c..527e1781a1b 100644 --- a/packages/opencode/src/tool/shell.ts +++ b/packages/opencode/src/tool/shell.ts @@ -12,7 +12,7 @@ import { Language, type Node } from "web-tree-sitter" import { AppFileSystem } from "@opencode-ai/core/filesystem" import { fileURLToPath } from "url" import { Config } from "@/config/config" -import { Flag } from "@opencode-ai/core/flag/flag" +import { RuntimeFlags } from "@/effect/runtime-flags" import { Shell } from "@/shell/shell" import { ShellID } from "./shell/id" @@ -27,7 +27,6 @@ import { BashArity } from "@/permission/arity" export { Parameters } from "./shell/prompt" const MAX_METADATA_LENGTH = 30_000 -const DEFAULT_TIMEOUT = Flag.KILO_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS || 2 * 60 * 1000 const CWD = new Set(["cd", "chdir", "popd", "pushd", "push-location", "set-location"]) const FILES = new Set([ ...CWD, @@ -363,6 +362,8 @@ export const ShellTool = Tool.define( const fs = yield* AppFileSystem.Service const trunc = yield* Truncate.Service const plugin = yield* Plugin.Service + const flags = yield* RuntimeFlags.Service + const defaultTimeout = flags.bashDefaultTimeoutMs ?? 2 * 60 * 1000 const cygpath = Effect.fn("ShellTool.cygpath")(function* (shell: string, text: string) { const lines = yield* spawner @@ -478,6 +479,31 @@ export const ShellTool = Tool.define( let expired = false let aborted = false + const closeSink = Effect.fnUntraced(function* () { + const stream = sink + if (!stream) return + sink = undefined + if (stream.destroyed || stream.closed) return + yield* Effect.promise( + () => + new Promise((resolve) => { + let settled = false + const done = () => { + if (settled) return + settled = true + stream.off("close", done) + stream.off("error", done) + stream.off("finish", done) + resolve() + } + stream.once("close", done) + stream.once("error", done) + stream.once("finish", done) + stream.end(done) + }), + ).pipe(Effect.catch(() => Effect.void)) + }) + yield* ctx.metadata({ metadata: { output: "", @@ -487,6 +513,7 @@ export const ShellTool = Tool.define( const code: number | null = yield* Effect.scoped( Effect.gen(function* () { + yield* Effect.addFinalizer(closeSink) const handle = yield* spawner.spawn(cmd(input.shell, input.command, input.cwd, input.env)) yield* Effect.forkScoped( @@ -590,17 +617,6 @@ export const ShellTool = Tool.define( if (meta.length > 0) { output += "\n\n\n" + meta.join("\n") + "\n" } - if (sink) { - const stream = sink - yield* Effect.promise( - () => - new Promise((resolve) => { - stream.end(() => resolve()) - stream.on("error", () => resolve()) - }), - ) - } - return { title: input.description, metadata: { @@ -635,7 +651,7 @@ export const ShellTool = Tool.define( if (params.timeout !== undefined && params.timeout < 0) { throw new Error(`Invalid timeout value: ${params.timeout}. Timeout must be a positive number.`) } - const timeout = params.timeout ?? DEFAULT_TIMEOUT + const timeout = params.timeout ?? defaultTimeout const ps = Shell.ps(shell) yield* Effect.scoped( Effect.gen(function* () { diff --git a/packages/opencode/src/tool/task.ts b/packages/opencode/src/tool/task.ts index 533316b538e..aa074dea264 100644 --- a/packages/opencode/src/tool/task.ts +++ b/packages/opencode/src/tool/task.ts @@ -1,28 +1,47 @@ import * as Tool from "./tool" import DESCRIPTION from "./task.txt" +import { ToolJsonSchema } from "./json-schema" +import { BackgroundJob } from "@/background/job" +import { Bus } from "@/bus" import { Session } from "@/session/session" import { SessionID, MessageID } from "../session/schema" import { MessageV2 } from "../session/message-v2" import { Agent } from "../agent/agent" import { deriveSubagentSessionPermission } from "../agent/subagent-permissions" import type { SessionPrompt } from "../session/prompt" +import { SessionStatus } from "@/session/status" import { Config } from "@/config/config" import { Provider } from "@/provider/provider" // kilocode_change import { KiloTask } from "../kilocode/tool/task" // kilocode_change import { KiloCostPropagation } from "../kilocode/session/cost-propagation" // kilocode_change import { KiloSessionProcessor } from "../kilocode/session/processor" // kilocode_change +import { KiloSession } from "../kilocode/session" // kilocode_change import { errorMessage } from "@/util/error" // kilocode_change -import { Effect, Exit, Schema } from "effect" +import { TuiEvent } from "@/cli/cmd/tui/event" +import { Cause, Effect, Exit, Option, Schema, Scope } from "effect" import { EffectBridge } from "@/effect/bridge" +import { RuntimeFlags } from "@/effect/runtime-flags" export interface TaskPromptOps { cancel(sessionID: SessionID): Effect.Effect resolvePromptParts(template: string): Effect.Effect prompt(input: SessionPrompt.PromptInput): Effect.Effect + loop(input: SessionPrompt.LoopInput): Effect.Effect } const id = "task" +const BaseParameters = Schema.Struct({ + description: Schema.String.annotate({ description: "A short (3-5 words) description of the task" }), + prompt: Schema.String.annotate({ description: "The task for the agent to perform" }), + subagent_type: Schema.String.annotate({ description: "The type of specialized agent to use for this task" }), + task_id: Schema.optional(Schema.String).annotate({ + description: + "This should only be set if you mean to resume a previous task (you can pass a prior task_id and the task will continue the same subagent session as before instead of creating a fresh one)", + }), + command: Schema.optional(Schema.String).annotate({ description: "The command that triggered this task" }), +}) + export const Parameters = Schema.Struct({ description: Schema.String.annotate({ description: "A short (3-5 words) description of the task" }), prompt: Schema.String.annotate({ description: "The task for the agent to perform" }), @@ -32,21 +51,75 @@ export const Parameters = Schema.Struct({ "This should only be set if you mean to resume a previous task (you can pass a prior task_id and the task will continue the same subagent session as before instead of creating a fresh one)", }), command: Schema.optional(Schema.String).annotate({ description: "The command that triggered this task" }), + background: Schema.optional(Schema.Boolean).annotate({ + description: "When true, launch the subagent in the background and return immediately", + }), }) +function output(sessionID: SessionID, text: string) { + return [ + `task_id: ${sessionID} (for resuming to continue this task if needed)`, + "", + "", + text, + "", + ].join("\n") +} + +function backgroundOutput(sessionID: SessionID) { + return [ + `task_id: ${sessionID} (for polling this task with task_status)`, + "state: running", + "", + "", + "Background task started. Continue your current work and call task_status when you need the result.", + "", + ].join("\n") +} + +function backgroundMessage(input: { + sessionID: SessionID + description: string + state: "completed" | "error" + text: string +}) { + const tag = input.state === "completed" ? "task_result" : "task_error" + const title = + input.state === "completed" + ? `Background task completed: ${input.description}` + : `Background task failed: ${input.description}` + return [title, `task_id: ${input.sessionID}`, `state: ${input.state}`, "", `<${tag}>`, input.text, ``].join( + "\n", + ) +} + +function errorText(error: unknown) { + if (error instanceof Error) return error.message + return String(error) +} + export const TaskTool = Tool.define( id, Effect.gen(function* () { const agent = yield* Agent.Service + const background = yield* BackgroundJob.Service + const bus = yield* Bus.Service const config = yield* Config.Service const sessions = yield* Session.Service const provider = yield* Provider.Service // kilocode_change + const scope = yield* Scope.Scope + const status = yield* SessionStatus.Service + const flags = yield* RuntimeFlags.Service const run = Effect.fn("TaskTool.execute")(function* ( params: Schema.Schema.Type, ctx: Tool.Context, ) { const cfg = yield* config.get() + const runInBackground = params.background === true + if (runInBackground && !flags.experimentalBackgroundSubagents) { + return yield* Effect.fail(new Error("Background subagents require KILO_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true")) + } if (!ctx.extra?.bypassAgentCheck) { yield* ctx.ask({ @@ -100,11 +173,13 @@ export const TaskTool = Tool.define( yield* sessions.setPermission({ sessionID: session.id, permission: session.permission }) } // kilocode_change end + const platform = KiloSession.resolvePlatform(ctx.sessionID) // kilocode_change - preserve parent attribution across task creation/resume const nextSession = session ?? (yield* sessions.create({ parentID: ctx.sessionID, title: params.description + ` (@${next.name} subagent)`, + platform, // kilocode_change // kilocode_change start - dedupe inherited restrictions before child prompt toggles persist permission: KiloTask.merge( deriveSubagentSessionPermission({ @@ -121,8 +196,11 @@ export const TaskTool = Tool.define( ), // kilocode_change end })) + // kilocode_change start - rebuild in-memory ancestry and attribution after process restart + KiloSession.register({ id: nextSession.id, parentID: ctx.sessionID, platform }) + // kilocode_change end - const msg = yield* Effect.sync(() => MessageV2.get({ sessionID: ctx.sessionID, messageID: ctx.messageID })) + const msg = yield* MessageV2.get({ sessionID: ctx.sessionID, messageID: ctx.messageID }).pipe(Effect.orDie) if (msg.info.role !== "assistant") return yield* Effect.fail(new Error("Not an assistant message")) // kilocode_change start — prefer valid subagent overrides, safely inheriting when overrides go stale @@ -139,21 +217,152 @@ export const TaskTool = Tool.define( const model = selected.model const variant = selected.variant // kilocode_change end + const metadata = { + parentSessionId: ctx.sessionID, + sessionId: nextSession.id, + model, + variant, // kilocode_change + ...(runInBackground ? { background: true } : {}), + } yield* ctx.metadata({ title: params.description, - metadata: { - sessionId: nextSession.id, - model, - variant, // kilocode_change - }, + metadata, }) const ops = ctx.extra?.promptOps as TaskPromptOps if (!ops) return yield* Effect.fail(new Error("TaskTool requires promptOps in ctx.extra")) const runCancel = yield* EffectBridge.make() - const messageID = MessageID.ascending() + const runTask = Effect.fn("TaskTool.runTask")(function* () { + const parts = yield* ops.resolvePromptParts(params.prompt) + KiloSessionProcessor.markReviewTelemetry(parts, params.command) // kilocode_change - carry review command into child session telemetry + const result = yield* ops.prompt({ + messageID: MessageID.ascending(), + sessionID: nextSession.id, + model: { + modelID: model.modelID, + providerID: model.providerID, + }, + variant, // kilocode_change + agent: next.name, + tools: { + ...(canTodo ? {} : { todowrite: false }), + ...(canTask ? {} : { task: false }), + ...Object.fromEntries((cfg.experimental?.primary_tools ?? []).map((item) => [item, false])), + }, + parts, + }) + // kilocode_change start - expose terminal child assistant errors through the task tool boundary + if (result.info.role === "assistant" && result.info.error) { + return yield* Effect.fail(new Error(errorMessage(result.info.error))) + } + // kilocode_change end + return result.parts.findLast((item) => item.type === "text")?.text ?? "" + }) + + const resumeWhenIdle: (input: { userID: MessageID; state: "completed" | "error" }) => Effect.Effect = + Effect.fn("TaskTool.resumeWhenIdle")(function* (input: { userID: MessageID; state: "completed" | "error" }) { + const latest = yield* sessions + .findMessage(ctx.sessionID, (item) => item.info.role === "user") + .pipe(Effect.orDie) + if (Option.isNone(latest)) return + if (latest.value.info.id !== input.userID) return + if ((yield* status.get(ctx.sessionID)).type !== "idle") { + yield* Effect.sleep("300 millis") + return yield* resumeWhenIdle(input) + } + yield* bus.publish(TuiEvent.ToastShow, { + title: input.state === "completed" ? "Background task complete" : "Background task failed", + message: + input.state === "completed" + ? `Background task "${params.description}" finished. Resuming the main thread.` + : `Background task "${params.description}" failed. Resuming the main thread.`, + variant: input.state === "completed" ? "success" : "error", + duration: 5000, + }) + yield* ops + .loop({ sessionID: ctx.sessionID }) + .pipe(Effect.ignore, Effect.forkIn(scope, { startImmediately: true })) + }) + + const continueIfIdle = Effect.fn("TaskTool.continueIfIdle")(function* (input: { + userID: MessageID + state: "completed" | "error" + }) { + yield* resumeWhenIdle(input).pipe(Effect.ignore, Effect.forkIn(scope, { startImmediately: true })) + }) + + const inject = Effect.fn("TaskTool.injectBackgroundResult")(function* ( + state: "completed" | "error", + text: string, + ) { + const currentParent = yield* sessions.get(ctx.sessionID) + const message = yield* ops.prompt({ + sessionID: ctx.sessionID, + noReply: true, + agent: currentParent.agent ?? ctx.agent, + parts: [ + { + type: "text", + synthetic: true, + text: backgroundMessage({ + sessionID: nextSession.id, + description: params.description, + state, + text, + }), + }, + ], + }) + yield* continueIfIdle({ userID: message.info.id, state }) + }) + + const existing = yield* background.get(nextSession.id) + if (existing?.status === "running") { + return yield* Effect.fail( + new Error(`Task ${nextSession.id} is already running. Use task_status to check progress.`), + ) + } + + if (runInBackground) { + const info = yield* background.start({ + id: nextSession.id, + type: id, + title: params.description, + metadata, + // kilocode_change start - background tasks propagate only cost accrued by this invocation + run: Effect.acquireUseRelease( + KiloCostPropagation.childCost(sessions, nextSession.id), + () => + runTask().pipe( + Effect.tap((text) => inject("completed", text).pipe(Effect.ignore)), + Effect.catchCause((cause) => + (Cause.hasInterruptsOnly(cause) + ? Effect.void + : inject("error", errorText(Cause.squash(cause))).pipe(Effect.ignore) + ).pipe(Effect.andThen(Effect.failCause(cause))), + ), + ), + (costBefore) => + Effect.gen(function* () { + const costAfter = yield* KiloCostPropagation.childCost(sessions, nextSession.id) + yield* KiloCostPropagation.propagate(sessions, ctx.sessionID, ctx.messageID, costAfter - costBefore) + }), + ), + // kilocode_change end + }) + + return { + title: params.description, + metadata: { + ...metadata, + jobId: info.id, + }, + output: backgroundOutput(nextSession.id), + } + } + const cancel = ops.cancel(nextSession.id) function onAbort() { @@ -169,45 +378,11 @@ export const TaskTool = Tool.define( // kilocode_change end () => Effect.gen(function* () { - const parts = yield* ops.resolvePromptParts(params.prompt) - KiloSessionProcessor.markReviewTelemetry(parts, params.command) // kilocode_change - carry review command into child session telemetry - const result = yield* ops.prompt({ - messageID, - sessionID: nextSession.id, - model: { - modelID: model.modelID, - providerID: model.providerID, - }, - variant, // kilocode_change - agent: next.name, - tools: { - ...(canTodo ? {} : { todowrite: false }), - ...(canTask ? {} : { task: false }), - ...Object.fromEntries((cfg.experimental?.primary_tools ?? []).map((item) => [item, false])), - }, - parts, - }) - - // kilocode_change start - expose terminal child assistant errors through the task tool boundary - if (result.info.role === "assistant" && result.info.error) { - return yield* Effect.fail(new Error(errorMessage(result.info.error))) - } - // kilocode_change end - + const text = yield* runTask() return { title: params.description, - metadata: { - sessionId: nextSession.id, - model, - variant, // kilocode_change - }, - output: [ - `task_id: ${nextSession.id} (for resuming to continue this task if needed)`, - "", - "", - result.parts.findLast((item) => item.type === "text")?.text ?? "", - "", - ].join("\n"), + metadata, + output: output(nextSession.id, text), } }), // kilocode_change start - propagate subagent cost delta to parent on every exit path (#6321) @@ -218,8 +393,12 @@ export const TaskTool = Tool.define( Effect.ensuring( Effect.gen(function* () { ctx.abort.removeEventListener("abort", onAbort) - const costAfter = yield* KiloCostPropagation.childCost(sessions, nextSession.id) - yield* KiloCostPropagation.propagate(sessions, ctx.sessionID, ctx.messageID, costAfter - costBefore) + const costAfter = yield* KiloCostPropagation.childCost(sessions, nextSession.id).pipe( + Effect.catchTag("NotFoundError", () => Effect.succeed(costBefore)), + ) + yield* KiloCostPropagation.propagate(sessions, ctx.sessionID, ctx.messageID, costAfter - costBefore).pipe( + Effect.catchTag("NotFoundError", () => Effect.void), + ) }), ), ), @@ -230,6 +409,7 @@ export const TaskTool = Tool.define( return { description: DESCRIPTION, parameters: Parameters, + jsonSchema: flags.experimentalBackgroundSubagents ? undefined : ToolJsonSchema.fromSchema(BaseParameters), execute: (params: Schema.Schema.Type, ctx: Tool.Context) => run(params, ctx).pipe(Effect.orDie), } diff --git a/packages/opencode/src/tool/task.txt b/packages/opencode/src/tool/task.txt index fba8470d1b4..cab2374a3e2 100644 --- a/packages/opencode/src/tool/task.txt +++ b/packages/opencode/src/tool/task.txt @@ -15,10 +15,11 @@ When NOT to use the Task tool: Usage notes: 1. Launch multiple agents concurrently whenever possible, to maximize performance; to do that, use a single message with multiple tool uses 2. When the agent is done, it will return a single message back to you. The result returned by the agent is not visible to the user. To show the user the result, you should send a text message back to the user with a concise summary of the result. The output includes a task_id you can reuse later to continue the same subagent session. -3. Each agent invocation starts with a fresh context unless you provide task_id to resume the same subagent session (which continues with its previous messages and tool outputs). When starting fresh, your prompt should contain a highly detailed task description for the agent to perform autonomously and you should specify exactly what information the agent should return back to you in its final and only message to you. -4. The agent's outputs should generally be trusted -5. Clearly tell the agent whether you expect it to write code or just to do research (search, file reads, web fetches, etc.), since it is not aware of the user's intent. Tell it how to verify its work if possible (e.g., relevant test commands). -6. If the agent description mentions that it should be used proactively, then you should try your best to use it without the user having to ask for it first. Use your judgement. +3. If OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS is enabled, background=true launches the subagent asynchronously. Use task_status(task_id=..., wait=false) to poll, or wait=true to block until done. +4. Each agent invocation starts with a fresh context unless you provide task_id to resume the same subagent session (which continues with its previous messages and tool outputs). When starting fresh, your prompt should contain a highly detailed task description for the agent to perform autonomously and you should specify exactly what information the agent should return back to you in its final and only message to you. +5. The agent's outputs should generally be trusted +6. Clearly tell the agent whether you expect it to write code or just to do research (search, file reads, web fetches, etc.), since it is not aware of the user's intent. Tell it how to verify its work if possible (e.g., relevant test commands). +7. If the agent description mentions that it should be used proactively, then you should try your best to use it without the user having to ask for it first. Use your judgement. Example usage (NOTE: The agents below are fictional examples for illustration only - use the actual agents listed above): diff --git a/packages/opencode/src/tool/task_status.ts b/packages/opencode/src/tool/task_status.ts new file mode 100644 index 00000000000..00bdfc98dd8 --- /dev/null +++ b/packages/opencode/src/tool/task_status.ts @@ -0,0 +1,186 @@ +import * as Tool from "./tool" +import DESCRIPTION from "./task_status.txt" +import { BackgroundJob } from "@/background/job" +import { Session } from "@/session/session" +import { MessageV2 } from "@/session/message-v2" +import { SessionID } from "@/session/schema" +import { SessionStatus } from "@/session/status" +import { PositiveInt } from "@opencode-ai/core/schema" +import { RuntimeFlags } from "@/effect/runtime-flags" +import { Effect, Option, Schema } from "effect" + +const DEFAULT_TIMEOUT = 60_000 +const POLL_MS = 300 + +const Parameters = Schema.Struct({ + task_id: SessionID.annotate({ description: "The task_id returned by the task tool" }), + wait: Schema.optional(Schema.Boolean).annotate({ + description: "When true, wait until the task reaches a terminal state or timeout", + }), + timeout_ms: Schema.optional(PositiveInt).annotate({ + description: "Maximum milliseconds to wait when wait=true (default: 60000)", + }), +}) + +type State = BackgroundJob.Status +type InspectResult = { state: State; text: string } + +function format(input: { taskID: SessionID; state: State; text: string }) { + const tag = input.state === "completed" || input.state === "running" ? "task_result" : "task_error" + return [`task_id: ${input.taskID}`, `state: ${input.state}`, "", `<${tag}>`, input.text, ``].join("\n") +} + +function errorText(error: NonNullable) { + const data = Reflect.get(error, "data") + const message = data && typeof data === "object" ? Reflect.get(data, "message") : undefined + if (typeof message === "string" && message) return message + return error.name +} + +function inspectMessage(message: MessageV2.WithParts): InspectResult | undefined { + if (message.info.role !== "assistant") return + const text = message.parts.findLast((part) => part.type === "text")?.text ?? "" + if (message.info.error) return { state: "error", text: text || errorText(message.info.error) } + if (message.info.finish && !["tool-calls", "unknown"].includes(message.info.finish)) + return { state: "completed", text } + return { state: "running", text: text || "Task is still running." } +} + +export const TaskStatusTool = Tool.define( + "task_status", + Effect.gen(function* () { + const jobs = yield* BackgroundJob.Service + const sessions = yield* Session.Service + const status = yield* SessionStatus.Service + const flags = yield* RuntimeFlags.Service + + const inspect: (taskID: SessionID) => Effect.Effect = Effect.fn("TaskStatusTool.inspect")(function* ( + taskID: SessionID, + ) { + const job = yield* jobs.get(taskID) + if (job) { + return { + state: job.status, + text: + job.output ?? + job.error ?? + (job.status === "running" + ? "Task is still running." + : job.status === "cancelled" + ? "Task was cancelled." + : ""), + } + } + + const current = yield* status.get(taskID) + if (current.type === "busy" || current.type === "retry") { + return { + state: "running", + text: current.type === "retry" ? `Task is retrying: ${current.message}` : "Task is still running.", + } + } + + const latestAssistant = yield* sessions + .findMessage(taskID, (item) => item.info.role === "assistant") + .pipe(Effect.orDie) + if (Option.isSome(latestAssistant)) { + const latest = inspectMessage(latestAssistant.value) + if (!latest) return { state: "error", text: "Task is not running in this process." } + if (latest.state === "running") + return { state: "error", text: "Task is not running in this process and has no final output." } + return latest + } + return { state: "error", text: "Task is not running in this process and has not produced output." } + }) + + const waitForTerminal: ( + taskID: SessionID, + timeout: number, + ) => Effect.Effect<{ result: InspectResult; timedOut: boolean }> = Effect.fn("TaskStatusTool.waitForTerminal")( + function* (taskID: SessionID, timeout: number) { + const result = yield* inspect(taskID) + if (result.state !== "running") return { result, timedOut: false } + if (timeout <= 0) return { result, timedOut: true } + const sleep = Math.min(POLL_MS, timeout) + yield* Effect.sleep(`${sleep} millis`) + return yield* waitForTerminal(taskID, timeout - sleep) + }, + ) + + const run = Effect.fn("TaskStatusTool.execute")(function* ( + params: Schema.Schema.Type, + ctx: Tool.Context, + ) { + if (!flags.experimentalBackgroundSubagents) { + return yield* Effect.fail(new Error("task_status requires KILO_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true")) + } + + const session = yield* sessions.get(params.task_id).pipe(Effect.catchCause(() => Effect.succeed(undefined))) + if (!session) { + return { + title: "Task status", + metadata: { + task_id: params.task_id, + state: "error" as const, + timed_out: false, + }, + output: format({ + taskID: params.task_id, + state: "error", + text: `Task not found: ${params.task_id}`, + }), + } + } + // kilocode_change start - task status follows the same direct-child ownership boundary as task resume + if (session.parentID !== ctx.sessionID) { + return yield* Effect.fail( + new Error(`Cannot inspect task ${params.task_id}: not a child of the current session`), + ) + } + // kilocode_change end + + const waited = + params.wait === true + ? yield* jobs.wait({ id: params.task_id, timeout: params.timeout_ms ?? DEFAULT_TIMEOUT }) + : { info: yield* jobs.get(params.task_id), timedOut: false } + const inspected = waited.info + ? { + result: { + state: waited.info.status, + text: + waited.info.output ?? + waited.info.error ?? + (waited.info.status === "running" ? "Task is still running." : ""), + }, + timedOut: waited.timedOut, + } + : params.wait === true + ? yield* waitForTerminal(params.task_id, params.timeout_ms ?? DEFAULT_TIMEOUT) + : { result: yield* inspect(params.task_id), timedOut: false } + const text = inspected.timedOut + ? `Timed out after ${params.timeout_ms ?? DEFAULT_TIMEOUT}ms while waiting for task completion.` + : inspected.result.text + + return { + title: "Task status", + metadata: { + task_id: params.task_id, + state: inspected.result.state, + timed_out: inspected.timedOut, + }, + output: format({ + taskID: params.task_id, + state: inspected.result.state, + text, + }), + } + }) + + return { + description: DESCRIPTION, + parameters: Parameters, + execute: (params: Schema.Schema.Type, ctx: Tool.Context) => + run(params, ctx).pipe(Effect.orDie), + } + }), +) diff --git a/packages/opencode/src/tool/task_status.txt b/packages/opencode/src/tool/task_status.txt new file mode 100644 index 00000000000..ed6fa727b2a --- /dev/null +++ b/packages/opencode/src/tool/task_status.txt @@ -0,0 +1,13 @@ +Poll the status of a background subagent task launched with the task tool. + +Use this for tasks started with `task(background=true)`. + +Parameters: +- `task_id` (required): the task session id returned by the task tool +- `wait` (optional): when true, wait for completion +- `timeout_ms` (optional): max wait duration in milliseconds when `wait=true` + +Returns compact, parseable output: +- `task_id` +- `state` (`running`, `completed`, `error`, or `cancelled`) +- `...` or `...` containing final output, error summary, or current progress text diff --git a/packages/opencode/src/tool/tool.ts b/packages/opencode/src/tool/tool.ts index 4b9ea8774a4..a26422d04c2 100644 --- a/packages/opencode/src/tool/tool.ts +++ b/packages/opencode/src/tool/tool.ts @@ -1,4 +1,5 @@ import { Effect, Schema } from "effect" +import type { JSONSchema7 } from "@ai-sdk/provider" import type { MessageV2 } from "../session/message-v2" import type { Permission } from "../permission" import type { SessionID, MessageID } from "../session/schema" @@ -38,6 +39,7 @@ export interface Def< id: string description: string parameters: Parameters + jsonSchema?: JSONSchema7 execute(args: Schema.Schema.Type, ctx: Context): Effect.Effect> formatValidationError?(error: unknown): string } diff --git a/packages/opencode/src/tool/webfetch.ts b/packages/opencode/src/tool/webfetch.ts index 96d14900e40..ecf0de7f81b 100644 --- a/packages/opencode/src/tool/webfetch.ts +++ b/packages/opencode/src/tool/webfetch.ts @@ -1,5 +1,6 @@ import { Effect, Schema } from "effect" import { HttpClient, HttpClientRequest } from "effect/unstable/http" +import { Parser } from "htmlparser2" import * as Tool from "./tool" import TurndownService from "turndown" import DESCRIPTION from "./webfetch.txt" @@ -13,10 +14,11 @@ const MAX_TIMEOUT = 120 * 1000 // 2 minutes export const Parameters = Schema.Struct({ url: Schema.String.annotate({ description: "The URL to fetch content from" }), format: Schema.Literals(["text", "markdown", "html"]) - .pipe(Schema.optional, Schema.withDecodingDefault(Effect.succeed("markdown" as const))) .annotate({ description: "The format to return the content in (text, markdown, or html). Defaults to markdown.", - }), + default: "markdown", + }) + .pipe(Schema.optional, Schema.withDecodingDefault(Effect.succeed("markdown" as const))), timeout: Schema.optional(Schema.Number).annotate({ description: "Optional timeout in seconds (max 120)" }), }) @@ -142,8 +144,7 @@ export const WebFetchTool = Tool.define( case "text": if (contentType.includes("text/html")) { - const text = yield* Effect.promise(() => extractTextFromHTML(content)) - return { output: text, title, metadata: {} } + return { output: extractTextFromHTML(content), title, metadata: {} } } return { output: content, title, metadata: {} } @@ -158,35 +159,27 @@ export const WebFetchTool = Tool.define( }), ) -async function extractTextFromHTML(html: string) { +function extractTextFromHTML(html: string) { let text = "" - let skipContent = false - - const rewriter = new HTMLRewriter() - .on("script, style, noscript, iframe, object, embed", { - element() { - skipContent = true - }, - text() { - // Skip text content inside these elements - }, - }) - .on("*", { - element(element) { - // Reset skip flag when entering other elements - if (!["script", "style", "noscript", "iframe", "object", "embed"].includes(element.tagName)) { - skipContent = false - } - }, - text(input) { - if (!skipContent) { - text += input.text - } - }, - }) - .transform(new Response(html)) + let skipDepth = 0 + + const parser = new Parser({ + onopentag(name) { + if (skipDepth > 0 || ["script", "style", "noscript", "iframe", "object", "embed"].includes(name)) { + skipDepth++ + } + }, + ontext(input) { + if (skipDepth === 0) text += input + }, + onclosetag() { + if (skipDepth > 0) skipDepth-- + }, + }) + + parser.write(html) + parser.end() - await rewriter.text() return text.trim() } diff --git a/packages/opencode/src/tool/websearch.ts b/packages/opencode/src/tool/websearch.ts index 39c980911a2..12da7fbb3aa 100644 --- a/packages/opencode/src/tool/websearch.ts +++ b/packages/opencode/src/tool/websearch.ts @@ -3,9 +3,9 @@ import { HttpClient } from "effect/unstable/http" import * as Tool from "./tool" import * as McpWebSearch from "./mcp-websearch" import DESCRIPTION from "./websearch.txt" -import { Flag } from "@opencode-ai/core/flag/flag" import { checksum } from "@opencode-ai/core/util/encode" import { InstallationVersion } from "@opencode-ai/core/installation/version" +import { RuntimeFlags } from "@/effect/runtime-flags" export const Parameters = Schema.Struct({ query: Schema.String.annotate({ description: "Websearch query" }), @@ -27,10 +27,7 @@ export const Parameters = Schema.Struct({ const WebSearchProviderSchema = Schema.Literals(["exa", "parallel"]) export type WebSearchProvider = Schema.Schema.Type -export function selectWebSearchProvider( - sessionID: string, - flags = { exa: Flag.KILO_ENABLE_EXA, parallel: Flag.KILO_ENABLE_PARALLEL }, -): WebSearchProvider { +export function selectWebSearchProvider(sessionID: string, flags = { exa: false, parallel: false }): WebSearchProvider { const override = process.env.KILO_WEBSEARCH_PROVIDER if (override === "exa" || override === "parallel") return override if (flags.parallel) return "parallel" @@ -103,6 +100,7 @@ export const WebSearchTool = Tool.define( "websearch", Effect.gen(function* () { const http = yield* HttpClient.HttpClient + const flags = yield* RuntimeFlags.Service return { get description() { @@ -111,7 +109,10 @@ export const WebSearchTool = Tool.define( parameters: Parameters, execute: (params: Schema.Schema.Type, ctx: Tool.Context) => Effect.gen(function* () { - const provider = selectWebSearchProvider(ctx.sessionID) + const provider = selectWebSearchProvider(ctx.sessionID, { + exa: flags.enableExa, + parallel: flags.enableParallel, + }) const title = webSearchProviderLabel(provider) yield* ctx.metadata({ title: `${title} "${params.query}"`, metadata: { provider } }) diff --git a/packages/opencode/src/util/abort.ts b/packages/opencode/src/util/abort.ts deleted file mode 100644 index 3e7cfd8b28b..00000000000 --- a/packages/opencode/src/util/abort.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Creates an AbortController that automatically aborts after a timeout. - * - * Uses bind() instead of arrow functions to avoid capturing the surrounding - * scope in closures. Arrow functions like `() => controller.abort()` capture - * request bodies and other large objects, preventing GC for the timer lifetime. - * - * @param ms Timeout in milliseconds - * @returns Object with controller, signal, and clearTimeout function - */ -export function abortAfter(ms: number) { - const controller = new AbortController() - const id = setTimeout(controller.abort.bind(controller), ms) - return { - controller, - signal: controller.signal, - clearTimeout: () => globalThis.clearTimeout(id), - } -} - -/** - * Combines multiple AbortSignals with a timeout. - * - * @param ms Timeout in milliseconds - * @param signals Additional signals to combine - * @returns Combined signal that aborts on timeout or when any input signal aborts - */ -export function abortAfterAny(ms: number, ...signals: AbortSignal[]) { - const timeout = abortAfter(ms) - const signal = AbortSignal.any([timeout.signal, ...signals]) - return { - signal, - clearTimeout: timeout.clearTimeout, - } -} diff --git a/packages/opencode/src/util/color.ts b/packages/opencode/src/util/color.ts deleted file mode 100644 index 3752fd19bfd..00000000000 --- a/packages/opencode/src/util/color.ts +++ /dev/null @@ -1,19 +0,0 @@ -export function isValidHex(hex?: string): hex is string { - if (!hex) return false - return /^#[0-9a-fA-F]{6}$/.test(hex) -} - -export function hexToRgb(hex: string): { r: number; g: number; b: number } { - const r = parseInt(hex.slice(1, 3), 16) - const g = parseInt(hex.slice(3, 5), 16) - const b = parseInt(hex.slice(5, 7), 16) - return { r, g, b } -} - -export function hexToAnsiBold(hex?: string): string | undefined { - if (!isValidHex(hex)) return undefined - const { r, g, b } = hexToRgb(hex) - return `\x1b[38;2;${r};${g};${b}m\x1b[1m` -} - -export * as Color from "./color" diff --git a/packages/opencode/src/util/filesystem.ts b/packages/opencode/src/util/filesystem.ts index 8d7f9c59bf0..2e0931e780a 100644 --- a/packages/opencode/src/util/filesystem.ts +++ b/packages/opencode/src/util/filesystem.ts @@ -7,6 +7,7 @@ import { dirname, isAbsolute, join, relative, resolve as pathResolve, sep, win32 import { Readable } from "stream" import { pipeline } from "stream/promises" import { Glob } from "@opencode-ai/core/util/glob" +import { fileURLToPath } from "url" // Fast sync version for metadata checks export async function exists(p: string): Promise { @@ -149,6 +150,12 @@ export function resolve(p: string): string { } } +export function resolveFilePath(root: string, file: string): string { + const raw = file.startsWith("file://") ? fileURLToPath(file) : file + if (isAbsolute(raw)) return raw + return pathResolve(root, raw) +} + export function windowsPath(p: string): string { if (process.platform !== "win32") return p return ( diff --git a/packages/opencode/src/util/fn.ts b/packages/opencode/src/util/fn.ts deleted file mode 100644 index c75fc1bb547..00000000000 --- a/packages/opencode/src/util/fn.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { z } from "zod" - -export function fn(schema: T, cb: (input: z.infer) => Result) { - const result = (input: z.infer) => { - let parsed - try { - parsed = schema.parse(input) - } catch (e) { - console.trace("schema validation failure stack trace:") - if (e instanceof z.ZodError) { - console.error("schema validation issues:", JSON.stringify(e.issues, null, 2)) - } - throw e - } - - return cb(parsed) - } - result.force = (input: z.infer) => cb(input) - result.schema = schema - return result -} diff --git a/packages/opencode/src/util/lock.ts b/packages/opencode/src/util/lock.ts deleted file mode 100644 index 15635996ee9..00000000000 --- a/packages/opencode/src/util/lock.ts +++ /dev/null @@ -1,98 +0,0 @@ -const locks = new Map< - string, - { - readers: number - writer: boolean - waitingReaders: (() => void)[] - waitingWriters: (() => void)[] - } ->() - -function get(key: string) { - if (!locks.has(key)) { - locks.set(key, { - readers: 0, - writer: false, - waitingReaders: [], - waitingWriters: [], - }) - } - return locks.get(key)! -} - -function process(key: string) { - const lock = locks.get(key) - if (!lock || lock.writer || lock.readers > 0) return - - // Prioritize writers to prevent starvation - if (lock.waitingWriters.length > 0) { - const nextWriter = lock.waitingWriters.shift()! - nextWriter() - return - } - - // Wake up all waiting readers - while (lock.waitingReaders.length > 0) { - const nextReader = lock.waitingReaders.shift()! - nextReader() - } - - // Clean up empty locks - if (lock.readers === 0 && !lock.writer && lock.waitingReaders.length === 0 && lock.waitingWriters.length === 0) { - locks.delete(key) - } -} - -export async function read(key: string): Promise { - const lock = get(key) - - return new Promise((resolve) => { - if (!lock.writer && lock.waitingWriters.length === 0) { - lock.readers++ - resolve({ - [Symbol.dispose]: () => { - lock.readers-- - process(key) - }, - }) - } else { - lock.waitingReaders.push(() => { - lock.readers++ - resolve({ - [Symbol.dispose]: () => { - lock.readers-- - process(key) - }, - }) - }) - } - }) -} - -export async function write(key: string): Promise { - const lock = get(key) - - return new Promise((resolve) => { - if (!lock.writer && lock.readers === 0) { - lock.writer = true - resolve({ - [Symbol.dispose]: () => { - lock.writer = false - process(key) - }, - }) - } else { - lock.waitingWriters.push(() => { - lock.writer = true - resolve({ - [Symbol.dispose]: () => { - lock.writer = false - process(key) - }, - }) - }) - } - }) -} - -export * as Lock from "./lock" diff --git a/packages/opencode/src/util/media.ts b/packages/opencode/src/util/media.ts index aa5a9031261..5071a215ad9 100644 --- a/packages/opencode/src/util/media.ts +++ b/packages/opencode/src/util/media.ts @@ -15,7 +15,9 @@ export function isIconMimeType(mime: string) { } export function isImageAttachment(mime: string) { - return mime.startsWith("image/") && mime !== "image/svg+xml" && mime !== "image/vnd.fastbidsheet" && !isIconMimeType(mime) + return ( + mime.startsWith("image/") && mime !== "image/svg+xml" && mime !== "image/vnd.fastbidsheet" && !isIconMimeType(mime) + ) } // kilocode_change end diff --git a/packages/opencode/src/util/named-schema-error.ts b/packages/opencode/src/util/named-schema-error.ts deleted file mode 100644 index d87e1dcdb55..00000000000 --- a/packages/opencode/src/util/named-schema-error.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Schema } from "effect" -import z from "zod" -import { zod } from "@opencode-ai/core/effect-zod" - -/** - * Create a Schema-backed NamedError-shaped class. - * - * Drop-in replacement for `NamedError.create(tag, zodShape)` but backed by - * `Schema.Struct` under the hood. The wire shape emitted by the derived - * `.Schema` is still `{ name: tag, data: {...fields} }` so the generated - * OpenAPI/SDK output is byte-identical to the original NamedError schema. - * - * Preserves the existing surface: - * - static `Schema` (Zod schema of the wire shape) - * - static `isInstance(x)` - * - instance `toObject()` returning `{ name, data }` - * - `new X({ ...data }, { cause })` - */ -export function namedSchemaError(tag: Tag, fields: Fields) { - // Wire shape matches the original NamedError output so the SDK stays stable. - const dataSchema = Schema.Struct(fields) - const wire = z - .object({ - name: z.literal(tag), - data: zod(dataSchema), - }) - .meta({ ref: tag }) - - // Effect Schema for the wire shape — used by HttpApi OpenAPI generation. - const effectSchema = Schema.Struct({ - name: Schema.Literal(tag), - data: dataSchema, - }).annotate({ identifier: tag }) - - type Data = Schema.Schema.Type - - class NamedSchemaError extends Error { - static readonly Schema = wire - static readonly EffectSchema = effectSchema - static readonly tag = tag - public static isInstance(input: unknown): input is NamedSchemaError { - return typeof input === "object" && input !== null && "name" in input && (input as { name: unknown }).name === tag - } - - public override readonly name: Tag = tag - public readonly data: Data - - constructor(data: Data, options?: ErrorOptions) { - super(tag, options) - this.data = data - } - - toObject(): { name: Tag; data: Data } { - return { name: tag, data: this.data } - } - } - - Object.defineProperty(NamedSchemaError, "name", { value: tag }) - - return NamedSchemaError -} diff --git a/packages/opencode/src/util/network.ts b/packages/opencode/src/util/network.ts deleted file mode 100644 index 69e5d17588a..00000000000 --- a/packages/opencode/src/util/network.ts +++ /dev/null @@ -1,9 +0,0 @@ -export function online() { - const nav = globalThis.navigator - if (!nav || typeof nav.onLine !== "boolean") return true - return nav.onLine -} - -export function proxied() { - return !!(process.env.HTTP_PROXY || process.env.HTTPS_PROXY || process.env.http_proxy || process.env.https_proxy) -} diff --git a/packages/opencode/src/util/scrap.ts b/packages/opencode/src/util/scrap.ts deleted file mode 100644 index 554dba1c54a..00000000000 --- a/packages/opencode/src/util/scrap.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const foo: string = "42" -export const bar: number = 123 - -export function dummyFunction(): void { - console.log("This is a dummy function") -} - -export function randomHelper(): boolean { - return Math.random() > 0.5 -} diff --git a/packages/opencode/src/util/update-schema.ts b/packages/opencode/src/util/update-schema.ts deleted file mode 100644 index f2246ece331..00000000000 --- a/packages/opencode/src/util/update-schema.ts +++ /dev/null @@ -1,13 +0,0 @@ -import z from "zod" - -export function updateSchema(schema: z.ZodObject) { - const next = {} as { - [K in keyof T]: z.ZodOptional> - } - - for (const [k, v] of Object.entries(schema.required().shape) as [keyof T & string, z.ZodTypeAny][]) { - next[k] = v.nullable() as unknown as (typeof next)[typeof k] - } - - return z.object(next) -} diff --git a/packages/opencode/src/v2/model.ts b/packages/opencode/src/v2/model.ts deleted file mode 100644 index 56357ab4006..00000000000 --- a/packages/opencode/src/v2/model.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { withStatics } from "@opencode-ai/core/schema" -import { ModelStatus } from "@/provider/model-status" -import { Array, Context, Effect, HashMap, Layer, Option, Order, pipe, Schema } from "effect" -import { DateTimeUtcFromMillis } from "effect/Schema" - -export const ID = Schema.String.pipe(Schema.brand("Model.ID")) -export type ID = typeof ID.Type - -export const ProviderID = Schema.String.pipe( - Schema.brand("Model.ProviderID"), - withStatics((schema) => ({ - // Well-known providers - opencode: schema.make("opencode"), - anthropic: schema.make("anthropic"), - openai: schema.make("openai"), - google: schema.make("google"), - googleVertex: schema.make("google-vertex"), - githubCopilot: schema.make("github-copilot"), - amazonBedrock: schema.make("amazon-bedrock"), - azure: schema.make("azure"), - openrouter: schema.make("openrouter"), - mistral: schema.make("mistral"), - gitlab: schema.make("gitlab"), - })), -) -export type ProviderID = typeof ProviderID.Type - -export const VariantID = Schema.String.pipe(Schema.brand("VariantID")) -export type VariantID = typeof VariantID.Type - -// Grouping of models, eg claude opus, claude sonnet -export const Family = Schema.String.pipe(Schema.brand("Family")) -export type Family = typeof Family.Type - -const OpenAIResponses = Schema.Struct({ - type: Schema.Literal("openai/responses"), - url: Schema.String, - websocket: Schema.optional(Schema.Boolean), -}) - -const OpenAICompletions = Schema.Struct({ - type: Schema.Literal("openai/completions"), - url: Schema.String, - reasoning: Schema.Union([ - Schema.Struct({ - type: Schema.Literal("reasoning_content"), - }), - Schema.Struct({ - type: Schema.Literal("reasoning_details"), - }), - ]).pipe(Schema.optional), -}) -export type OpenAICompletions = typeof OpenAICompletions.Type - -const AnthropicMessages = Schema.Struct({ - type: Schema.Literal("anthropic/messages"), - url: Schema.String, -}) - -export const Endpoint = Schema.Union([OpenAIResponses, OpenAICompletions, AnthropicMessages]).pipe( - Schema.toTaggedUnion("type"), -) -export type Endpoint = typeof Endpoint.Type - -export const Capabilities = Schema.Struct({ - tools: Schema.Boolean, - // mime patterns, image, audio, video/*, text/* - input: Schema.String.pipe(Schema.Array), - output: Schema.String.pipe(Schema.Array), -}) -export type Capabilities = typeof Capabilities.Type - -export const Options = Schema.Struct({ - headers: Schema.Record(Schema.String, Schema.String), - body: Schema.Record(Schema.String, Schema.Any), -}) -export type Options = typeof Options.Type - -export const Cost = Schema.Struct({ - tier: Schema.Struct({ - type: Schema.Literal("context"), - size: Schema.Int, - }).pipe(Schema.optional), - input: Schema.Finite, - output: Schema.Finite, - cache: Schema.Struct({ - read: Schema.Finite, - write: Schema.Finite, - }), -}) - -export const Ref = Schema.Struct({ - id: ID, - providerID: ProviderID, - variant: VariantID, -}) -export type Ref = typeof Ref.Type - -export class Info extends Schema.Class("Model.Info")({ - id: ID, - providerID: ProviderID, - family: Family.pipe(Schema.optional), - name: Schema.String, - endpoint: Endpoint, - capabilities: Capabilities, - options: Schema.Struct({ - ...Options.fields, - variant: Schema.String.pipe(Schema.optional), - }), - variants: Schema.Struct({ - id: VariantID, - ...Options.fields, - }).pipe(Schema.Array), - time: Schema.Struct({ - released: DateTimeUtcFromMillis, - }), - cost: Cost.pipe(Schema.Array), - status: ModelStatus, - limit: Schema.Struct({ - context: Schema.Int, - input: Schema.Int.pipe(Schema.optional), - output: Schema.Int, - }), -}) {} - -export function parse(input: string): { providerID: ProviderID; modelID: ID } { - const [providerID, ...modelID] = input.split("/") - return { - providerID: ProviderID.make(providerID), - modelID: ID.make(modelID.join("/")), - } -} - -export interface Interface { - readonly get: (providerID: ProviderID, modelID: ID) => Effect.Effect> - readonly add: (model: Info) => Effect.Effect - readonly remove: (providerID: ProviderID, modelID: ID) => Effect.Effect - readonly all: () => Effect.Effect - readonly default: () => Effect.Effect> - readonly small: (provider: ProviderID) => Effect.Effect> -} - -export class Service extends Context.Service()("@opencode/v2/Model") {} - -export const layer = Layer.effect( - Service, - Effect.gen(function* () { - let models = HashMap.empty() - - function key(providerID: ProviderID, modelID: ID) { - return `${providerID}/${modelID}` - } - - const result: Interface = { - get: Effect.fn("V2Model.get")(function* (providerID, modelID) { - return HashMap.get(models, key(providerID, modelID)) - }), - - add: Effect.fn("V2Model.add")(function* (model) { - models = HashMap.set(models, key(model.providerID, model.id), model) - }), - - remove: Effect.fn("V2Model.remove")(function* (providerID, modelID) { - models = HashMap.remove(models, key(providerID, modelID)) - }), - - all: Effect.fn("V2Model.all")(function* () { - return pipe( - models, - HashMap.toValues, - Array.sortWith((item) => item.time.released.epochMilliseconds, Order.flip(Order.Number)), - ) - }), - - default: Effect.fn("V2Model.default")(function* () { - const all = yield* result.all() - return Option.fromUndefinedOr(all[0]) - }), - - small: Effect.fn("V2Model.small")(function* (providerID) { - const all = yield* result.all() - const match = all.find((model) => model.providerID === providerID && model.id.toLowerCase().includes("small")) - return Option.fromUndefinedOr(match) - }), - } - - return Service.of(result) - }), -) - -export const defaultLayer = layer - -export * as Modelv2 from "./model" diff --git a/packages/opencode/src/v2/provider-parity-checklist.md b/packages/opencode/src/v2/provider-parity-checklist.md new file mode 100644 index 00000000000..e3a599d8ec3 --- /dev/null +++ b/packages/opencode/src/v2/provider-parity-checklist.md @@ -0,0 +1,95 @@ +# Unported Provider Logic Checklist + +This tracks legacy provider behavior from `packages/opencode/src/provider/provider.ts` that still needs to be ported into the v2 provider plugins under `packages/opencode/src/v2/plugin/provider/`. Keep entries checked only when v2 has equivalent behavior or when the item is intentionally skipped. + +## Provider Setup + +- [x] Cloudflare AI Gateway custom SDK construction with `createAiGateway` / `createUnified`. +- [x] Google Vertex authenticated `fetch` injection. +- [x] Amazon Bedrock AWS credential chain setup. +- [x] Amazon Bedrock bearer token setup. +- [x] SAP AI Core service key setup. + +## Provider Options + +- [x] Azure resource name resolution. +- [x] Azure missing-resource error. +- [x] Azure Cognitive Services baseURL resolution. +- [x] Cloudflare Workers AI account ID validation. +- [x] Cloudflare Workers AI account ID vars. +- [x] Cloudflare AI Gateway account ID validation. +- [x] Cloudflare AI Gateway gateway ID validation. +- [x] Cloudflare AI Gateway token validation. +- [x] Amazon Bedrock region precedence. +- [x] Amazon Bedrock profile precedence. +- [x] Amazon Bedrock endpoint precedence. +- [x] Google Vertex project resolution. +- [x] Google Vertex location resolution. +- [x] GitLab instance URL resolution. +- [x] GitLab token resolution. +- [x] GitLab AI gateway headers. +- [x] GitLab feature flags. +- [x] Opencode unauthenticated paid-model filtering. +- [x] Opencode public API key fallback. + +## Request Behavior + +- [x] Request timeout handling. +- [x] Chunk timeout handling. +- [x] SSE timeout wrapping. +- [x] OpenAI response item ID stripping. +- [x] Azure response item ID stripping. +- [x] OpenAI-compatible `includeUsage` defaulting. + +## Dynamic Models + +- [ ] GitLab workflow model discovery. + +## Model Filtering + +- [ ] Experimental alpha model filtering. +- [ ] Deprecated model filtering. +- [ ] Config whitelist filtering. +- [ ] Config blacklist filtering. +- [ ] `gpt-5-chat-latest` filtering. +- [ ] OpenRouter `openai/gpt-5-chat` filtering. + +## Default Models + +- [x] Configured default model selection. Replaced by explicit `Catalog.model.setDefault`. +- [SKIP] Recent-history default model selection — not porting to server-side v2 catalog. +- [x] Default model fallback sorting. Uses newest available model, not legacy hard-coded priority. + +## Small Models + +- [SKIP] Configured `small_model` selection — not porting config-driven selection to server-side v2 catalog. +- [x] Provider-specific small model priority. Replaced by cheapest output cost selection. +- [x] Opencode small model priority. Replaced by cheapest output cost selection. +- [x] GitHub Copilot small model priority. Replaced by cheapest output cost selection. +- [x] Amazon Bedrock region-aware small model selection. Replaced by cheapest output cost selection. + +## URL And Env Vars + +- [SKIP] BaseURL `${VAR}` interpolation — not porting generic URL templating; provider plugins should construct concrete URLs. +- [x] Azure `AZURE_RESOURCE_NAME` vars. Handled by Azure provider plugins. +- [x] Google Vertex vars. Handled by Google Vertex provider plugins. +- [x] Cloudflare Workers AI vars. Handled by Cloudflare Workers AI provider plugin. + +## Auth + +- [ ] Auth-derived provider API keys. +- [ ] OpenAI OAuth/API auth distinction. +- [ ] GitLab OAuth token selection. +- [ ] GitLab API token selection. +- [ ] Azure auth metadata resource name. +- [ ] Cloudflare auth metadata account ID. +- [ ] Cloudflare auth metadata gateway ID. + +## Config And Plugin Parity + +- [ ] Legacy plugin auth loader behavior. +- [ ] Config provider merge behavior. +- [ ] Config model merge behavior. +- [ ] Variant generation from model metadata. +- [ ] Config variant merge behavior. +- [ ] Config variant disable behavior. diff --git a/packages/opencode/src/v2/session-event.ts b/packages/opencode/src/v2/session-event.ts index fa211bd8c4e..1fd0f909d5a 100644 --- a/packages/opencode/src/v2/session-event.ts +++ b/packages/opencode/src/v2/session-event.ts @@ -1,12 +1,12 @@ import { SessionID } from "@/session/schema" import { NonNegativeInt } from "@opencode-ai/core/schema" import { EventV2 } from "./event" -import { FileAttachment, Prompt } from "./session-prompt" +import { FileAttachment, Prompt } from "@opencode-ai/core/session-prompt" import { Schema } from "effect" export { FileAttachment } -import { ToolOutput } from "./tool-output" -import { V2Schema } from "./schema" -import { Modelv2 } from "./model" +import { ToolOutput } from "@opencode-ai/core/tool-output" +import { V2Schema } from "@opencode-ai/core/v2-schema" +import { ModelV2 } from "@opencode-ai/core/model" export const Source = Schema.Struct({ start: NonNegativeInt, @@ -47,7 +47,7 @@ export const ModelSwitched = EventV2.define({ version: 1, schema: { ...Base, - model: Modelv2.Ref, + model: ModelV2.Ref, }, }) export type ModelSwitched = Schema.Schema.Type @@ -104,7 +104,7 @@ export namespace Step { schema: { ...Base, agent: Schema.String, - model: Modelv2.Ref, + model: ModelV2.Ref, snapshot: Schema.String.pipe(Schema.optional), }, }) diff --git a/packages/opencode/src/v2/session-message-updater.ts b/packages/opencode/src/v2/session-message-updater.ts index 80ecb1011eb..bbdf59c555d 100644 --- a/packages/opencode/src/v2/session-message-updater.ts +++ b/packages/opencode/src/v2/session-message-updater.ts @@ -123,6 +123,7 @@ export function update(adapter: Adapter, event: SessionEvent.Eve text: event.data.prompt.text, files: event.data.prompt.files, agents: event.data.prompt.agents, + references: event.data.prompt.references, time: { created: event.data.timestamp }, }), ) diff --git a/packages/opencode/src/v2/session-message.ts b/packages/opencode/src/v2/session-message.ts index 024e28c4504..fa7c299ae57 100644 --- a/packages/opencode/src/v2/session-message.ts +++ b/packages/opencode/src/v2/session-message.ts @@ -1,10 +1,10 @@ import { Schema } from "effect" -import { Prompt } from "./session-prompt" +import { Prompt } from "@opencode-ai/core/session-prompt" import { SessionEvent } from "./session-event" import { EventV2 } from "./event" -import { ToolOutput } from "./tool-output" -import { V2Schema } from "./schema" -import { Modelv2 } from "./model" +import { ToolOutput } from "@opencode-ai/core/tool-output" +import { V2Schema } from "@opencode-ai/core/v2-schema" +import { ModelV2 } from "@opencode-ai/core/model" export const ID = EventV2.ID export type ID = Schema.Schema.Type @@ -26,7 +26,7 @@ export class AgentSwitched extends Schema.Class("Session.Message. export class ModelSwitched extends Schema.Class("Session.Message.ModelSwitched")({ ...Base, type: Schema.Literal("model-switched"), - model: Modelv2.Ref, + model: ModelV2.Ref, }) {} export class User extends Schema.Class("Session.Message.User")({ @@ -34,6 +34,7 @@ export class User extends Schema.Class("Session.Message.User")({ text: Prompt.fields.text, files: Prompt.fields.files, agents: Prompt.fields.agents, + references: Prompt.fields.references, type: Schema.Literal("user"), time: Schema.Struct({ created: V2Schema.DateTimeUtcFromMillis, diff --git a/packages/opencode/src/v2/session.ts b/packages/opencode/src/v2/session.ts index 3b0b61dcbc6..97c31d39b22 100644 --- a/packages/opencode/src/v2/session.ts +++ b/packages/opencode/src/v2/session.ts @@ -5,14 +5,15 @@ import { and, asc, desc, eq, gt, gte, isNull, like, lt, or, type SQL } from "@/s import * as Database from "@/storage/db" import { Context, DateTime, Effect, Layer, Option, Schema } from "effect" import { SessionMessage } from "./session-message" -import type { Prompt } from "./session-prompt" +import type { Prompt } from "@opencode-ai/core/session-prompt" import { EventV2 } from "./event" import { ProjectID } from "@/project/schema" import { SessionEvent } from "./session-event" -import { V2Schema } from "./schema" +import { V2Schema } from "@opencode-ai/core/v2-schema" import { optionalOmitUndefined } from "@opencode-ai/core/schema" -import { Modelv2 } from "./model" import { SyncEvent } from "@/sync" +import { ModelV2 } from "@opencode-ai/core/model" +import { ProviderV2 } from "@opencode-ai/core/provider" export const Delivery = Schema.Literals(["immediate", "deferred"]).annotate({ identifier: "Session.Delivery", @@ -28,7 +29,17 @@ export class Info extends Schema.Class("Session.Info")({ workspaceID: optionalOmitUndefined(WorkspaceID), path: optionalOmitUndefined(Schema.String), agent: optionalOmitUndefined(Schema.String), - model: Modelv2.Ref.pipe(optionalOmitUndefined), + model: ModelV2.Ref.pipe(optionalOmitUndefined), + cost: Schema.Finite, + tokens: Schema.Struct({ + input: Schema.Finite, + output: Schema.Finite, + reasoning: Schema.Finite, + cache: Schema.Struct({ + read: Schema.Finite, + write: Schema.Finite, + }), + }), time: Schema.Struct({ created: V2Schema.DateTimeUtcFromMillis, updated: V2Schema.DateTimeUtcFromMillis, @@ -57,7 +68,7 @@ export class NotFoundError extends Schema.TaggedErrorClass()("Ses export interface Interface { readonly create: (input?: { agent?: string - model?: Modelv2.Ref + model?: ModelV2.Ref parentID?: SessionID workspaceID?: WorkspaceID }) => Effect.Effect @@ -101,10 +112,10 @@ export interface Interface { parentID: SessionID prompt: Prompt agent: string - model?: Modelv2.Ref + model?: ModelV2.Ref }) => Effect.Effect readonly switchAgent: (input: { sessionID: SessionID; agent: string }) => Effect.Effect - readonly switchModel: (input: { sessionID: SessionID; model: Modelv2.Ref }) => Effect.Effect + readonly switchModel: (input: { sessionID: SessionID; model: ModelV2.Ref }) => Effect.Effect readonly compact: (sessionID: SessionID) => Effect.Effect readonly wait: (sessionID: SessionID) => Effect.Effect } @@ -131,11 +142,21 @@ export const layer = Layer.effect( agent: row.agent ?? undefined, model: row.model ? { - id: Modelv2.ID.make(row.model.id), - providerID: Modelv2.ProviderID.make(row.model.providerID), - variant: Modelv2.VariantID.make(row.model.variant ?? "default"), + id: ModelV2.ID.make(row.model.id), + providerID: ProviderV2.ID.make(row.model.providerID), + variant: ModelV2.VariantID.make(row.model.variant ?? "default"), } : undefined, + cost: row.cost, + tokens: { + input: row.tokens_input, + output: row.tokens_output, + reasoning: row.tokens_reasoning, + cache: { + read: row.tokens_cache_read, + write: row.tokens_cache_write, + }, + }, time: { created: DateTime.makeUnsafe(row.time_created), updated: DateTime.makeUnsafe(row.time_updated), @@ -144,7 +165,7 @@ export const layer = Layer.effect( }) } - const result: Interface = { + const result = Service.of({ create: Effect.fn("V2Session.create")(function* (_input) { return {} as any }), @@ -286,7 +307,7 @@ export const layer = Layer.effect( }), subagent: Effect.fn("V2Session.subagent")(function* (input) { const parent = yield* result.get(input.parentID) - const session = yield* result.create({ + const child = yield* result.create({ agent: input.agent, model: input.model, parentID: input.parentID, @@ -294,11 +315,11 @@ export const layer = Layer.effect( }) yield* result.prompt({ prompt: input.prompt, - sessionID: session.id, + sessionID: child.id, }) yield* Effect.gen(function* () { - yield* result.wait(session.id) - const messages = yield* result.messages({ sessionID: session.id, order: "desc" }) + yield* result.wait(child.id) + const messages = yield* result.messages({ sessionID: child.id, order: "desc" }) const assistant = messages.find((msg) => msg.type === "assistant") if (!assistant) return const text = assistant.content.findLast((part) => part.type === "text") @@ -307,9 +328,9 @@ export const layer = Layer.effect( }), compact: Effect.fn("V2Session.compact")(function* (_sessionID) {}), wait: Effect.fn("V2Session.wait")(function* (_sessionID) {}), - } + }) - return Service.of(result) + return result }), ) diff --git a/packages/opencode/src/worktree/index.ts b/packages/opencode/src/worktree/index.ts index b8b2833f97b..58922a3167d 100644 --- a/packages/opencode/src/worktree/index.ts +++ b/packages/opencode/src/worktree/index.ts @@ -1,5 +1,3 @@ -import z from "zod" -import { NamedError } from "@opencode-ai/core/util/error" import { Global } from "@opencode-ai/core/global" import { InstanceLayer } from "@/project/instance-layer" import { InstanceStore } from "@/project/instance-store" @@ -14,12 +12,12 @@ import { errorMessage } from "../util/error" import { BusEvent } from "@/bus/bus-event" import { GlobalBus } from "@/bus/global" import { Git } from "@/git" -import { Effect, Layer, Path, Schema, Scope, Context, Stream } from "effect" -import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process" +import { Effect, Layer, Path, Schema, Scope, Context } from "effect" +import { ChildProcess } from "effect/unstable/process" import { NodePath } from "@effect/platform-node" import { AppFileSystem } from "@opencode-ai/core/filesystem" import { BootstrapRuntime } from "@/effect/bootstrap-runtime" -import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" +import { AppProcess } from "@opencode-ai/core/process" import { InstanceState } from "@/effect/instance-state" import { WorktreeCleanup } from "@/kilocode/worktree-cleanup" // kilocode_change @@ -30,7 +28,7 @@ export const Event = { "worktree.ready", Schema.Struct({ name: Schema.String, - branch: Schema.String, + branch: Schema.optional(Schema.String), }), ), Failed: BusEvent.define( @@ -43,7 +41,7 @@ export const Event = { export const Info = Schema.Struct({ name: Schema.String, - branch: Schema.String, + branch: Schema.optional(Schema.String), directory: Schema.String, }).annotate({ identifier: "Worktree" }) export type Info = Schema.Schema.Type @@ -66,54 +64,48 @@ export const ResetInput = Schema.Struct({ }).annotate({ identifier: "WorktreeResetInput" }) export type ResetInput = Schema.Schema.Type -export const NotGitError = NamedError.create( - "WorktreeNotGitError", - z.object({ - message: z.string(), - }), -) +export class NotGitError extends Schema.TaggedErrorClass()("WorktreeNotGitError", { + message: Schema.String, +}) {} -export const NameGenerationFailedError = NamedError.create( +export class NameGenerationFailedError extends Schema.TaggedErrorClass()( "WorktreeNameGenerationFailedError", - z.object({ - message: z.string(), - }), -) + { + message: Schema.String, + }, +) {} -export const CreateFailedError = NamedError.create( - "WorktreeCreateFailedError", - z.object({ - message: z.string(), - }), -) +export class CreateFailedError extends Schema.TaggedErrorClass()("WorktreeCreateFailedError", { + message: Schema.String, +}) {} -export const StartCommandFailedError = NamedError.create( +export class StartCommandFailedError extends Schema.TaggedErrorClass()( "WorktreeStartCommandFailedError", - z.object({ - message: z.string(), - }), -) - -export const RemoveFailedError = NamedError.create( - "WorktreeRemoveFailedError", - z.object({ - message: z.string(), - }), -) - -export const ResetFailedError = NamedError.create( - "WorktreeResetFailedError", - z.object({ - message: z.string(), - }), -) - -export const ListFailedError = NamedError.create( - "WorktreeListFailedError", - z.object({ - message: z.string(), - }), -) + { + message: Schema.String, + }, +) {} + +export class RemoveFailedError extends Schema.TaggedErrorClass()("WorktreeRemoveFailedError", { + message: Schema.String, +}) {} + +export class ResetFailedError extends Schema.TaggedErrorClass()("WorktreeResetFailedError", { + message: Schema.String, +}) {} + +export class ListFailedError extends Schema.TaggedErrorClass()("WorktreeListFailedError", { + message: Schema.String, +}) {} + +export type Error = + | NotGitError + | NameGenerationFailedError + | CreateFailedError + | StartCommandFailedError + | RemoveFailedError + | ResetFailedError + | ListFailedError function slugify(input: string) { return input @@ -144,12 +136,12 @@ function failedRemoves(...chunks: string[]) { // --------------------------------------------------------------------------- export interface Interface { - readonly makeWorktreeInfo: (name?: string) => Effect.Effect - readonly createFromInfo: (info: Info, startCommand?: string) => Effect.Effect - readonly create: (input?: CreateInput) => Effect.Effect - readonly list: () => Effect.Effect<(Omit & { branch?: string })[]> - readonly remove: (input: RemoveInput) => Effect.Effect - readonly reset: (input: ResetInput) => Effect.Effect + readonly makeWorktreeInfo: (options?: { name?: string; detached?: boolean }) => Effect.Effect + readonly createFromInfo: (info: Info, startCommand?: string) => Effect.Effect + readonly create: (input?: CreateInput) => Effect.Effect + readonly list: () => Effect.Effect<(Omit & { branch?: string })[], Error> + readonly remove: (input: RemoveInput) => Effect.Effect + readonly reset: (input: ResetInput) => Effect.Effect } export class Service extends Context.Service()("@opencode/Worktree") {} @@ -159,80 +151,90 @@ type GitResult = { code: number; text: string; stderr: string } export const layer: Layer.Layer< Service, never, - | AppFileSystem.Service - | Path.Path - | ChildProcessSpawner.ChildProcessSpawner - | Git.Service - | Project.Service - | InstanceStore.Service + AppFileSystem.Service | Path.Path | AppProcess.Service | Git.Service | Project.Service | InstanceStore.Service > = Layer.effect( Service, Effect.gen(function* () { const scope = yield* Scope.Scope const fs = yield* AppFileSystem.Service const pathSvc = yield* Path.Path - const spawner = yield* ChildProcessSpawner.ChildProcessSpawner + const appProcess = yield* AppProcess.Service const gitSvc = yield* Git.Service const project = yield* Project.Service const store = yield* InstanceStore.Service const git = Effect.fnUntraced( function* (args: string[], opts?: { cwd?: string }) { - const handle = yield* spawner.spawn( + const result = yield* appProcess.run( ChildProcess.make("git", args, { cwd: opts?.cwd, extendEnv: true, stdin: "ignore" }), ) - const [text, stderr] = yield* Effect.all( - [Stream.mkString(Stream.decodeText(handle.stdout)), Stream.mkString(Stream.decodeText(handle.stderr))], - { concurrency: 2 }, - ) - const code = yield* handle.exitCode - return { code, text, stderr } satisfies GitResult + return { + code: result.exitCode, + text: result.stdout.toString("utf8"), + stderr: result.stderr.toString("utf8"), + } satisfies GitResult }, - Effect.scoped, Effect.catch((e) => - Effect.succeed({ code: 1, text: "", stderr: e instanceof Error ? e.message : String(e) } satisfies GitResult), + Effect.succeed({ + code: 1, + text: "", + stderr: e instanceof Error ? e.message : String(e), + } satisfies GitResult), ), ) const MAX_NAME_ATTEMPTS = 26 - const candidate = Effect.fn("Worktree.candidate")(function* (root: string, base?: string) { + const candidate = Effect.fn("Worktree.candidate")(function* (input: { + root: string + name?: string + detached?: boolean + }) { const ctx = yield* InstanceState.context for (const attempt of Array.from({ length: MAX_NAME_ATTEMPTS }, (_, i) => i)) { - const name = base ? (attempt === 0 ? base : `${base}-${Slug.create()}`) : Slug.create() - const branch = `opencode/${name}` - const directory = pathSvc.join(root, name) + const name = input.name ? (attempt === 0 ? input.name : `${input.name}-${Slug.create()}`) : Slug.create() + const branch = input.detached ? undefined : `opencode/${name}` + const directory = pathSvc.join(input.root, name) if (yield* fs.exists(directory).pipe(Effect.orDie)) continue - const ref = `refs/heads/${branch}` - const branchCheck = yield* git(["show-ref", "--verify", "--quiet", ref], { cwd: ctx.worktree }) - if (branchCheck.code === 0) continue + if (branch) { + const ref = `refs/heads/${branch}` + const branchCheck = yield* git(["show-ref", "--verify", "--quiet", ref], { cwd: ctx.worktree }) + if (branchCheck.code === 0) continue + } - return { name, branch, directory } + return { name, directory, ...(branch ? { branch } : {}) } } - throw new NameGenerationFailedError({ message: "Failed to generate a unique worktree name" }) + return yield* new NameGenerationFailedError({ message: "Failed to generate a unique worktree name" }) }) - const makeWorktreeInfo = Effect.fn("Worktree.makeWorktreeInfo")(function* (name?: string) { + const makeWorktreeInfo = Effect.fn("Worktree.makeWorktreeInfo")(function* (input?: { + name?: string + detached?: boolean + }) { const ctx = yield* InstanceState.context if (ctx.project.vcs !== "git") { - throw new NotGitError({ message: "Worktrees are only supported for git projects" }) + return yield* new NotGitError({ message: "Worktrees are only supported for git projects" }) } const root = pathSvc.join(Global.Path.data, "worktree", ctx.project.id) yield* fs.makeDirectory(root, { recursive: true }).pipe(Effect.orDie) - const base = name ? slugify(name) : "" - return yield* candidate(root, base || undefined) + return yield* candidate({ root, name: input?.name ? slugify(input.name) : "", detached: input?.detached }) }) const setup = Effect.fnUntraced(function* (info: Info) { const ctx = yield* InstanceState.context - const created = yield* git(["worktree", "add", "--no-checkout", "-b", info.branch, info.directory], { - cwd: ctx.worktree, - }) + const created = yield* git( + info.branch + ? ["worktree", "add", "--no-checkout", "-b", info.branch, info.directory] + : ["worktree", "add", "--no-checkout", "--detach", info.directory, "HEAD"], + { cwd: ctx.worktree }, + ) if (created.code !== 0) { - throw new CreateFailedError({ message: created.stderr || created.text || "Failed to create git worktree" }) + return yield* new CreateFailedError({ + message: created.stderr || created.text || "Failed to create git worktree", + }) } yield* project.addSandbox(ctx.project.id, info.directory).pipe(Effect.catch(() => Effect.void)) @@ -281,7 +283,7 @@ export const layer: Layer.Layer< workspace: workspaceID, payload: { type: Event.Ready.type, - properties: { name: info.name, branch: info.branch }, + properties: { name: info.name, ...(info.branch ? { branch: info.branch } : {}) }, }, }) @@ -297,7 +299,7 @@ export const layer: Layer.Layer< }) const create = Effect.fn("Worktree.create")(function* (input?: CreateInput) { - const info = yield* makeWorktreeInfo(input?.name) + const info = yield* makeWorktreeInfo({ name: input?.name }) yield* createFromInfo(info, input?.startCommand) return info }) @@ -348,7 +350,7 @@ export const layer: Layer.Layer< const result = yield* git(["worktree", "list", "--porcelain"], { cwd: ctx.worktree }) if (result.code !== 0) { - throw new ListFailedError({ message: result.stderr || result.text || "Failed to read git worktrees" }) + return yield* new ListFailedError({ message: result.stderr || result.text || "Failed to read git worktrees" }) } const primary = yield* canonical(ctx.worktree) @@ -377,28 +379,25 @@ export const layer: Layer.Layer< // kilocode_change start - use Kilo cleanup helper for slow Windows handle release function cleanDirectory(target: string) { - return Effect.promise(() => WorktreeCleanup.removeDirectory(target)).pipe( - Effect.catch((error) => - Effect.sync(() => { - const message = errorMessage(error) - throw new RemoveFailedError({ message: message || "Failed to remove git worktree directory" }) - }), - ), - ) + return Effect.tryPromise({ + try: () => WorktreeCleanup.removeDirectory(target), + catch: (error) => + new RemoveFailedError({ message: errorMessage(error) || "Failed to remove git worktree directory" }), + }) } // kilocode_change end const remove = Effect.fn("Worktree.remove")(function* (input: RemoveInput) { const ctx = yield* InstanceState.context if (ctx.project.vcs !== "git") { - throw new NotGitError({ message: "Worktrees are only supported for git projects" }) + return yield* new NotGitError({ message: "Worktrees are only supported for git projects" }) } const directory = yield* canonical(input.directory) const list = yield* git(["worktree", "list", "--porcelain"], { cwd: ctx.worktree }) if (list.code !== 0) { - throw new RemoveFailedError({ message: list.stderr || list.text || "Failed to read git worktrees" }) + return yield* new RemoveFailedError({ message: list.stderr || list.text || "Failed to read git worktrees" }) } const entries = parseWorktreeList(list.text) @@ -422,14 +421,16 @@ export const layer: Layer.Layer< if (removed.code !== 0) { const next = yield* git(["worktree", "list", "--porcelain"], { cwd: ctx.worktree }) if (next.code !== 0) { - throw new RemoveFailedError({ + return yield* new RemoveFailedError({ message: removed.stderr || removed.text || next.stderr || next.text || "Failed to remove git worktree", }) } const stale = yield* locateWorktree(parseWorktreeList(next.text), directory) if (stale?.path) { - throw new RemoveFailedError({ message: removed.stderr || removed.text || "Failed to remove git worktree" }) + return yield* new RemoveFailedError({ + message: removed.stderr || removed.text || "Failed to remove git worktree", + }) } } @@ -439,7 +440,7 @@ export const layer: Layer.Layer< if (branch) { const deleted = yield* git(["branch", "-D", branch], { cwd: ctx.worktree }) if (deleted.code !== 0) { - throw new RemoveFailedError({ + return yield* new RemoveFailedError({ message: deleted.stderr || deleted.text || "Failed to delete worktree branch", }) } @@ -454,25 +455,18 @@ export const layer: Layer.Layer< error: (r: GitResult) => Error, ) { const result = yield* git(args, opts) - if (result.code !== 0) throw error(result) + if (result.code !== 0) return yield* error(result) return result }) const runStartCommand = Effect.fnUntraced( function* (directory: string, cmd: string) { const [shell, args] = process.platform === "win32" ? ["cmd", ["/c", cmd]] : ["bash", ["-lc", cmd]] - const handle = yield* spawner.spawn( - ChildProcess.make(shell, args, { cwd: directory, extendEnv: true, stdin: "ignore" }), + const result = yield* appProcess.run( + ChildProcess.make(shell, args as string[], { cwd: directory, extendEnv: true, stdin: "ignore" }), ) - // Drain stdout, capture stderr for error reporting - const [, stderr] = yield* Effect.all( - [Stream.runDrain(handle.stdout), Stream.mkString(Stream.decodeText(handle.stderr))], - { concurrency: 2 }, - ).pipe(Effect.orDie) - const code = yield* handle.exitCode - return { code, stderr } + return { code: result.exitCode, stderr: result.stderr.toString("utf8") } }, - Effect.scoped, Effect.catch(() => Effect.succeed({ code: 1, stderr: "" })), ) @@ -529,30 +523,30 @@ export const layer: Layer.Layer< const reset = Effect.fn("Worktree.reset")(function* (input: ResetInput) { const ctx = yield* InstanceState.context if (ctx.project.vcs !== "git") { - throw new NotGitError({ message: "Worktrees are only supported for git projects" }) + return yield* new NotGitError({ message: "Worktrees are only supported for git projects" }) } const directory = yield* canonical(input.directory) const primary = yield* canonical(ctx.worktree) if (directory === primary) { - throw new ResetFailedError({ message: "Cannot reset the primary workspace" }) + return yield* new ResetFailedError({ message: "Cannot reset the primary workspace" }) } const list = yield* git(["worktree", "list", "--porcelain"], { cwd: ctx.worktree }) if (list.code !== 0) { - throw new ResetFailedError({ message: list.stderr || list.text || "Failed to read git worktrees" }) + return yield* new ResetFailedError({ message: list.stderr || list.text || "Failed to read git worktrees" }) } const entry = yield* locateWorktree(parseWorktreeList(list.text), directory) if (!entry?.path) { - throw new ResetFailedError({ message: "Worktree not found" }) + return yield* new ResetFailedError({ message: "Worktree not found" }) } const worktreePath = entry.path const base = yield* gitSvc.defaultBranch(ctx.worktree) if (!base) { - throw new ResetFailedError({ message: "Default branch not found" }) + return yield* new ResetFailedError({ message: "Default branch not found" }) } const sep = base.ref.indexOf("/") @@ -574,7 +568,9 @@ export const layer: Layer.Layer< const cleanResult = yield* sweep(worktreePath) if (cleanResult.code !== 0) { - throw new ResetFailedError({ message: cleanResult.stderr || cleanResult.text || "Failed to clean worktree" }) + return yield* new ResetFailedError({ + message: cleanResult.stderr || cleanResult.text || "Failed to clean worktree", + }) } yield* gitExpect( @@ -597,11 +593,11 @@ export const layer: Layer.Layer< const status = yield* git(["-c", "core.fsmonitor=false", "status", "--porcelain=v1"], { cwd: worktreePath }) if (status.code !== 0) { - throw new ResetFailedError({ message: status.stderr || status.text || "Failed to read git status" }) + return yield* new ResetFailedError({ message: status.stderr || status.text || "Failed to read git status" }) } if (status.text.trim()) { - throw new ResetFailedError({ message: `Worktree reset left local changes:\n${status.text.trim()}` }) + return yield* new ResetFailedError({ message: `Worktree reset left local changes:\n${status.text.trim()}` }) } yield* runStartScripts(worktreePath, { projectID: ctx.project.id }).pipe( @@ -618,7 +614,7 @@ export const layer: Layer.Layer< export const appLayer = layer.pipe( Layer.provide(Git.defaultLayer), - Layer.provide(CrossSpawnSpawner.defaultLayer), + Layer.provide(AppProcess.defaultLayer), Layer.provide(Project.defaultLayer), Layer.provide(AppFileSystem.defaultLayer), Layer.provide(NodePath.layer), diff --git a/packages/opencode/test/AGENTS.md b/packages/opencode/test/AGENTS.md index 52bd6ee98ec..bff1ff5dde0 100644 --- a/packages/opencode/test/AGENTS.md +++ b/packages/opencode/test/AGENTS.md @@ -157,3 +157,48 @@ const failingAccountLayer = Layer.mock(Account.Service, { ``` This is much shorter than stubbing every method with `Effect.void` / `Effect.succeed(...)` placeholders, and it keeps the test focused on the behaviour under test. + +## Synchronizing With Concurrent Work + +### The Anti-Pattern + +Using `Effect.sleep(N)` or `setTimeout` as a "wait for the forked fiber to be ready" hack races the scheduler. The forked fiber may not have reached the synchronization point within `N` ms on a slow CI host, and the test fails intermittently. See PR #27622 for a concrete flake that fell out of this exact pattern. + +### The Fix + +Wait on a **published readiness signal**, not wall-clock time. Available affordances: + +- `pollWithTimeout(effect, message, duration?)` from `test/lib/effect.ts` — repeatedly run a predicate effect until it returns a non-`undefined` value, with a timeout. +- `awaitWithTimeout(effect, message, duration?)` from `test/lib/effect.ts` — wrap any effect with `Effect.timeoutOrElse` and a custom error message. +- `llm.wait(n)` from `test/lib/llm-server.ts` — wait until the mock LLM has received `n` HTTP calls. +- `SessionStatus.Service` `.get(sessionID)` — observable per-session state (`{ type: "busy" | "idle" | ... }`). +- `BackgroundJob.wait({ id, timeout })` from `src/background/job.ts` — wait for a background job to complete. +- Bus subscriptions — fork `Stream.runForEach(bus.subscribe(Event), ...)` and open a `Latch` inside the callback to signal first-event readiness. +- `Deferred.await(deferred).pipe(Effect.timeoutOrElse(...))` for one-shot signals. + +### Example + +```ts +// Antipattern — race +yield * prompt.shell({ command: "sleep 30" }).pipe(Effect.forkChild) +yield * Effect.sleep(50) +yield * prompt.cancel(chat.id) + +// Fix — wait for a published readiness signal +yield * prompt.shell({ command: "sleep 30" }).pipe(Effect.forkChild) +yield * + pollWithTimeout( + Effect.gen(function* () { + const s = yield* (yield* SessionStatus.Service).get(chat.id) + return s.type === "busy" ? (true as const) : undefined + }), + "session never became busy", + ) +yield * prompt.cancel(chat.id) +``` + +### When Fixed Sleeps Are OK + +- Testing debounce or throttle behavior, where the sleep **is** the test. +- Letting real wall-clock advance past a genuine timestamp resolution boundary (e.g. mtime granularity). +- Simulating network latency in race-regression tests that intentionally exercise ordering. diff --git a/packages/opencode/test/EFFECT_TEST_MIGRATION.md b/packages/opencode/test/EFFECT_TEST_MIGRATION.md new file mode 100644 index 00000000000..73acea91489 --- /dev/null +++ b/packages/opencode/test/EFFECT_TEST_MIGRATION.md @@ -0,0 +1,169 @@ +# Effect Test Migration + +Move tests that exercise Effect services out of Promise-land and into the +shared `testEffect` pattern. + +This file is guidance, not a live inventory. Before claiming a migration, +search current `dev` for the exact anti-pattern and update any PR notes +with what you actually changed. + +## Target Pattern + +Every Effect service test should have one local runner near the top: + +```ts +const it = testEffect(layer) +``` + +Use the runner method that matches the behavior: + +```ts +it.effect("pure service behavior", () => + Effect.gen(function* () { + const service = yield* SomeService.Service + expect(yield* service.run()).toEqual("ok") + }), +) + +it.instance("instance-local behavior", () => + Effect.gen(function* () { + const test = yield* TestInstance + expect(test.directory).toContain("opencode-test-") + }), +) + +it.live("live filesystem or process behavior", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + // real clock / fs / git / process work + }), +) +``` + +## Choosing The Runner + +- `it.effect(...)` — pure Effect behavior with `TestClock` and + `TestConsole`. +- `it.instance(...)` — service behavior that needs one scoped opencode + instance. +- `it.live(...)` — real time, filesystem mtimes, child processes, git, + locks, servers, watchers, or OS behavior. + +Most integration-style tests use `it.live(...)` or `it.instance(...)`. + +## Layer Rules + +Compose tests from open service layers when a dependency needs replacing. +Do not use a closed `defaultLayer` and then try to override an inner +dependency after it has already been provided. + +Prefer small reusable fake boundary layers in `test/fake/*`: + +```ts +AuthTest.empty +AccountTest.empty +NpmTest.noop +SkillTest.empty +ProviderTest.fake().layer +``` + +Use `Layer.mock` for partial service stubs. Missing methods should fail +loudly if the test accidentally calls them. + +Do not add generic test-layer builders until repeated local compositions +prove the need. + +## Fixture Rules + +Use Effect-aware fixtures from `test/fixture/fixture.ts`: + +- `TestInstance` inside `it.instance(...)` for the current temp instance. +- `tmpdirScoped(...)` inside `Effect.gen` for extra temp directories. +- `provideInstance(dir)(effect)` when one test needs to switch instance + context. +- `provideTmpdirInstance((dir) => effect, options)` when a live test needs + custom instance setup or multiple instance scopes. +- `disposeAllInstances()` in `afterEach` only for integration tests that + intentionally touch shared instance registries. + +Avoid mutable global setup. If a global mutation is unavoidable during a +migration, scope it with acquire/release and treat it as temporary. + +Long term, tests should not toggle `process.env`, `Global.Path`, or +mutable flags when behavior can be modeled with services. Prefer layers +such as `RuntimeFlags.layer(...)` or focused fake services. + +## Anti-Patterns To Remove + +- `test(..., async () => Effect.runPromise(...))` +- local `run(...)`, `load(...)`, `svc(...)`, or `runtime.runPromise(...)` + wrappers that only provide a layer +- `tmpdir()` plus legacy instance provision in Promise test bodies +- custom `ManagedRuntime.make(...)` in test files +- Promise `try/catch` around Effect failures +- `Promise.withResolvers`, `Bun.sleep`, or `setTimeout` for synchronization + when events, `Deferred`, fibers, or deterministic state checks fit +- mutable env/global/flag changes after layers are built + +Promise helpers are acceptable at non-Effect boundaries, but yield them from +inside an Effect body with `Effect.promise(...)` rather than making them the +test harness. + +## Conversion Recipe + +1. Identify the real service under test and whether its open `layer` or + closed `defaultLayer` is appropriate. +2. Build one top-level `layer` with real dependencies where relevant and + fake layers at slow or external boundaries. +3. Replace local Promise wrappers with Effect helpers. +4. Convert `test(..., async () => { ... })` to `it.effect`, `it.instance`, + or `it.live`. +5. Move `await` calls inside `Effect.gen` as `yield*` calls. +6. Replace `await using tmp = await tmpdir(...)` with + `yield* tmpdirScoped(...)` when the temp directory lives inside the + Effect test. +7. Replace Promise failure assertions with `Effect.exit`, `Effect.flip`, or + focused assertion helpers. +8. Preserve concurrency with fibers, `Deferred`, and + `Effect.all(..., { concurrency: "unbounded" })`; do not accidentally + serialize formerly parallel behavior. +9. Run the focused test file and `bun typecheck` from `packages/opencode`. + +## Good Examples + +Use current examples as patterns, but re-check them before copying because +test migrations are active: + +- `test/effect/instance-state.test.ts` — scoped directories, instance + switching, disposal, and concurrency. +- `test/bus/bus-effect.test.ts` — `Deferred`, streams, scoped fibers. +- `test/agent/plugin-agent-regression.test.ts` — real service layers plus + fake boundary layers. +- `test/account/service.test.ts` — service-level live tests, typed errors, + fake HTTP clients. + +## Migration Queue Policy + +Do not maintain a long file checklist here. It goes stale quickly. + +When looking for the next target, search for current anti-patterns: + +```bash +git grep -n "Effect.runPromise\|ManagedRuntime\|Promise.withResolvers\|Bun.sleep\|WithInstance" -- packages/opencode/test +``` + +Then choose one file or one small cluster, keep the PR focused, and mention +the focused verification in the PR body. + +## Rough Edges To Watch + +- Failure assertions against `Exit` / `Cause` can get verbose. Add helpers + only after the same shape repeats across multiple files. +- Some tests still need `Effect.promise(...)` around Node/Bun APIs. Prefer + Effect platform services when the surrounding code already uses them, but + do not block useful migrations on perfect abstraction. +- Layer composition can be noisy when a test needs real service subtrees plus + fake boundaries. Extract small `test/fake/*` layers before inventing + larger builders. +- Concurrency tests can get harder to read after replacing Promise + resolvers. Look for repeated patterns that deserve named helpers. diff --git a/packages/opencode/test/acp/event-subscription.test.ts b/packages/opencode/test/acp/event-subscription.test.ts index db491685bc7..cb9e3950b4b 100644 --- a/packages/opencode/test/acp/event-subscription.test.ts +++ b/packages/opencode/test/acp/event-subscription.test.ts @@ -2,23 +2,35 @@ import { describe, expect, test } from "bun:test" import { ACP } from "../../src/acp/agent" import type { AgentSideConnection } from "@agentclientprotocol/sdk" import type { - Event, - EventMessagePartUpdated, + GlobalEvent, + SyncEventMessagePartUpdated, ToolStateCompleted, ToolStatePending, ToolStateRunning, } from "@kilocode/sdk/v2" -import { WithInstance } from "../../src/project/with-instance" -import { tmpdir } from "../fixture/fixture" +import { provideTestInstance, tmpdir } from "../fixture/fixture" + +const pollUntil = async ( + check: () => T | undefined | false | Promise, + message: string, + opts?: { timeoutMs?: number; intervalMs?: number }, +): Promise => { + const timeoutMs = opts?.timeoutMs ?? 2000 + const intervalMs = opts?.intervalMs ?? 5 + const started = Date.now() + while (true) { + const v = await check() + if (v !== undefined && v !== null && v !== false) return v as T + if (Date.now() - started > timeoutMs) throw new Error(message) + await new Promise((r) => setTimeout(r, intervalMs)) + } +} type SessionUpdateParams = Parameters[0] type RequestPermissionParams = Parameters[0] type RequestPermissionResult = Awaited> -type GlobalEventEnvelope = { - directory?: string - payload?: Event -} +type GlobalEventEnvelope = Partial type EventController = { push: (event: GlobalEventEnvelope) => void @@ -71,10 +83,13 @@ function toolEvent( input: opts.input, raw: opts.raw, } - const payload: EventMessagePartUpdated = { + const payload: SyncEventMessagePartUpdated = { id: `evt_${opts.callID}`, - type: "message.part.updated", - properties: { + type: "sync", + name: "message.part.updated.1", + seq: 1, + aggregateID: "sessionID", + data: { sessionID: sessionId, time: Date.now(), part: { @@ -111,10 +126,13 @@ function completedToolEvent( time: { start: Date.now() - 1, end: Date.now() }, ...(opts.attachments && { attachments: opts.attachments }), } - const payload: EventMessagePartUpdated = { + const payload: SyncEventMessagePartUpdated = { id: `evt_${opts.callID}`, - type: "message.part.updated", - properties: { + type: "sync", + name: "message.part.updated.1", + seq: 1, + aggregateID: "sessionID", + data: { sessionID: sessionId, time: Date.now(), part: { @@ -319,7 +337,7 @@ function createFakeAgent() { describe("acp.agent event subscription", () => { test("routes message.part.delta by the event sessionID (no cross-session pollution)", async () => { await using tmp = await tmpdir() - await WithInstance.provide({ + await provideTestInstance({ directory: tmp.path, fn: async () => { const { agent, controller, updates, stop } = createFakeAgent() @@ -342,7 +360,10 @@ describe("acp.agent event subscription", () => { }, } as any) - await new Promise((r) => setTimeout(r, 10)) + await pollUntil( + () => (updates.get(sessionB) ?? []).includes("agent_message_chunk"), + "sessionB never received agent_message_chunk", + ) expect((updates.get(sessionA) ?? []).includes("agent_message_chunk")).toBe(false) expect((updates.get(sessionB) ?? []).includes("agent_message_chunk")).toBe(true) @@ -354,7 +375,7 @@ describe("acp.agent event subscription", () => { test("does not emit user_message_chunk for live prompt parts", async () => { await using tmp = await tmpdir() - await WithInstance.provide({ + await provideTestInstance({ directory: tmp.path, fn: async () => { const { agent, controller, sessionUpdates, stop } = createFakeAgent() @@ -364,8 +385,12 @@ describe("acp.agent event subscription", () => { controller.push({ directory: cwd, payload: { - type: "message.part.updated", - properties: { + type: "sync", + name: "message.part.updated.1", + id: "evt_part_1", + seq: 1, + aggregateID: "sessionID", + data: { sessionID: sessionId, time: Date.now(), part: { @@ -379,7 +404,25 @@ describe("acp.agent event subscription", () => { }, } as any) - await new Promise((r) => setTimeout(r, 20)) + controller.push({ + directory: cwd, + payload: { + type: "message.part.delta", + properties: { + sessionID: sessionId, + messageID: "msg_marker", + partID: "msg_marker_part", + field: "text", + delta: "marker", + }, + }, + } as any) + + await pollUntil( + () => + sessionUpdates.some((u) => u.sessionId === sessionId && u.update.sessionUpdate === "agent_message_chunk"), + "marker event was never processed", + ) expect( sessionUpdates @@ -394,7 +437,7 @@ describe("acp.agent event subscription", () => { test("keeps concurrent sessions isolated when message.part.delta events are interleaved", async () => { await using tmp = await tmpdir() - await WithInstance.provide({ + await provideTestInstance({ directory: tmp.path, fn: async () => { const { agent, controller, chunks, stop } = createFakeAgent() @@ -429,7 +472,12 @@ describe("acp.agent event subscription", () => { push(sessionA, "msg_a", tokenA[2]) push(sessionB, "msg_b", tokenB[2]) - await new Promise((r) => setTimeout(r, 20)) + await pollUntil( + () => + (chunks.get(sessionA) ?? "").includes(tokenA.join("")) && + (chunks.get(sessionB) ?? "").includes(tokenB.join("")), + "interleaved chunks never fully arrived", + ) const a = chunks.get(sessionA) ?? "" const b = chunks.get(sessionB) ?? "" @@ -446,7 +494,7 @@ describe("acp.agent event subscription", () => { test("does not create additional event subscriptions on repeated loadSession()", async () => { await using tmp = await tmpdir() - await WithInstance.provide({ + await provideTestInstance({ directory: tmp.path, fn: async () => { const { agent, calls, stop } = createFakeAgent() @@ -468,7 +516,7 @@ describe("acp.agent event subscription", () => { test("permission.asked events are handled and replied", async () => { await using tmp = await tmpdir() - await WithInstance.provide({ + await provideTestInstance({ directory: tmp.path, fn: async () => { const permissionReplies: string[] = [] @@ -496,7 +544,7 @@ describe("acp.agent event subscription", () => { }, } as any) - await new Promise((r) => setTimeout(r, 20)) + await pollUntil(() => permissionReplies.includes("perm_1"), "perm_1 was never replied") expect(permissionReplies).toContain("perm_1") @@ -507,7 +555,7 @@ describe("acp.agent event subscription", () => { test("permission prompt on session A does not block message updates for session B", async () => { await using tmp = await tmpdir() - await WithInstance.provide({ + await provideTestInstance({ directory: tmp.path, fn: async () => { const permissionReplies: string[] = [] @@ -555,10 +603,8 @@ describe("acp.agent event subscription", () => { }, } as any) - // Give time for permission handling to start - await new Promise((r) => setTimeout(r, 10)) + await pollUntil(() => _permissionCalls > 0, "permission handling for A never started") - // Push message for session B while A's permission is pending controller.push({ directory: cwd, payload: { @@ -573,18 +619,17 @@ describe("acp.agent event subscription", () => { }, } as any) - // Wait for session B's message to be processed - await new Promise((r) => setTimeout(r, 20)) + await pollUntil( + () => (chunks.get(sessionB) ?? "").includes("session_b_message"), + "session B never received its message", + ) - // Session B should have received message even though A's permission is still pending expect(chunks.get(sessionB) ?? "").toContain("session_b_message") expect(permissionReplies).not.toContain("perm_a") - // Release session A's permission resolvePermissionA!() - await new Promise((r) => setTimeout(r, 20)) + await pollUntil(() => permissionReplies.includes("perm_a"), "perm_a was never replied after release") - // Now session A's permission should be replied expect(permissionReplies).toContain("perm_a") stop() @@ -594,7 +639,7 @@ describe("acp.agent event subscription", () => { test("streams running bash output snapshots and de-dupes identical snapshots", async () => { await using tmp = await tmpdir() - await WithInstance.provide({ + await provideTestInstance({ directory: tmp.path, fn: async () => { const { agent, controller, sessionUpdates, stop } = createFakeAgent() @@ -613,7 +658,15 @@ describe("acp.agent event subscription", () => { }), ) } - await new Promise((r) => setTimeout(r, 20)) + await pollUntil( + () => + sessionUpdates + .filter((u) => u.sessionId === sessionId) + .filter((u) => isToolCallUpdate(u.update)) + .map((u) => inProgressText(u.update)) + .filter((t) => t === "ab").length > 0, + "final bash snapshot 'ab' never arrived", + ) const snapshots = sessionUpdates .filter((u) => u.sessionId === sessionId) @@ -628,7 +681,7 @@ describe("acp.agent event subscription", () => { test("emits synthetic pending before first running update for any tool", async () => { await using tmp = await tmpdir() - await WithInstance.provide({ + await provideTestInstance({ directory: tmp.path, fn: async () => { const { agent, controller, sessionUpdates, stop } = createFakeAgent() @@ -652,7 +705,14 @@ describe("acp.agent event subscription", () => { input: { filePath: "/tmp/example.txt" }, }), ) - await new Promise((r) => setTimeout(r, 20)) + await pollUntil( + () => + sessionUpdates + .filter((u) => u.sessionId === sessionId) + .map((u) => u.update.sessionUpdate) + .filter((u) => u === "tool_call" || u === "tool_call_update").length >= 4, + "expected 4 tool_call/tool_call_update events", + ) const types = sessionUpdates .filter((u) => u.sessionId === sessionId) @@ -673,7 +733,7 @@ describe("acp.agent event subscription", () => { test("emits image attachments as ACP tool content blocks on live completed tool updates", async () => { await using tmp = await tmpdir() - await WithInstance.provide({ + await provideTestInstance({ directory: tmp.path, fn: async () => { const { agent, controller, sessionUpdates, stop } = createFakeAgent() @@ -709,7 +769,10 @@ describe("acp.agent event subscription", () => { ], }), ) - await new Promise((r) => setTimeout(r, 20)) + await pollUntil( + () => completedToolUpdate(sessionUpdates, sessionId, "call_image"), + "completed tool update for call_image never arrived", + ) const update = completedToolUpdate(sessionUpdates, sessionId, "call_image") expect(update?.content).toContainEqual({ @@ -730,7 +793,7 @@ describe("acp.agent event subscription", () => { test("replays completed tool image attachments as ACP tool content blocks", async () => { await using tmp = await tmpdir() - await WithInstance.provide({ + await provideTestInstance({ directory: tmp.path, fn: async () => { const { agent, sessionUpdates, stop, sdk } = createFakeAgent() @@ -797,7 +860,7 @@ describe("acp.agent event subscription", () => { test("does not emit duplicate synthetic pending after replayed running tool", async () => { await using tmp = await tmpdir() - await WithInstance.provide({ + await provideTestInstance({ directory: tmp.path, fn: async () => { const { agent, controller, sessionUpdates, stop, sdk } = createFakeAgent() @@ -839,7 +902,16 @@ describe("acp.agent event subscription", () => { metadata: { output: "hi\nthere\n" }, }), ) - await new Promise((r) => setTimeout(r, 20)) + await pollUntil( + () => + sessionUpdates + .filter((u) => u.sessionId === sessionId) + .map((u) => u.update) + .filter((u) => "toolCallId" in u && u.toolCallId === "call_1") + .map((u) => u.sessionUpdate) + .filter((u) => u === "tool_call" || u === "tool_call_update").length >= 3, + "expected 3 tool events for call_1", + ) const types = sessionUpdates .filter((u) => u.sessionId === sessionId) @@ -856,7 +928,7 @@ describe("acp.agent event subscription", () => { test("clears bash snapshot marker on pending state", async () => { await using tmp = await tmpdir() - await WithInstance.provide({ + await provideTestInstance({ directory: tmp.path, fn: async () => { const { agent, controller, sessionUpdates, stop } = createFakeAgent() @@ -891,7 +963,15 @@ describe("acp.agent event subscription", () => { metadata: { output: "a" }, }), ) - await new Promise((r) => setTimeout(r, 20)) + await pollUntil( + () => + sessionUpdates + .filter((u) => u.sessionId === sessionId) + .filter((u) => isToolCallUpdate(u.update)) + .map((u) => inProgressText(u.update)) + .filter((t) => t === "a").length >= 2, + "expected two 'a' bash snapshots after pending reset", + ) const snapshots = sessionUpdates .filter((u) => u.sessionId === sessionId) diff --git a/packages/opencode/test/agent/agent.test.ts b/packages/opencode/test/agent/agent.test.ts index f14db0ad0e3..8253471efb4 100644 --- a/packages/opencode/test/agent/agent.test.ts +++ b/packages/opencode/test/agent/agent.test.ts @@ -1,12 +1,31 @@ -import { afterEach, test, expect } from "bun:test" -import { Effect } from "effect" +import { afterEach, expect } from "bun:test" +import { Cause, Effect, Exit, Layer } from "effect" import path from "path" -import { disposeAllInstances, provideInstance, tmpdir } from "../fixture/fixture" -import { WithInstance } from "../../src/project/with-instance" +import { disposeAllInstances, TestInstance } from "../fixture/fixture" +import { testEffect } from "../lib/effect" import { Agent } from "../../src/agent/agent" +import { Auth } from "../../src/auth" +import { Config } from "../../src/config/config" +import { RuntimeFlags } from "../../src/effect/runtime-flags" import { Global } from "@opencode-ai/core/global" -import { Flag } from "@opencode-ai/core/flag/flag" import { Permission } from "../../src/permission" +import { Plugin } from "../../src/plugin" +import { Provider } from "../../src/provider/provider" +import { Skill } from "../../src/skill" +import { Truncate } from "../../src/tool/truncate" + +const agentLayer = (flags: Partial = {}) => + Agent.layer.pipe( + Layer.provide(Plugin.defaultLayer), + Layer.provide(Provider.defaultLayer), + Layer.provide(Auth.defaultLayer), + Layer.provide(Config.defaultLayer), + Layer.provide(Skill.defaultLayer), + Layer.provide(RuntimeFlags.layer(flags)), + ) + +const it = testEffect(agentLayer()) +const scout = testEffect(agentLayer({ experimentalScout: true })) // Helper to evaluate permission for a tool with wildcard pattern function evalPerm(agent: Agent.Info | undefined, permission: string): Permission.Action | undefined { @@ -14,332 +33,155 @@ function evalPerm(agent: Agent.Info | undefined, permission: string): Permission return Permission.evaluate(permission, "*", agent.permission).action } -function load(dir: string, fn: (svc: Agent.Interface) => Effect.Effect) { - return Effect.runPromise(provideInstance(dir)(Agent.Service.use(fn)).pipe(Effect.provide(Agent.defaultLayer))) +function load(fn: (svc: Agent.Interface) => Effect.Effect) { + return Agent.Service.use(fn) } -async function withExperimentalScout(enabled: boolean, fn: () => Promise) { - const original = Flag.KILO_EXPERIMENTAL_SCOUT - Flag.KILO_EXPERIMENTAL_SCOUT = enabled - try { - await fn() - } finally { - Flag.KILO_EXPERIMENTAL_SCOUT = original - } -} +const expectDefaultAgentError = Effect.fn("AgentTest.expectDefaultAgentError")(function* (message: string) { + const exit = yield* load((svc) => svc.defaultAgent()).pipe(Effect.exit) + expect(Exit.isFailure(exit)).toBe(true) + if (Exit.isFailure(exit)) expect(Cause.pretty(exit.cause)).toContain(message) +}) afterEach(async () => { await disposeAllInstances() }) -test("returns default native agents when no config", async () => { - await withExperimentalScout(false, async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const agents = await load(tmp.path, (svc) => svc.list()) - const names = agents.map((a) => a.name) - expect(names).toContain("code") // kilocode_change - expect(names).toContain("plan") - // kilocode_change start - expect(names).toContain("debug") - expect(names).toContain("orchestrator") - expect(names).toContain("ask") - // kilocode_change end - expect(names).toContain("general") - expect(names).toContain("explore") - expect(names).not.toContain("scout") - expect(names).toContain("compaction") - expect(names).toContain("title") - expect(names).toContain("summary") - }, - }) - }) -}) - -// kilocode_change start - renamed from "build" to "code" -test("code agent has correct default properties", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const code = await load(tmp.path, (svc) => svc.get("code")) - expect(code).toBeDefined() - expect(code?.mode).toBe("primary") - expect(code?.native).toBe(true) - expect(evalPerm(code, "edit")).toBe("allow") - expect(evalPerm(code, "bash")).toBe("ask") - expect(evalPerm(code, "repo_clone")).toBe("deny") - expect(evalPerm(code, "repo_overview")).toBe("deny") - }, - }) -}) - -test("ask agent has correct default properties", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const ask = await load(tmp.path, (svc) => svc.get("ask")) - expect(ask).toBeDefined() - expect(ask?.mode).toBe("primary") - expect(ask?.native).toBe(true) - // ask agent should allow read-only tools - expect(evalPerm(ask, "read")).toBe("allow") - expect(evalPerm(ask, "grep")).toBe("allow") - expect(evalPerm(ask, "glob")).toBe("allow") - expect(evalPerm(ask, "webfetch")).toBe("allow") - expect(evalPerm(ask, "websearch")).toBe("allow") - expect(evalPerm(ask, "codesearch")).toBe("allow") - // ask agent should deny edit and bash - expect(evalPerm(ask, "edit")).toBe("deny") - expect(evalPerm(ask, "bash")).toBe("deny") - expect(evalPerm(ask, "task")).toBe("deny") - // ask agent should gate .env files - expect(Permission.evaluate("read", ".env", ask!.permission).action).toBe("ask") - expect(Permission.evaluate("read", "config.env.local", ask!.permission).action).toBe("ask") - expect(Permission.evaluate("read", ".env.example", ask!.permission).action).toBe("allow") - expect(Permission.evaluate("read", "src/index.ts", ask!.permission).action).toBe("allow") - }, - }) -}) -test("ask agent denies edit/write/bash even when user config adds a specific edit allow", async () => { - await using tmp = await tmpdir({ +it.instance("returns default native agents when no config", () => + Effect.gen(function* () { + const agents = yield* load((svc) => svc.list()) + const names = agents.map((a) => a.name) + expect(names).toContain("code") + expect(names).toContain("plan") + expect(names).toContain("general") + expect(names).toContain("explore") + expect(names).not.toContain("scout") + expect(names).toContain("compaction") + expect(names).toContain("title") + expect(names).toContain("summary") + }), +) + +it.instance("build agent has correct default properties", () => + Effect.gen(function* () { + const build = yield* load((svc) => svc.get("build")) + expect(build).toBeDefined() + expect(build?.mode).toBe("primary") + expect(build?.native).toBe(true) + expect(evalPerm(build, "edit")).toBe("allow") + expect(evalPerm(build, "bash")).toBe("ask") + expect(evalPerm(build, "repo_clone")).toBe("deny") + expect(evalPerm(build, "repo_overview")).toBe("deny") + }), +) + +it.instance("plan agent denies edits except .opencode/plans/*", () => + Effect.gen(function* () { + const plan = yield* load((svc) => svc.get("plan")) + expect(plan).toBeDefined() + // Wildcard is denied + expect(evalPerm(plan, "edit")).toBe("deny") + // But specific path is allowed + expect(Permission.evaluate("edit", ".opencode/plans/foo.md", plan!.permission).action).toBe("allow") + }), +) + +it.instance("explore agent denies edit and write", () => + Effect.gen(function* () { + const explore = yield* load((svc) => svc.get("explore")) + expect(explore).toBeDefined() + expect(explore?.mode).toBe("subagent") + expect(evalPerm(explore, "edit")).toBe("deny") + expect(evalPerm(explore, "write")).toBe("deny") + expect(evalPerm(explore, "todowrite")).toBe("deny") + }), +) + +it.instance("explore agent asks for external directories and allows whitelisted external paths", () => + Effect.gen(function* () { + const explore = yield* load((svc) => svc.get("explore")) + expect(explore).toBeDefined() + expect(Permission.evaluate("external_directory", "/some/other/path", explore!.permission).action).toBe("ask") + expect(Permission.evaluate("external_directory", Truncate.GLOB, explore!.permission).action).toBe("allow") + expect( + Permission.evaluate("external_directory", path.join(Global.Path.tmp, "agent-work"), explore!.permission).action, + ).toBe("allow") + }), +) + +scout.instance("scout agent allows repo cloning and repo cache reads", () => + Effect.gen(function* () { + const scout = yield* load((svc) => svc.get("scout")) + expect(scout).toBeDefined() + expect(scout?.mode).toBe("subagent") + expect(evalPerm(scout, "repo_clone")).toBe("allow") + expect(evalPerm(scout, "repo_overview")).toBe("allow") + expect(evalPerm(scout, "edit")).toBe("deny") + expect( + Permission.evaluate( + "external_directory", + path.join(Global.Path.repos, "github.com", "owner", "repo", "README.md"), + scout!.permission, + ).action, + ).toBe("allow") + }), +) + +scout.instance( + "reference config creates scout-backed subagents", + () => + Effect.gen(function* () { + const agents = yield* load((svc) => svc.list()) + const names = agents.map((agent) => agent.name) + expect(names).toContain("scout") + expect(names).toContain("effect") + expect(names).toContain("effectFull") + expect(names).toContain("localdocs") + expect(names).toContain("localdocsFull") + }), + { config: { - permission: { - edit: { "src/output.log": "allow" }, - }, - }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const ask = await load(tmp.path, (svc) => svc.get("ask")) - expect(ask).toBeDefined() - // user config must not leak edit capability into ask mode — even for the - // specific path the user allowed, ask mode must still deny it - expect(Permission.evaluate("edit", "src/output.log", ask!.permission).action).toBe("deny") - expect(evalPerm(ask, "bash")).toBe("deny") - expect(evalPerm(ask, "task")).toBe("deny") - // safe tools still work - expect(evalPerm(ask, "read")).toBe("allow") - expect(evalPerm(ask, "grep")).toBe("allow") - // disabled() hides tools entirely from LLM — bash is NOT disabled because it has specific allow rules - const disabled = Permission.disabled(["edit", "write", "bash"], ask!.permission) - expect(disabled.has("edit")).toBe(true) - expect(disabled.has("write")).toBe(true) - expect(disabled.has("bash")).toBe(false) - }, - }) -}) - -test("plan agent denies edits except .kilo/plans/* and .opencode/plans/*", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const plan = await load(tmp.path, (svc) => svc.get("plan")) - expect(plan).toBeDefined() - expect(evalPerm(plan, "edit")).toBe("deny") - expect(Permission.evaluate("edit", "src/index.ts", plan!.permission).action).toBe("deny") - expect(Permission.evaluate("edit", ".kilo/plans/foo.md", plan!.permission).action).toBe("allow") - expect(Permission.evaluate("edit", ".opencode/plans/foo.md", plan!.permission).action).toBe("allow") - }, - }) -}) - -test("plan agent user config allows cannot re-enable non-plan edits", async () => { - await using tmp = await tmpdir({ - config: { - permission: { - edit: { "src/output.log": "allow" }, - }, - }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const plan = await load(tmp.path, (svc) => svc.get("plan")) - expect(plan).toBeDefined() - expect(Permission.evaluate("edit", "src/output.log", plan!.permission).action).toBe("deny") - expect(Permission.evaluate("edit", ".kilo/plans/foo.md", plan!.permission).action).toBe("allow") - expect(Permission.evaluate("edit", ".opencode/plans/foo.md", plan!.permission).action).toBe("allow") - }, - }) -}) -// kilocode_change end - -test("explore agent denies edit and write", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const explore = await load(tmp.path, (svc) => svc.get("explore")) - expect(explore).toBeDefined() - expect(explore?.mode).toBe("subagent") - expect(evalPerm(explore, "edit")).toBe("deny") - expect(evalPerm(explore, "write")).toBe("deny") - expect(evalPerm(explore, "todowrite")).toBe("deny") - }, - }) -}) - -test("explore agent asks for external directories and allows whitelisted external paths", async () => { - const { Truncate } = await import("../../src/tool/truncate") - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const explore = await load(tmp.path, (svc) => svc.get("explore")) - expect(explore).toBeDefined() - expect(Permission.evaluate("external_directory", "/some/other/path", explore!.permission).action).toBe("ask") - expect(Permission.evaluate("external_directory", Truncate.GLOB, explore!.permission).action).toBe("allow") - expect( - Permission.evaluate("external_directory", path.join(Global.Path.tmp, "agent-work"), explore!.permission).action, - ).toBe("allow") - }, - }) -}) - -test("scout agent allows repo cloning and repo cache reads", async () => { - await withExperimentalScout(true, async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const scout = await load(tmp.path, (svc) => svc.get("scout")) - expect(scout).toBeDefined() - expect(scout?.mode).toBe("subagent") - expect(evalPerm(scout, "repo_clone")).toBe("allow") - expect(evalPerm(scout, "repo_overview")).toBe("allow") - expect(evalPerm(scout, "edit")).toBe("deny") - expect( - Permission.evaluate( - "external_directory", - path.join(Global.Path.repos, "github.com", "owner", "repo", "README.md"), - scout!.permission, - ).action, - ).toBe("allow") - }, - }) - }) -}) - -test("reference config creates scout-backed subagents", async () => { - await withExperimentalScout(true, async () => { - await using tmp = await tmpdir({ - config: { - reference: { - effect: "github.com/effect/effect-smol", - effectDev: { - repository: "https://github.com/effect/effect-smol", - branch: "dev", - }, - effectFull: { - repository: "Effect-TS/effect", - branch: "main", - }, - localdocs: "../docs", - localdocsFull: { - path: "../local-docs", - }, + reference: { + effect: "github.com/effect/effect-smol", + effectFull: { + repository: "Effect-TS/effect", + branch: "main", }, - }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const effect = await load(tmp.path, (svc) => svc.get("effect")) - const effectDev = await load(tmp.path, (svc) => svc.get("effectDev")) - const effectFull = await load(tmp.path, (svc) => svc.get("effectFull")) - const local = await load(tmp.path, (svc) => svc.get("localdocs")) - const localFull = await load(tmp.path, (svc) => svc.get("localdocsFull")) - - expect(effect).toBeDefined() - expect(effect?.mode).toBe("subagent") - expect(effect?.prompt).toContain("Repository: github.com/effect/effect-smol") - expect(effect?.prompt).toContain( - `Cached directory: ${path.join(Global.Path.repos, "github.com", "effect", "effect-smol")}`, - ) - expect(effect?.prompt).toContain("Do not call repo_clone") - expect(evalPerm(effect, "repo_clone")).toBe("deny") - - expect(effectDev).toBeDefined() - expect(effectDev?.prompt).toContain("Problem: Reference conflicts with @effect") - expect(effectDev?.prompt).not.toContain("Cached directory:") - - expect(effectFull).toBeDefined() - expect(effectFull?.mode).toBe("subagent") - expect(effectFull?.prompt).toContain("Repository: Effect-TS/effect") - expect(effectFull?.prompt).toContain("Branch/ref: main") - expect(evalPerm(effectFull, "repo_clone")).toBe("deny") - - expect(local).toBeDefined() - expect(local?.mode).toBe("subagent") - expect(local?.prompt).toContain(`Local directory: ${path.resolve(tmp.path, "../docs")}`) - expect( - Permission.evaluate( - "external_directory", - path.join(path.resolve(tmp.path, "../docs"), "README.md"), - local!.permission, - ).action, - ).toBe("allow") - - expect(localFull).toBeDefined() - expect(localFull?.mode).toBe("subagent") - expect(localFull?.prompt).toContain(`Local directory: ${path.resolve(tmp.path, "../local-docs")}`) - }, - }) - }) -}) - -test("general agent denies todo tools", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const general = await load(tmp.path, (svc) => svc.get("general")) - expect(general).toBeDefined() - expect(general?.mode).toBe("subagent") - expect(general?.hidden).toBeUndefined() - expect(evalPerm(general, "todowrite")).toBe("deny") - }, - }) -}) - -test("compaction agent denies all permissions", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const compaction = await load(tmp.path, (svc) => svc.get("compaction")) - expect(compaction).toBeDefined() - expect(compaction?.hidden).toBe(true) - expect(evalPerm(compaction, "bash")).toBe("deny") - expect(evalPerm(compaction, "edit")).toBe("deny") - expect(evalPerm(compaction, "read")).toBe("deny") - }, - }) -}) - -test("custom agent from config creates new agent", async () => { - await using tmp = await tmpdir({ - config: { - agent: { - my_custom_agent: { - model: "openai/gpt-4", - description: "My custom agent", - temperature: 0.5, - top_p: 0.9, + localdocs: "../docs", + localdocsFull: { + path: "../local-docs", }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const custom = await load(tmp.path, (svc) => svc.get("my_custom_agent")) + }, +) + +it.instance("general agent denies todo tools", () => + Effect.gen(function* () { + const general = yield* load((svc) => svc.get("general")) + expect(general).toBeDefined() + expect(general?.mode).toBe("subagent") + expect(general?.hidden).toBeUndefined() + expect(evalPerm(general, "todowrite")).toBe("deny") + }), +) + +it.instance("compaction agent denies all permissions", () => + Effect.gen(function* () { + const compaction = yield* load((svc) => svc.get("compaction")) + expect(compaction).toBeDefined() + expect(compaction?.hidden).toBe(true) + expect(evalPerm(compaction, "bash")).toBe("deny") + expect(evalPerm(compaction, "edit")).toBe("deny") + expect(evalPerm(compaction, "read")).toBe("deny") + }), +) + +it.instance( + "custom agent from config creates new agent", + () => + Effect.gen(function* () { + const custom = yield* load((svc) => svc.get("my_custom_agent")) expect(custom).toBeDefined() expect(String(custom?.model?.providerID)).toBe("openai") expect(String(custom?.model?.modelID)).toBe("gpt-4") @@ -348,69 +190,82 @@ test("custom agent from config creates new agent", async () => { expect(custom?.topP).toBe(0.9) expect(custom?.native).toBe(false) expect(custom?.mode).toBe("all") + }), + { + config: { + agent: { + my_custom_agent: { + model: "openai/gpt-4", + description: "My custom agent", + temperature: 0.5, + top_p: 0.9, + }, + }, }, - }) -}) + }, +) -test("custom agent config overrides native agent properties", async () => { - await using tmp = await tmpdir({ +it.instance( + "custom agent config overrides native agent properties", + () => + Effect.gen(function* () { + const build = yield* load((svc) => svc.get("build")) + expect(build).toBeDefined() + expect(String(build?.model?.providerID)).toBe("anthropic") + expect(String(build?.model?.modelID)).toBe("claude-3") + expect(build?.description).toBe("Custom build agent") + expect(build?.temperature).toBe(0.7) + expect(build?.color).toBe("#FF0000") + expect(build?.native).toBe(true) + }), + { config: { agent: { - // kilocode_change start - code: { + build: { model: "anthropic/claude-3", - description: "Custom code agent", + description: "Custom build agent", temperature: 0.7, color: "#FF0000", }, - // kilocode_change end }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // kilocode_change start - renamed from "build" to "code" - const code = await load(tmp.path, (svc) => svc.get("code")) - expect(code).toBeDefined() - expect(String(code?.model?.providerID)).toBe("anthropic") - expect(String(code?.model?.modelID)).toBe("claude-3") - expect(code?.description).toBe("Custom code agent") - expect(code?.temperature).toBe(0.7) - expect(code?.color).toBe("#FF0000") - expect(code?.native).toBe(true) - // kilocode_change end - }, - }) -}) + }, +) -test("agent disable removes agent from list", async () => { - await using tmp = await tmpdir({ +it.instance( + "agent disable removes agent from list", + () => + Effect.gen(function* () { + const explore = yield* load((svc) => svc.get("explore")) + expect(explore).toBeUndefined() + const agents = yield* load((svc) => svc.list()) + const names = agents.map((a) => a.name) + expect(names).not.toContain("explore") + }), + { config: { agent: { explore: { disable: true }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const explore = await load(tmp.path, (svc) => svc.get("explore")) - expect(explore).toBeUndefined() - const agents = await load(tmp.path, (svc) => svc.list()) - const names = agents.map((a) => a.name) - expect(names).not.toContain("explore") - }, - }) -}) + }, +) -test("agent permission config merges with defaults", async () => { - await using tmp = await tmpdir({ +it.instance( + "agent permission config merges with defaults", + () => + Effect.gen(function* () { + const build = yield* load((svc) => svc.get("build")) + expect(build).toBeDefined() + // Specific pattern is denied + expect(Permission.evaluate("bash", "rm -rf *", build!.permission).action).toBe("deny") + // Edit still allowed + expect(evalPerm(build, "edit")).toBe("allow") + }), + { config: { agent: { - // kilocode_change start - code: { - // kilocode_change end + build: { permission: { bash: { "rm -rf *": "deny", @@ -419,150 +274,125 @@ test("agent permission config merges with defaults", async () => { }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // kilocode_change start - renamed from "build" to "code" - const code = await load(tmp.path, (svc) => svc.get("code")) - expect(code).toBeDefined() - // Specific pattern is denied - expect(Permission.evaluate("bash", "rm -rf *", code!.permission).action).toBe("deny") - // Edit still allowed - expect(evalPerm(code, "edit")).toBe("allow") - // kilocode_change end - }, - }) -}) + }, +) -test("global permission config applies to all agents", async () => { - await using tmp = await tmpdir({ +it.instance( + "global permission config applies to all agents", + () => + Effect.gen(function* () { + const build = yield* load((svc) => svc.get("build")) + expect(build).toBeDefined() + expect(evalPerm(build, "bash")).toBe("deny") + }), + { config: { permission: { bash: "deny", }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // kilocode_change start - renamed from "build" to "code" - const code = await load(tmp.path, (svc) => svc.get("code")) - expect(code).toBeDefined() - expect(evalPerm(code, "bash")).toBe("deny") - // kilocode_change end - }, - }) -}) + }, +) -test("agent steps/maxSteps config sets steps property", async () => { - await using tmp = await tmpdir({ +it.instance( + "agent steps/maxSteps config sets steps property", + () => + Effect.gen(function* () { + const build = yield* load((svc) => svc.get("build")) + const plan = yield* load((svc) => svc.get("plan")) + expect(build?.steps).toBe(50) + expect(plan?.steps).toBe(100) + }), + { config: { agent: { - // kilocode_change start - renamed from "build" to "code" - code: { steps: 50 }, - // kilocode_change end + build: { steps: 50 }, plan: { maxSteps: 100 }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const code = await load(tmp.path, (svc) => svc.get("code")) // kilocode_change - const plan = await load(tmp.path, (svc) => svc.get("plan")) - expect(code?.steps).toBe(50) // kilocode_change - expect(plan?.steps).toBe(100) - }, - }) -}) + }, +) -test("agent mode can be overridden", async () => { - await using tmp = await tmpdir({ +it.instance( + "agent mode can be overridden", + () => + Effect.gen(function* () { + const explore = yield* load((svc) => svc.get("explore")) + expect(explore?.mode).toBe("primary") + }), + { config: { agent: { explore: { mode: "primary" }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const explore = await load(tmp.path, (svc) => svc.get("explore")) - expect(explore?.mode).toBe("primary") - }, - }) -}) - -test("agent name can be overridden", async () => { - await using tmp = await tmpdir({ + }, +) + +it.instance( + "agent name can be overridden", + () => + Effect.gen(function* () { + const build = yield* load((svc) => svc.get("build")) + expect(build?.name).toBe("Builder") + }), + { config: { agent: { - code: { name: "Coder" }, // kilocode_change + build: { name: "Builder" }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // kilocode_change start - renamed from "build" to "code" - const code = await load(tmp.path, (svc) => svc.get("code")) - expect(code?.name).toBe("Coder") - // kilocode_change end - }, - }) -}) - -test("agent prompt can be set from config", async () => { - await using tmp = await tmpdir({ + }, +) + +it.instance( + "agent prompt can be set from config", + () => + Effect.gen(function* () { + const build = yield* load((svc) => svc.get("build")) + expect(build?.prompt).toBe("Custom system prompt") + }), + { config: { agent: { - code: { prompt: "Custom system prompt" }, // kilocode_change + build: { prompt: "Custom system prompt" }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // kilocode_change start - renamed from "build" to "code" - const code = await load(tmp.path, (svc) => svc.get("code")) - expect(code?.prompt).toBe("Custom system prompt") - // kilocode_change end - }, - }) -}) - -test("unknown agent properties are placed into options", async () => { - await using tmp = await tmpdir({ + }, +) + +it.instance( + "unknown agent properties are placed into options", + () => + Effect.gen(function* () { + const build = yield* load((svc) => svc.get("build")) + expect(build?.options.random_property).toBe("hello") + expect(build?.options.another_random).toBe(123) + }), + { config: { agent: { - code: { - // kilocode_change + build: { random_property: "hello", another_random: 123, }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // kilocode_change start - renamed from "build" to "code" - const code = await load(tmp.path, (svc) => svc.get("code")) - expect(code?.options.random_property).toBe("hello") - expect(code?.options.another_random).toBe(123) - // kilocode_change end - }, - }) -}) - -test("agent options merge correctly", async () => { - await using tmp = await tmpdir({ + }, +) + +it.instance( + "agent options merge correctly", + () => + Effect.gen(function* () { + const build = yield* load((svc) => svc.get("build")) + expect(build?.options.custom_option).toBe(true) + expect(build?.options.another_option).toBe("value") + }), + { config: { agent: { - // kilocode_change start - renamed from "build" to "code" - code: { - // kilocode_change end + build: { options: { custom_option: true, another_option: "value", @@ -570,21 +400,21 @@ test("agent options merge correctly", async () => { }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // kilocode_change start - renamed from "build" to "code" - const code = await load(tmp.path, (svc) => svc.get("code")) - expect(code?.options.custom_option).toBe(true) - expect(code?.options.another_option).toBe("value") - // kilocode_change end - }, - }) -}) + }, +) -test("multiple custom agents can be defined", async () => { - await using tmp = await tmpdir({ +it.instance( + "multiple custom agents can be defined", + () => + Effect.gen(function* () { + const agentA = yield* load((svc) => svc.get("agent_a")) + const agentB = yield* load((svc) => svc.get("agent_b")) + expect(agentA?.description).toBe("Agent A") + expect(agentA?.mode).toBe("subagent") + expect(agentB?.description).toBe("Agent B") + expect(agentB?.mode).toBe("primary") + }), + { config: { agent: { agent_a: { @@ -597,22 +427,18 @@ test("multiple custom agents can be defined", async () => { }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const agentA = await load(tmp.path, (svc) => svc.get("agent_a")) - const agentB = await load(tmp.path, (svc) => svc.get("agent_b")) - expect(agentA?.description).toBe("Agent A") - expect(agentA?.mode).toBe("subagent") - expect(agentB?.description).toBe("Agent B") - expect(agentB?.mode).toBe("primary") - }, - }) -}) + }, +) -test("Agent.list keeps the default agent first and sorts the rest by name", async () => { - await using tmp = await tmpdir({ +it.instance( + "Agent.list keeps the default agent first and sorts the rest by name", + () => + Effect.gen(function* () { + const names = (yield* load((svc) => svc.list())).map((a) => a.name) + expect(names[0]).toBe("plan") + expect(names.slice(1)).toEqual(names.slice(1).toSorted((a, b) => a.localeCompare(b))) + }), + { config: { default_agent: "plan", agent: { @@ -626,62 +452,43 @@ test("Agent.list keeps the default agent first and sorts the rest by name", asyn }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const names = (await load(tmp.path, (svc) => svc.list())).map((a) => a.name) - expect(names[0]).toBe("plan") - expect(names.slice(1)).toEqual(names.slice(1).toSorted((a, b) => a.localeCompare(b))) - }, - }) -}) - -test("Agent.get returns undefined for non-existent agent", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const nonExistent = await load(tmp.path, (svc) => svc.get("does_not_exist")) - expect(nonExistent).toBeUndefined() - }, - }) -}) - -test("default permission includes doom_loop and external_directory as ask", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // kilocode_change start - renamed from "build" to "code" - const code = await load(tmp.path, (svc) => svc.get("code")) - expect(evalPerm(code, "doom_loop")).toBe("ask") - expect(evalPerm(code, "external_directory")).toBe("ask") - // kilocode_change end - }, - }) -}) - -test("webfetch is allowed by default", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // kilocode_change start - renamed from "build" to "code" - const code = await load(tmp.path, (svc) => svc.get("code")) - expect(evalPerm(code, "webfetch")).toBe("allow") - // kilocode_change end - }, - }) -}) - -test("legacy tools config converts to permissions", async () => { - await using tmp = await tmpdir({ + }, +) + +it.instance("Agent.get returns undefined for non-existent agent", () => + Effect.gen(function* () { + const nonExistent = yield* load((svc) => svc.get("does_not_exist")) + expect(nonExistent).toBeUndefined() + }), +) + +it.instance("default permission includes doom_loop and external_directory as ask", () => + Effect.gen(function* () { + const build = yield* load((svc) => svc.get("build")) + expect(evalPerm(build, "doom_loop")).toBe("ask") + expect(evalPerm(build, "external_directory")).toBe("ask") + }), +) + +it.instance("webfetch is allowed by default", () => + Effect.gen(function* () { + const build = yield* load((svc) => svc.get("build")) + expect(evalPerm(build, "webfetch")).toBe("allow") + }), +) + +it.instance( + "legacy tools config converts to permissions", + () => + Effect.gen(function* () { + const build = yield* load((svc) => svc.get("build")) + expect(evalPerm(build, "bash")).toBe("deny") + expect(evalPerm(build, "read")).toBe("deny") + }), + { config: { agent: { - // kilocode_change start - renamed from "build" to "code" - code: { - // kilocode_change end + build: { tools: { bash: false, read: false, @@ -689,111 +496,88 @@ test("legacy tools config converts to permissions", async () => { }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // kilocode_change start - renamed from "build" to "code" - const code = await load(tmp.path, (svc) => svc.get("code")) - expect(evalPerm(code, "bash")).toBe("deny") - expect(evalPerm(code, "read")).toBe("deny") - // kilocode_change end - }, - }) -}) - -test("legacy tools config maps write/edit/patch to edit permission", async () => { - await using tmp = await tmpdir({ + }, +) + +it.instance( + "legacy tools config maps write/edit/patch to edit permission", + () => + Effect.gen(function* () { + const build = yield* load((svc) => svc.get("build")) + expect(evalPerm(build, "edit")).toBe("deny") + }), + { config: { agent: { - // kilocode_change start - renamed from "build" to "code" - code: { - // kilocode_change end + build: { tools: { write: false, }, }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // kilocode_change start - renamed from "build" to "code" - const code = await load(tmp.path, (svc) => svc.get("code")) - expect(evalPerm(code, "edit")).toBe("deny") - // kilocode_change end - }, - }) -}) + }, +) -test("Truncate.GLOB is allowed even when user denies external_directory globally", async () => { - const { Truncate } = await import("../../src/tool/truncate") - await using tmp = await tmpdir({ +it.instance( + "Truncate.GLOB is allowed even when user denies external_directory globally", + () => + Effect.gen(function* () { + const build = yield* load((svc) => svc.get("build")) + expect(Permission.evaluate("external_directory", Truncate.GLOB, build!.permission).action).toBe("allow") + expect(Permission.evaluate("external_directory", Truncate.DIR, build!.permission).action).toBe("deny") + expect(Permission.evaluate("external_directory", "/some/other/path", build!.permission).action).toBe("deny") + }), + { config: { permission: { external_directory: "deny", }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // kilocode_change start - renamed from "build" to "code" - const build = await load(tmp.path, (svc) => svc.get("code")) - // kilocode_change end + }, +) + +it.instance("global tmp directory children are allowed for external_directory", () => + Effect.gen(function* () { + const build = yield* load((svc) => svc.get("build")) + expect( + Permission.evaluate("external_directory", path.join(Global.Path.tmp, "scratch"), build!.permission).action, + ).toBe("allow") + expect(Permission.evaluate("external_directory", "/some/other/path", build!.permission).action).toBe("ask") + }), +) + +it.instance( + "Truncate.GLOB is allowed even when user denies external_directory per-agent", + () => + Effect.gen(function* () { + const build = yield* load((svc) => svc.get("build")) expect(Permission.evaluate("external_directory", Truncate.GLOB, build!.permission).action).toBe("allow") expect(Permission.evaluate("external_directory", Truncate.DIR, build!.permission).action).toBe("deny") expect(Permission.evaluate("external_directory", "/some/other/path", build!.permission).action).toBe("deny") - }, - }) -}) - -test("global tmp directory children are allowed for external_directory", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const build = await load(tmp.path, (svc) => svc.get("build")) - expect( - Permission.evaluate("external_directory", path.join(Global.Path.tmp, "scratch"), build!.permission).action, - ).toBe("allow") - expect(Permission.evaluate("external_directory", "/some/other/path", build!.permission).action).toBe("ask") - }, - }) -}) - -test("Truncate.GLOB is allowed even when user denies external_directory per-agent", async () => { - const { Truncate } = await import("../../src/tool/truncate") - await using tmp = await tmpdir({ + }), + { config: { agent: { - // kilocode_change start - renamed from "build" to "code" - code: { - // kilocode_change end + build: { permission: { external_directory: "deny", }, }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // kilocode_change start - renamed from "build" to "code" - const build = await load(tmp.path, (svc) => svc.get("code")) - // kilocode_change end - expect(Permission.evaluate("external_directory", Truncate.GLOB, build!.permission).action).toBe("allow") - expect(Permission.evaluate("external_directory", Truncate.DIR, build!.permission).action).toBe("deny") - expect(Permission.evaluate("external_directory", "/some/other/path", build!.permission).action).toBe("deny") - }, - }) -}) + }, +) -test("explicit Truncate.GLOB deny is respected", async () => { - const { Truncate } = await import("../../src/tool/truncate") - await using tmp = await tmpdir({ +it.instance( + "explicit Truncate.GLOB deny is respected", + () => + Effect.gen(function* () { + const build = yield* load((svc) => svc.get("build")) + expect(Permission.evaluate("external_directory", Truncate.GLOB, build!.permission).action).toBe("deny") + expect(Permission.evaluate("external_directory", Truncate.DIR, build!.permission).action).toBe("deny") + }), + { config: { permission: { external_directory: { @@ -802,83 +586,80 @@ test("explicit Truncate.GLOB deny is respected", async () => { }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // kilocode_change start - renamed from "build" to "code" - const build = await load(tmp.path, (svc) => svc.get("code")) - // kilocode_change end - expect(Permission.evaluate("external_directory", Truncate.GLOB, build!.permission).action).toBe("deny") - expect(Permission.evaluate("external_directory", Truncate.DIR, build!.permission).action).toBe("deny") - }, - }) -}) - -test("skill directories are allowed for external_directory", async () => { - await using tmp = await tmpdir({ - git: true, - init: async (dir) => { - const skillDir = path.join(dir, ".kilo", "skill", "perm-skill") // kilocode_change: .kilo is primary - await Bun.write( - path.join(skillDir, "SKILL.md"), - `--- + }, +) + +it.instance( + "skill directories are allowed for external_directory", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const skillDir = path.join(test.directory, ".opencode", "skill", "perm-skill") + yield* Effect.promise(() => + Bun.write( + path.join(skillDir, "SKILL.md"), + `--- name: perm-skill description: Permission skill. --- # Permission Skill `, + ), ) - }, - }) - const home = process.env.KILO_TEST_HOME - process.env.KILO_TEST_HOME = tmp.path - - try { - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const build = await load(tmp.path, (svc) => svc.get("build")) - const skillDir = path.join(tmp.path, ".kilo", "skill", "perm-skill") // kilocode_change: .kilo is primary - const target = path.join(skillDir, "reference", "notes.md") - expect(Permission.evaluate("external_directory", target, build!.permission).action).toBe("allow") - }, - }) - } finally { - process.env.KILO_TEST_HOME = home - } -}) - -test("defaultAgent returns build when no default_agent config", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const agent = await load(tmp.path, (svc) => svc.defaultAgent()) - expect(agent).toBe("code") // kilocode_change - }, - }) -}) + const home = process.env.OPENCODE_TEST_HOME + process.env.OPENCODE_TEST_HOME = test.directory + yield* Effect.addFinalizer(() => + Effect.sync(() => { + process.env.OPENCODE_TEST_HOME = home + }), + ) -test("defaultAgent respects default_agent config set to plan", async () => { - await using tmp = await tmpdir({ + const build = yield* load((svc) => svc.get("build")) + const target = path.join(skillDir, "reference", "notes.md") + expect(Permission.evaluate("external_directory", target, build!.permission).action).toBe("allow") + }), + { git: true }, +) + +it.instance("defaultAgent returns code when no default_agent config", () => + Effect.gen(function* () { + const agent = yield* load((svc) => svc.defaultAgent()) + expect(agent).toBe("code") + }), +) + +it.instance("defaultInfo returns resolved code agent when no default_agent config", () => + Effect.gen(function* () { + const agent = yield* load((svc) => svc.defaultInfo()) + expect(agent.name).toBe("code") + expect(agent.mode).toBe("primary") + }), +) + +it.instance( + "defaultAgent respects default_agent config set to plan", + () => + Effect.gen(function* () { + const agent = yield* load((svc) => svc.defaultAgent()) + expect(agent).toBe("plan") + }), + { config: { default_agent: "plan", }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const agent = await load(tmp.path, (svc) => svc.defaultAgent()) - expect(agent).toBe("plan") - }, - }) -}) + }, +) -test("defaultAgent respects default_agent config set to custom agent with mode all", async () => { - await using tmp = await tmpdir({ +it.instance( + "defaultAgent respects default_agent config set to custom agent with mode all", + () => + Effect.gen(function* () { + const agent = yield* load((svc) => svc.defaultAgent()) + expect(agent).toBe("my_custom") + }), + { config: { default_agent: "my_custom", agent: { @@ -887,177 +668,68 @@ test("defaultAgent respects default_agent config set to custom agent with mode a }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const agent = await load(tmp.path, (svc) => svc.defaultAgent()) - expect(agent).toBe("my_custom") - }, - }) -}) + }, +) -test("defaultAgent throws when default_agent points to subagent", async () => { - await using tmp = await tmpdir({ +it.instance( + "defaultAgent throws when default_agent points to subagent", + () => expectDefaultAgentError('default agent "explore" is a subagent'), + { config: { default_agent: "explore", }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await expect(load(tmp.path, (svc) => svc.defaultAgent())).rejects.toThrow('default agent "explore" is a subagent') - }, - }) -}) + }, +) -test("defaultAgent throws when default_agent points to hidden agent", async () => { - await using tmp = await tmpdir({ +it.instance( + "defaultAgent throws when default_agent points to hidden agent", + () => expectDefaultAgentError('default agent "compaction" is hidden'), + { config: { default_agent: "compaction", }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await expect(load(tmp.path, (svc) => svc.defaultAgent())).rejects.toThrow('default agent "compaction" is hidden') - }, - }) -}) + }, +) -test("defaultAgent throws when default_agent points to non-existent agent", async () => { - await using tmp = await tmpdir({ +it.instance( + "defaultAgent throws when default_agent points to non-existent agent", + () => expectDefaultAgentError('default agent "does_not_exist" not found'), + { config: { default_agent: "does_not_exist", }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await expect(load(tmp.path, (svc) => svc.defaultAgent())).rejects.toThrow( - 'default agent "does_not_exist" not found', - ) - }, - }) -}) + }, +) -// kilocode_change start - renamed from "build" to "code" -test("defaultAgent returns plan when code is disabled and default_agent not set", async () => { - // kilocode_change end - await using tmp = await tmpdir({ +it.instance( + "defaultAgent returns plan when build is disabled and default_agent not set", + () => + Effect.gen(function* () { + const agent = yield* load((svc) => svc.defaultAgent()) + // build is disabled, so it should return plan (next primary agent) + expect(agent).toBe("plan") + }), + { config: { agent: { - // kilocode_change start - renamed from "build" to "code" - code: { disable: true }, - // kilocode_change end + build: { disable: true }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const agent = await load(tmp.path, (svc) => svc.defaultAgent()) - expect(agent).toBe("plan") - }, - }) -}) + }, +) -test("defaultAgent throws when all primary agents are disabled", async () => { - await using tmp = await tmpdir({ +it.instance( + "defaultAgent throws when all primary agents are disabled", + () => expectDefaultAgentError("no primary visible agent found"), + { config: { agent: { - // kilocode_change start - disable all primary agents code: { disable: true }, plan: { disable: true }, debug: { disable: true }, orchestrator: { disable: true }, ask: { disable: true }, - // kilocode_change end }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await expect(load(tmp.path, (svc) => svc.defaultAgent())).rejects.toThrow("no primary visible agent found") - }, - }) -}) - -// kilocode_change start - Backward compatibility tests for "build" -> "code" rename -test("Agent.get('build') returns code agent for backward compatibility", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const [build, code] = await load(tmp.path, (svc) => - Effect.gen(function* () { - const build = yield* svc.get("build") - const code = yield* svc.get("code") - return [build, code] as const - }), - ) - expect(build).toBeDefined() - expect(build).toBe(code) - expect(build?.name).toBe("code") - }, - }) -}) - -test("agent.build config applies to code agent for backward compatibility", async () => { - await using tmp = await tmpdir({ - config: { - agent: { - build: { - temperature: 0.8, - color: "#00FF00", - }, - }, - }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const code = await load(tmp.path, (svc) => svc.get("code")) - expect(code).toBeDefined() - expect(code?.temperature).toBe(0.8) - expect(code?.color).toBe("#00FF00") - }, - }) -}) - -test("default_agent: 'build' returns code agent for backward compatibility", async () => { - await using tmp = await tmpdir({ - config: { - default_agent: "build", - }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const agent = await load(tmp.path, (svc) => svc.defaultAgent()) - expect(agent).toBe("code") - }, - }) -}) - -test("agent.build disable removes code agent for backward compatibility", async () => { - await using tmp = await tmpdir({ - config: { - agent: { - build: { disable: true }, - }, - }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const code = await load(tmp.path, (svc) => svc.get("code")) - expect(code).toBeUndefined() - const agents = await load(tmp.path, (svc) => svc.list()) - const names = agents.map((a) => a.name) - expect(names).not.toContain("code") - }, - }) -}) -// kilocode_change end + }, +) diff --git a/packages/opencode/test/agent/plan-mode-subagent-bypass.test.ts b/packages/opencode/test/agent/plan-mode-subagent-bypass.test.ts index 5ba6b54834b..641a929aeb2 100644 --- a/packages/opencode/test/agent/plan-mode-subagent-bypass.test.ts +++ b/packages/opencode/test/agent/plan-mode-subagent-bypass.test.ts @@ -18,110 +18,98 @@ * permissions are passed through, and Plan Mode's restrictions live on the * agent, not the session. */ -import { test, expect, afterEach } from "bun:test" +import { expect } from "bun:test" import { Effect } from "effect" -import { disposeAllInstances, provideInstance, tmpdir } from "../fixture/fixture" -import { WithInstance } from "../../src/project/with-instance" import { Agent } from "../../src/agent/agent" import { deriveSubagentSessionPermission } from "../../src/agent/subagent-permissions" import { Permission } from "../../src/permission" +import { testEffect } from "../lib/effect" -afterEach(async () => { - await disposeAllInstances() -}) +const it = testEffect(Agent.defaultLayer) -function load(dir: string, fn: (svc: Agent.Interface) => Effect.Effect) { - return Effect.runPromise(provideInstance(dir)(Agent.Service.use(fn)).pipe(Effect.provide(Agent.defaultLayer))) +function testAgent(input: { + name: string + mode: Agent.Info["mode"] + permission: Parameters[0] +}) { + return { + name: input.name, + mode: input.mode, + permission: Permission.fromConfig(input.permission), + options: {}, + } satisfies Agent.Info } // `deriveSubagentSessionPermission` is imported from production. The test // exercises the actual helper that task.ts uses to build the subagent's // session permission, so any regression in that helper trips this test. -test("[#26514] subagent spawned from plan mode inherits read-only restriction (edit denied)", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const planAgent = await load(tmp.path, (svc) => svc.get("plan")) - const generalAgent = await load(tmp.path, (svc) => svc.get("general")) +it.instance("[#26514] subagent spawned from plan mode inherits read-only restriction (edit denied)", () => + Effect.gen(function* () { + const planAgent = yield* Agent.Service.use((svc) => svc.get("plan")) + const generalAgent = yield* Agent.Service.use((svc) => svc.get("general")) - expect(planAgent).toBeDefined() - expect(generalAgent).toBeDefined() - // Sanity: the plan agent itself blocks edit. (Note: `write` and - // `apply_patch` route through the `edit` permission at the runtime - // tool layer — see Permission.disabled / EDIT_TOOLS.) - expect(Permission.evaluate("edit", "/some/file.ts", planAgent!.permission).action).toBe("deny") - - // Simulate the plan-mode parent session: in real flow the plan - // session's `permission` field is empty (Plan Mode lives on the agent - // ruleset, not the session). So we pass [] through as the parent - // session permission, exactly like the actual code path. - const parentSessionPermission: Permission.Ruleset = [] + expect(planAgent).toBeDefined() + expect(generalAgent).toBeDefined() + // Sanity: the plan agent itself blocks edit. (Note: `write` and + // `apply_patch` route through the `edit` permission at the runtime + // tool layer — see Permission.disabled / EDIT_TOOLS.) + expect(Permission.evaluate("edit", "/some/file.ts", planAgent!.permission).action).toBe("deny") - const subagentSessionPermission = deriveSubagentSessionPermission({ - parentSessionPermission, - parentAgent: planAgent, - subagent: generalAgent!, - }) + // Simulate the plan-mode parent session: in real flow the plan + // session's `permission` field is empty (Plan Mode lives on the agent + // ruleset, not the session). So we pass [] through as the parent + // session permission, exactly like the actual code path. + const parentSessionPermission: Permission.Ruleset = [] - // Mirror the runtime evaluation in session/prompt.ts (~line 410, 639): - // ruleset: Permission.merge(agent.permission, session.permission ?? []) - const effective = Permission.merge(generalAgent!.permission, subagentSessionPermission) + const subagentSessionPermission = deriveSubagentSessionPermission({ + parentSessionPermission, + parentAgent: planAgent, + subagent: generalAgent!, + }) - expect(Permission.evaluate("edit", "/some/file.ts", effective).action).toBe("deny") - expect(Permission.evaluate("edit", "/another/path/index.tsx", effective).action).toBe("deny") - }, - }) -}) + // Mirror the runtime evaluation in session/prompt.ts (~line 410, 639): + // ruleset: Permission.merge(agent.permission, session.permission ?? []) + const effective = Permission.merge(generalAgent!.permission, subagentSessionPermission) + + expect(Permission.evaluate("edit", "/some/file.ts", effective).action).toBe("deny") + expect(Permission.evaluate("edit", "/another/path/index.tsx", effective).action).toBe("deny") + }), +) -test("[#26514] explore subagent launched from plan mode also stays read-only", async () => { +it.instance("[#26514] explore subagent launched from plan mode also stays read-only", () => // Sibling check: even though `explore` is intrinsically read-only, the // bug surface is the same. Including this case to document that the fix // should propagate the parent **agent** permissions, not just deny edit // when the subagent happens to already deny it. - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const planAgent = await load(tmp.path, (svc) => svc.get("plan")) - const explore = await load(tmp.path, (svc) => svc.get("explore")) - expect(planAgent).toBeDefined() - expect(explore).toBeDefined() + Effect.gen(function* () { + const planAgent = yield* Agent.Service.use((svc) => svc.get("plan")) + const explore = yield* Agent.Service.use((svc) => svc.get("explore")) + expect(planAgent).toBeDefined() + expect(explore).toBeDefined() - const parentSessionPermission: Permission.Ruleset = [] - const subagentSessionPermission = deriveSubagentSessionPermission({ - parentSessionPermission, - parentAgent: planAgent, - subagent: explore!, - }) - const effective = Permission.merge(explore!.permission, subagentSessionPermission) + const parentSessionPermission: Permission.Ruleset = [] + const subagentSessionPermission = deriveSubagentSessionPermission({ + parentSessionPermission, + parentAgent: planAgent, + subagent: explore!, + }) + const effective = Permission.merge(explore!.permission, subagentSessionPermission) - // Already deny — sanity check. - expect(Permission.evaluate("edit", "/x.ts", effective).action).toBe("deny") - }, - }) -}) + // Already deny — sanity check. + expect(Permission.evaluate("edit", "/x.ts", effective).action).toBe("deny") + }), +) -test("[#26514] custom user subagent launched from plan mode bypasses Plan Mode read-only", async () => { +it.instance( + "[#26514] custom user subagent launched from plan mode bypasses Plan Mode read-only", // The most damaging case: a user-defined subagent with default // permissions (allow-by-default, like `general`). The subagent must NOT // be able to edit when the parent agent is `plan`. - await using tmp = await tmpdir({ - config: { - agent: { - my_subagent: { - description: "A user-defined subagent", - mode: "subagent", - }, - }, - }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const planAgent = await load(tmp.path, (svc) => svc.get("plan")) - const my = await load(tmp.path, (svc) => svc.get("my_subagent")) + () => + Effect.gen(function* () { + const planAgent = yield* Agent.Service.use((svc) => svc.get("plan")) + const my = yield* Agent.Service.use((svc) => svc.get("my_subagent")) expect(planAgent).toBeDefined() expect(my).toBeDefined() @@ -136,6 +124,89 @@ test("[#26514] custom user subagent launched from plan mode bypasses Plan Mode r // BUG: on origin/dev edit resolves to "allow" because the plan // agent's `edit: deny *` rule never reaches the subagent. expect(Permission.evaluate("edit", "/some/file.ts", effective).action).toBe("deny") + }), + { + config: { + agent: { + my_subagent: { + description: "A user-defined subagent", + mode: "subagent", + }, + }, }, - }) -}) + }, +) + +it.effect("[#26700] controller self-restrictions do not erase executor permissions", () => + Effect.sync(() => { + const controller = testAgent({ + name: "controller", + mode: "primary", + permission: { + "*": "deny", + read: "deny", + bash: "deny", + task: { + "*": "deny", + executor: "allow", + }, + edit: "deny", + write: "deny", + }, + }) + const executor = testAgent({ + name: "executor", + mode: "subagent", + permission: { + "*": "deny", + read: "allow", + bash: "allow", + task: { + "*": "deny", + worker: "allow", + }, + edit: "deny", + write: "deny", + }, + }) + + const effective = Permission.merge( + executor.permission, + deriveSubagentSessionPermission({ + parentSessionPermission: [], + parentAgent: controller, + subagent: executor, + }), + ) + + expect(Permission.evaluate("read", "README.md", effective).action).toBe("allow") + expect(Permission.evaluate("bash", "git status", effective).action).toBe("allow") + expect(Permission.evaluate("task", "worker", effective).action).toBe("allow") + expect(Permission.evaluate("task", "other", effective).action).toBe("deny") + expect(Permission.disabled(["edit", "write", "apply_patch"], effective)).toEqual( + new Set(["edit", "write", "apply_patch"]), + ) + }), +) + +it.effect("subagent inherits parent session deny rules as hard runtime ceilings", () => + Effect.sync(() => { + const executor = testAgent({ + name: "executor", + mode: "subagent", + permission: { + bash: "allow", + }, + }) + const effective = Permission.merge( + executor.permission, + deriveSubagentSessionPermission({ + parentSessionPermission: Permission.fromConfig({ bash: "deny" }), + parentAgent: undefined, + subagent: executor, + }), + ) + + expect(Permission.evaluate("bash", "git status", effective).action).toBe("deny") + }), +) diff --git a/packages/opencode/test/agent/plugin-agent-regression.test.ts b/packages/opencode/test/agent/plugin-agent-regression.test.ts index dff972d100e..ef173d073b8 100644 --- a/packages/opencode/test/agent/plugin-agent-regression.test.ts +++ b/packages/opencode/test/agent/plugin-agent-regression.test.ts @@ -1,9 +1,19 @@ import { expect } from "bun:test" +import { AppFileSystem } from "@opencode-ai/core/filesystem" import { Effect, Layer } from "effect" import path from "path" import { pathToFileURL } from "url" import { Agent } from "../../src/agent/agent" +import { Bus } from "../../src/bus" +import { Config } from "../../src/config/config" +import { Env } from "../../src/env" +import { RuntimeFlags } from "../../src/effect/runtime-flags" import { Plugin } from "../../src/plugin" +import { AccountTest } from "../fake/account" +import { AuthTest } from "../fake/auth" +import { NpmTest } from "../fake/npm" +import { ProviderTest } from "../fake/provider" +import { SkillTest } from "../fake/skill" import { testEffect } from "../lib/effect" import { PLUGIN_AGENT } from "../fixture/agent-plugin.constants" @@ -12,7 +22,29 @@ import { PLUGIN_AGENT } from "../fixture/agent-plugin.constants" // to verify plugin → config hook → Agent.list. const pluginUrl = pathToFileURL(path.join(import.meta.dir, "..", "fixture", "agent-plugin.ts")).href -const it = testEffect(Layer.mergeAll(Agent.defaultLayer, Plugin.defaultLayer)) +const provider = ProviderTest.fake() +const configLayer = Config.layer.pipe( + Layer.provide(RuntimeFlags.layer({ disableDefaultPlugins: true })), + Layer.provide(AppFileSystem.defaultLayer), + Layer.provide(Env.defaultLayer), + Layer.provide(AuthTest.empty), + Layer.provide(AccountTest.empty), + Layer.provide(NpmTest.noop), +) +const pluginLayer = Plugin.layer.pipe( + Layer.provide(Bus.layer), + Layer.provide(RuntimeFlags.layer({ disableDefaultPlugins: true })), +) +const dependencies = Layer.mergeAll(configLayer, pluginLayer).pipe(Layer.provideMerge(configLayer)) +const agentLayer = Agent.layer.pipe( + Layer.provide(AuthTest.empty), + Layer.provide(SkillTest.empty), + Layer.provide(provider.layer), + Layer.provide(RuntimeFlags.layer({ disableDefaultPlugins: true })), +) +const layer = Layer.mergeAll(agentLayer, dependencies).pipe(Layer.provideMerge(dependencies)) + +const it = testEffect(layer) it.instance( "plugin-registered agents appear in Agent.list", diff --git a/packages/opencode/test/background/job.test.ts b/packages/opencode/test/background/job.test.ts new file mode 100644 index 00000000000..afc7260bb82 --- /dev/null +++ b/packages/opencode/test/background/job.test.ts @@ -0,0 +1,127 @@ +import { describe, expect } from "bun:test" +import { Deferred, Effect } from "effect" +import { BackgroundJob } from "@/background/job" +import { testEffect } from "../lib/effect" + +const it = testEffect(BackgroundJob.defaultLayer) + +describe("background.job", () => { + it.instance("tracks started jobs through completion", () => + Effect.gen(function* () { + const jobs = yield* BackgroundJob.Service + const latch = yield* Deferred.make() + const job = yield* jobs.start({ + type: "test", + title: "test job", + run: Deferred.await(latch).pipe(Effect.as("done")), + }) + + expect(job.id.startsWith("job_")).toBe(true) + expect(job.status).toBe("running") + expect(job.title).toBe("test job") + + yield* Deferred.succeed(latch, undefined) + const done = yield* jobs.wait({ id: job.id }) + + expect(done.timedOut).toBe(false) + expect(done.info?.status).toBe("completed") + expect(done.info?.output).toBe("done") + expect((yield* jobs.list()).map((item) => item.id)).toEqual([job.id]) + }), + ) + + it.instance("returns a running snapshot when wait times out", () => + Effect.gen(function* () { + const jobs = yield* BackgroundJob.Service + const job = yield* jobs.start({ + type: "test", + run: Effect.never, + }) + + const result = yield* jobs.wait({ id: job.id, timeout: 1 }) + + expect(result.timedOut).toBe(true) + expect(result.info?.status).toBe("running") + }), + ) + + it.instance("deduplicates concurrent starts for a running id", () => + Effect.gen(function* () { + const jobs = yield* BackgroundJob.Service + const started = yield* Deferred.make() + const id = "job_test" + const [first, second] = yield* Effect.all( + [ + jobs.start({ + id, + type: "test", + run: Deferred.succeed(started, undefined).pipe(Effect.andThen(Effect.never)), + }), + jobs.start({ + id, + type: "test", + run: Effect.fail(new Error("duplicate started")), + }), + ], + { concurrency: "unbounded" }, + ) + + yield* Deferred.await(started) + + expect(first.id).toBe(id) + expect(second.id).toBe(id) + expect(first.status).toBe("running") + expect(second.status).toBe("running") + expect((yield* jobs.list()).map((item) => item.id)).toEqual([id]) + + yield* jobs.cancel(id) + }), + ) + + it.instance("records failed jobs", () => + Effect.gen(function* () { + const jobs = yield* BackgroundJob.Service + const job = yield* jobs.start({ + type: "test", + run: Effect.fail(new Error("boom")), + }) + + const result = yield* jobs.wait({ id: job.id }) + + expect(result.info?.status).toBe("error") + expect(result.info?.error).toBe("boom") + }), + ) + + it.instance("can cancel running jobs", () => + Effect.gen(function* () { + const jobs = yield* BackgroundJob.Service + const interrupted = yield* Deferred.make() + const job = yield* jobs.start({ + type: "test", + run: Effect.never.pipe(Effect.ensuring(Deferred.succeed(interrupted, undefined))), + }) + + const cancelled = yield* jobs.cancel(job.id) + + expect(cancelled?.status).toBe("cancelled") + yield* Deferred.await(interrupted).pipe(Effect.timeout("1 second")) + expect((yield* jobs.get(job.id))?.status).toBe("cancelled") + }), + ) + + it.instance("returns immutable snapshots", () => + Effect.gen(function* () { + const jobs = yield* BackgroundJob.Service + const job = yield* jobs.start({ + type: "test", + metadata: { value: "initial" }, + run: Effect.succeed("done"), + }) + + if (job.metadata) job.metadata.value = "changed" + + expect((yield* jobs.get(job.id))?.metadata?.value).toBe("initial") + }), + ) +}) diff --git a/packages/opencode/test/bus/bus-effect.test.ts b/packages/opencode/test/bus/bus-effect.test.ts index 377c5410969..0dc58ecdae5 100644 --- a/packages/opencode/test/bus/bus-effect.test.ts +++ b/packages/opencode/test/bus/bus-effect.test.ts @@ -1,5 +1,5 @@ import { describe, expect } from "bun:test" -import { Deferred, Effect, Layer, Schema, Stream } from "effect" +import { Deferred, Effect, Fiber, Latch, Layer, Schema, Stream } from "effect" import { Bus } from "../../src/bus" import { BusEvent } from "../../src/bus/bus-event" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" @@ -9,6 +9,7 @@ import { testEffect } from "../lib/effect" const TestEvent = { Ping: BusEvent.define("test.effect.ping", Schema.Struct({ value: Schema.Number })), Pong: BusEvent.define("test.effect.pong", Schema.Struct({ message: Schema.String })), + Warmup: BusEvent.define("test.effect.warmup", Schema.Struct({})), } const node = CrossSpawnSpawner.defaultLayer @@ -17,21 +18,44 @@ const live = Layer.mergeAll(Bus.layer, node) const it = testEffect(live) +// Publishes warmup events until the latch opens, proving the forked subscriber +// fiber has actually wired up its PubSub subscription. +const awaitSubscriberReady = Effect.fn("test.awaitSubscriberReady")(function* ( + ready: Latch.Latch, + warmup: Effect.Effect, +) { + const pump = yield* Effect.forkScoped( + Effect.gen(function* () { + while (true) { + yield* warmup + yield* Effect.sleep("5 millis") + } + }), + ) + yield* ready.await.pipe(Effect.timeout("2 seconds")) + yield* Fiber.interrupt(pump) +}) + describe("Bus (Effect-native)", () => { it.instance("publish + subscribe stream delivers events", () => Effect.gen(function* () { const bus = yield* Bus.Service const received: number[] = [] const done = yield* Deferred.make() + const ready = yield* Latch.make() yield* Stream.runForEach(bus.subscribe(TestEvent.Ping), (evt) => - Effect.sync(() => { + Effect.gen(function* () { + if (evt.properties.value < 0) { + yield* ready.open + return + } received.push(evt.properties.value) if (received.length === 2) Deferred.doneUnsafe(done, Effect.void) }), ).pipe(Effect.forkScoped) - yield* Effect.sleep("10 millis") + yield* awaitSubscriberReady(ready, bus.publish(TestEvent.Ping, { value: -1 })) yield* bus.publish(TestEvent.Ping, { value: 1 }) yield* bus.publish(TestEvent.Ping, { value: 2 }) yield* Deferred.await(done) @@ -45,15 +69,20 @@ describe("Bus (Effect-native)", () => { const bus = yield* Bus.Service const pings: number[] = [] const done = yield* Deferred.make() + const ready = yield* Latch.make() yield* Stream.runForEach(bus.subscribe(TestEvent.Ping), (evt) => - Effect.sync(() => { + Effect.gen(function* () { + if (evt.properties.value < 0) { + yield* ready.open + return + } pings.push(evt.properties.value) Deferred.doneUnsafe(done, Effect.void) }), ).pipe(Effect.forkScoped) - yield* Effect.sleep("10 millis") + yield* awaitSubscriberReady(ready, bus.publish(TestEvent.Ping, { value: -1 })) yield* bus.publish(TestEvent.Pong, { message: "ignored" }) yield* bus.publish(TestEvent.Ping, { value: 42 }) yield* Deferred.await(done) @@ -67,15 +96,20 @@ describe("Bus (Effect-native)", () => { const bus = yield* Bus.Service const types: string[] = [] const done = yield* Deferred.make() + const ready = yield* Latch.make() yield* Stream.runForEach(bus.subscribeAll(), (evt) => - Effect.sync(() => { + Effect.gen(function* () { + if (evt.type === TestEvent.Warmup.type) { + yield* ready.open + return + } types.push(evt.type) if (types.length === 2) Deferred.doneUnsafe(done, Effect.void) }), ).pipe(Effect.forkScoped) - yield* Effect.sleep("10 millis") + yield* awaitSubscriberReady(ready, bus.publish(TestEvent.Warmup, {})) yield* bus.publish(TestEvent.Ping, { value: 1 }) yield* bus.publish(TestEvent.Pong, { message: "hi" }) yield* Deferred.await(done) @@ -92,22 +126,33 @@ describe("Bus (Effect-native)", () => { const b: number[] = [] const doneA = yield* Deferred.make() const doneB = yield* Deferred.make() + const readyA = yield* Latch.make() + const readyB = yield* Latch.make() yield* Stream.runForEach(bus.subscribe(TestEvent.Ping), (evt) => - Effect.sync(() => { + Effect.gen(function* () { + if (evt.properties.value < 0) { + yield* readyA.open + return + } a.push(evt.properties.value) Deferred.doneUnsafe(doneA, Effect.void) }), ).pipe(Effect.forkScoped) yield* Stream.runForEach(bus.subscribe(TestEvent.Ping), (evt) => - Effect.sync(() => { + Effect.gen(function* () { + if (evt.properties.value < 0) { + yield* readyB.open + return + } b.push(evt.properties.value) Deferred.doneUnsafe(doneB, Effect.void) }), ).pipe(Effect.forkScoped) - yield* Effect.sleep("10 millis") + yield* awaitSubscriberReady(readyA, bus.publish(TestEvent.Ping, { value: -1 })) + yield* awaitSubscriberReady(readyB, bus.publish(TestEvent.Ping, { value: -1 })) yield* bus.publish(TestEvent.Ping, { value: 99 }) yield* Deferred.await(doneA) yield* Deferred.await(doneB) @@ -123,20 +168,25 @@ describe("Bus (Effect-native)", () => { const types: string[] = [] const seen = yield* Deferred.make() const disposed = yield* Deferred.make() + const ready = yield* Latch.make() // Set up subscriber inside the instance yield* Effect.gen(function* () { const bus = yield* Bus.Service yield* Stream.runForEach(bus.subscribeAll(), (evt) => - Effect.sync(() => { + Effect.gen(function* () { + if (evt.type === TestEvent.Warmup.type) { + yield* ready.open + return + } types.push(evt.type) if (evt.type === TestEvent.Ping.type) Deferred.doneUnsafe(seen, Effect.void) if (evt.type === Bus.InstanceDisposed.type) Deferred.doneUnsafe(disposed, Effect.void) }), ).pipe(Effect.forkScoped) - yield* Effect.sleep("10 millis") + yield* awaitSubscriberReady(ready, bus.publish(TestEvent.Warmup, {})) yield* bus.publish(TestEvent.Ping, { value: 1 }) yield* Deferred.await(seen) }).pipe(provideInstance(dir)) diff --git a/packages/opencode/test/bus/bus-integration.test.ts b/packages/opencode/test/bus/bus-integration.test.ts index 18524b4d61a..645a94fb3b6 100644 --- a/packages/opencode/test/bus/bus-integration.test.ts +++ b/packages/opencode/test/bus/bus-integration.test.ts @@ -1,87 +1,88 @@ -import { afterEach, describe, expect, test } from "bun:test" -import { Schema } from "effect" +import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" +import { afterEach, describe, expect } from "bun:test" +import { Deferred, Effect, Layer, Schema } from "effect" import { Bus } from "../../src/bus" import { BusEvent } from "../../src/bus/bus-event" -import { WithInstance } from "../../src/project/with-instance" -import { disposeAllInstances, tmpdir } from "../fixture/fixture" +import { disposeAllInstances, provideInstance, tmpdirScoped } from "../fixture/fixture" +import { testEffect } from "../lib/effect" const TestEvent = BusEvent.define("test.integration", Schema.Struct({ value: Schema.Number })) - -function withInstance(directory: string, fn: () => Promise) { - return WithInstance.provide({ directory, fn }) -} +const it = testEffect(Layer.mergeAll(Bus.layer, CrossSpawnSpawner.defaultLayer)) describe("Bus integration: acquireRelease subscriber pattern", () => { afterEach(() => disposeAllInstances()) - test("subscriber via callback facade receives events and cleans up on unsub", async () => { - await using tmp = await tmpdir() - const received: number[] = [] + it.instance("subscriber via callback facade receives events and cleans up on unsub", () => + Effect.gen(function* () { + const bus = yield* Bus.Service + const received: number[] = [] + const receivedTwo = yield* Deferred.make() - await withInstance(tmp.path, async () => { - const unsub = Bus.subscribe(TestEvent, (evt) => { + const unsub = yield* bus.subscribeCallback(TestEvent, (evt) => { received.push(evt.properties.value) + if (received.length === 2) Deferred.doneUnsafe(receivedTwo, Effect.void) }) - await Bun.sleep(10) - await Bus.publish(TestEvent, { value: 1 }) - await Bus.publish(TestEvent, { value: 2 }) - await Bun.sleep(10) + yield* bus.publish(TestEvent, { value: 1 }) + yield* bus.publish(TestEvent, { value: 2 }) + yield* Deferred.await(receivedTwo).pipe(Effect.timeout("2 seconds")) expect(received).toEqual([1, 2]) - unsub() - await Bun.sleep(10) - await Bus.publish(TestEvent, { value: 3 }) - await Bun.sleep(10) + yield* Effect.sync(unsub) + yield* bus.publish(TestEvent, { value: 3 }) + yield* Effect.sleep("10 millis") expect(received).toEqual([1, 2]) - }) - }) - - test("subscribeAll receives events from multiple types", async () => { - await using tmp = await tmpdir() - const received: Array<{ type: string; value?: number }> = [] + }), + ) - const OtherEvent = BusEvent.define("test.other", Schema.Struct({ value: Schema.Number })) + it.instance("subscribeAll receives events from multiple types", () => + Effect.gen(function* () { + const bus = yield* Bus.Service + const received: Array<{ type: string; value?: number }> = [] + const OtherEvent = BusEvent.define("test.other", Schema.Struct({ value: Schema.Number })) + const receivedTwo = yield* Deferred.make() - await withInstance(tmp.path, async () => { - Bus.subscribeAll((evt) => { + yield* bus.subscribeAllCallback((evt) => { received.push({ type: evt.type, value: evt.properties.value }) + if (received.length === 2) Deferred.doneUnsafe(receivedTwo, Effect.void) }) - await Bun.sleep(10) - await Bus.publish(TestEvent, { value: 10 }) - await Bus.publish(OtherEvent, { value: 20 }) - await Bun.sleep(10) - }) - - expect(received).toEqual([ - { type: "test.integration", value: 10 }, - { type: "test.other", value: 20 }, - ]) - }) - - test("subscriber cleanup on instance disposal interrupts the stream", async () => { - await using tmp = await tmpdir() - const received: number[] = [] - let disposed = false - - await withInstance(tmp.path, async () => { - Bus.subscribeAll((evt) => { - if (evt.type === Bus.InstanceDisposed.type) { - disposed = true - return - } - received.push(evt.properties.value) - }) - await Bun.sleep(10) - await Bus.publish(TestEvent, { value: 1 }) - await Bun.sleep(10) - }) - - await disposeAllInstances() - await Bun.sleep(50) - - expect(received).toEqual([1]) - expect(disposed).toBe(true) - }) + yield* bus.publish(TestEvent, { value: 10 }) + yield* bus.publish(OtherEvent, { value: 20 }) + yield* Deferred.await(receivedTwo).pipe(Effect.timeout("2 seconds")) + + expect(received).toEqual([ + { type: "test.integration", value: 10 }, + { type: "test.other", value: 20 }, + ]) + }), + ) + + it.live("subscriber cleanup on instance disposal interrupts the stream", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + const received: number[] = [] + const seen = yield* Deferred.make() + const disposed = yield* Deferred.make() + + yield* Effect.gen(function* () { + const bus = yield* Bus.Service + yield* bus.subscribeAllCallback((evt) => { + if (evt.type === Bus.InstanceDisposed.type) { + Deferred.doneUnsafe(disposed, Effect.void) + return + } + received.push(evt.properties.value) + Deferred.doneUnsafe(seen, Effect.void) + }) + yield* bus.publish(TestEvent, { value: 1 }) + yield* Deferred.await(seen).pipe(Effect.timeout("2 seconds")) + }).pipe(provideInstance(dir)) + + yield* Effect.promise(() => disposeAllInstances()) + yield* Deferred.await(disposed).pipe(Effect.timeout("2 seconds")) + + expect(received).toEqual([1]) + }), + ) }) diff --git a/packages/opencode/test/bus/bus.test.ts b/packages/opencode/test/bus/bus.test.ts index 2abd6c26b90..08449861621 100644 --- a/packages/opencode/test/bus/bus.test.ts +++ b/packages/opencode/test/bus/bus.test.ts @@ -1,219 +1,240 @@ -import { afterEach, describe, expect, test } from "bun:test" -import { Schema } from "effect" +import { afterEach, describe, expect } from "bun:test" +import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" +import { Deferred, Effect, Layer, Schema } from "effect" import { Bus } from "../../src/bus" import { BusEvent } from "../../src/bus/bus-event" -import { WithInstance } from "../../src/project/with-instance" -import { disposeAllInstances, tmpdir } from "../fixture/fixture" +import { disposeAllInstances, provideInstance, tmpdirScoped } from "../fixture/fixture" +import { testEffect } from "../lib/effect" const TestEvent = { Ping: BusEvent.define("test.ping", Schema.Struct({ value: Schema.Number })), Pong: BusEvent.define("test.pong", Schema.Struct({ message: Schema.String })), } -function withInstance(directory: string, fn: () => Promise) { - return WithInstance.provide({ directory, fn }) -} +const it = testEffect(Layer.mergeAll(Bus.layer, CrossSpawnSpawner.defaultLayer)) describe("Bus", () => { afterEach(() => disposeAllInstances()) describe("publish + subscribe", () => { - test("subscriber is live immediately after subscribe returns", async () => { - await using tmp = await tmpdir() - const received: number[] = [] + it.instance("subscriber is live immediately after subscribe returns", () => + Effect.gen(function* () { + const bus = yield* Bus.Service + const received: number[] = [] + const done = yield* Deferred.make() - await withInstance(tmp.path, async () => { - Bus.subscribe(TestEvent.Ping, (evt) => { + yield* bus.subscribeCallback(TestEvent.Ping, (evt) => { received.push(evt.properties.value) + Deferred.doneUnsafe(done, Effect.void) }) - await Bus.publish(TestEvent.Ping, { value: 42 }) - await Bun.sleep(10) - }) + yield* bus.publish(TestEvent.Ping, { value: 42 }) + yield* Deferred.await(done).pipe(Effect.timeout("2 seconds")) - expect(received).toEqual([42]) - }) + expect(received).toEqual([42]) + }), + ) - test("subscriber receives matching events", async () => { - await using tmp = await tmpdir() - const received: number[] = [] + it.instance("subscriber receives matching events", () => + Effect.gen(function* () { + const bus = yield* Bus.Service + const received: number[] = [] + const done = yield* Deferred.make() - await withInstance(tmp.path, async () => { - Bus.subscribe(TestEvent.Ping, (evt) => { + yield* bus.subscribeCallback(TestEvent.Ping, (evt) => { received.push(evt.properties.value) + if (received.length === 2) Deferred.doneUnsafe(done, Effect.void) }) - // Give the subscriber fiber time to start consuming - await Bun.sleep(10) - await Bus.publish(TestEvent.Ping, { value: 42 }) - await Bus.publish(TestEvent.Ping, { value: 99 }) - // Give subscriber time to process - await Bun.sleep(10) - }) - - expect(received).toEqual([42, 99]) - }) - - test("subscriber does not receive events of other types", async () => { - await using tmp = await tmpdir() - const pings: number[] = [] - - await withInstance(tmp.path, async () => { - Bus.subscribe(TestEvent.Ping, (evt) => { + yield* bus.publish(TestEvent.Ping, { value: 42 }) + yield* bus.publish(TestEvent.Ping, { value: 99 }) + yield* Deferred.await(done).pipe(Effect.timeout("2 seconds")) + + expect(received).toEqual([42, 99]) + }), + ) + + it.instance("subscriber does not receive events of other types", () => + Effect.gen(function* () { + const bus = yield* Bus.Service + const pings: number[] = [] + const done = yield* Deferred.make() + + yield* bus.subscribeCallback(TestEvent.Ping, (evt) => { pings.push(evt.properties.value) + Deferred.doneUnsafe(done, Effect.void) }) - await Bun.sleep(10) - await Bus.publish(TestEvent.Pong, { message: "hello" }) - await Bus.publish(TestEvent.Ping, { value: 1 }) - await Bun.sleep(10) - }) - - expect(pings).toEqual([1]) - }) - - test("publish with no subscribers does not throw", async () => { - await using tmp = await tmpdir() - - await withInstance(tmp.path, async () => { - await Bus.publish(TestEvent.Ping, { value: 1 }) - }) - }) + yield* bus.publish(TestEvent.Pong, { message: "hello" }) + yield* bus.publish(TestEvent.Ping, { value: 1 }) + yield* Deferred.await(done).pipe(Effect.timeout("2 seconds")) + + expect(pings).toEqual([1]) + }), + ) + + it.instance("publish with no subscribers does not throw", () => + Effect.gen(function* () { + const bus = yield* Bus.Service + yield* bus.publish(TestEvent.Ping, { value: 1 }) + }), + ) }) describe("unsubscribe", () => { - test("unsubscribe stops delivery", async () => { - await using tmp = await tmpdir() - const received: number[] = [] + it.instance("unsubscribe stops delivery", () => + Effect.gen(function* () { + const bus = yield* Bus.Service + const received: number[] = [] + const first = yield* Deferred.make() - await withInstance(tmp.path, async () => { - const unsub = Bus.subscribe(TestEvent.Ping, (evt) => { + const unsub = yield* bus.subscribeCallback(TestEvent.Ping, (evt) => { received.push(evt.properties.value) + if (evt.properties.value === 1) Deferred.doneUnsafe(first, Effect.void) }) - await Bun.sleep(10) - await Bus.publish(TestEvent.Ping, { value: 1 }) - await Bun.sleep(10) - unsub() - await Bun.sleep(10) - await Bus.publish(TestEvent.Ping, { value: 2 }) - await Bun.sleep(10) - }) - - expect(received).toEqual([1]) - }) + yield* bus.publish(TestEvent.Ping, { value: 1 }) + yield* Deferred.await(first).pipe(Effect.timeout("2 seconds")) + yield* Effect.sync(unsub) + yield* bus.publish(TestEvent.Ping, { value: 2 }) + yield* Effect.sleep("10 millis") + + expect(received).toEqual([1]) + }), + ) }) describe("subscribeAll", () => { - test("subscribeAll is live immediately after subscribe returns", async () => { - await using tmp = await tmpdir() - const received: string[] = [] + it.instance("subscribeAll is live immediately after subscribe returns", () => + Effect.gen(function* () { + const bus = yield* Bus.Service + const received: string[] = [] + const done = yield* Deferred.make() - await withInstance(tmp.path, async () => { - Bus.subscribeAll((evt) => { + yield* bus.subscribeAllCallback((evt) => { received.push(evt.type) + Deferred.doneUnsafe(done, Effect.void) }) - await Bus.publish(TestEvent.Ping, { value: 1 }) - await Bun.sleep(10) - }) + yield* bus.publish(TestEvent.Ping, { value: 1 }) + yield* Deferred.await(done).pipe(Effect.timeout("2 seconds")) - expect(received).toEqual(["test.ping"]) - }) + expect(received).toEqual(["test.ping"]) + }), + ) - test("receives all event types", async () => { - await using tmp = await tmpdir() - const received: string[] = [] + it.instance("receives all event types", () => + Effect.gen(function* () { + const bus = yield* Bus.Service + const received: string[] = [] + const done = yield* Deferred.make() - await withInstance(tmp.path, async () => { - Bus.subscribeAll((evt) => { + yield* bus.subscribeAllCallback((evt) => { received.push(evt.type) + if (received.length === 2) Deferred.doneUnsafe(done, Effect.void) }) - await Bun.sleep(10) - await Bus.publish(TestEvent.Ping, { value: 1 }) - await Bus.publish(TestEvent.Pong, { message: "hi" }) - await Bun.sleep(10) - }) - - expect(received).toContain("test.ping") - expect(received).toContain("test.pong") - }) + yield* bus.publish(TestEvent.Ping, { value: 1 }) + yield* bus.publish(TestEvent.Pong, { message: "hi" }) + yield* Deferred.await(done).pipe(Effect.timeout("2 seconds")) + + expect(received).toContain("test.ping") + expect(received).toContain("test.pong") + }), + ) }) describe("multiple subscribers", () => { - test("all subscribers for same event type are called", async () => { - await using tmp = await tmpdir() - const a: number[] = [] - const b: number[] = [] - - await withInstance(tmp.path, async () => { - Bus.subscribe(TestEvent.Ping, (evt) => { + it.instance("all subscribers for same event type are called", () => + Effect.gen(function* () { + const bus = yield* Bus.Service + const a: number[] = [] + const b: number[] = [] + const doneA = yield* Deferred.make() + const doneB = yield* Deferred.make() + + yield* bus.subscribeCallback(TestEvent.Ping, (evt) => { a.push(evt.properties.value) + Deferred.doneUnsafe(doneA, Effect.void) }) - Bus.subscribe(TestEvent.Ping, (evt) => { + yield* bus.subscribeCallback(TestEvent.Ping, (evt) => { b.push(evt.properties.value) + Deferred.doneUnsafe(doneB, Effect.void) }) - await Bun.sleep(10) - await Bus.publish(TestEvent.Ping, { value: 7 }) - await Bun.sleep(10) - }) - - expect(a).toEqual([7]) - expect(b).toEqual([7]) - }) + yield* bus.publish(TestEvent.Ping, { value: 7 }) + yield* Deferred.await(doneA).pipe(Effect.timeout("2 seconds")) + yield* Deferred.await(doneB).pipe(Effect.timeout("2 seconds")) + + expect(a).toEqual([7]) + expect(b).toEqual([7]) + }), + ) }) describe("instance isolation", () => { - test("events in one directory do not reach subscribers in another", async () => { - await using tmpA = await tmpdir() - await using tmpB = await tmpdir() - const receivedA: number[] = [] - const receivedB: number[] = [] - - await withInstance(tmpA.path, async () => { - Bus.subscribe(TestEvent.Ping, (evt) => { - receivedA.push(evt.properties.value) - }) - await Bun.sleep(10) - }) - - await withInstance(tmpB.path, async () => { - Bus.subscribe(TestEvent.Ping, (evt) => { - receivedB.push(evt.properties.value) - }) - await Bun.sleep(10) - }) - - await withInstance(tmpA.path, async () => { - await Bus.publish(TestEvent.Ping, { value: 1 }) - await Bun.sleep(10) - }) - - await withInstance(tmpB.path, async () => { - await Bus.publish(TestEvent.Ping, { value: 2 }) - await Bun.sleep(10) - }) - - expect(receivedA).toEqual([1]) - expect(receivedB).toEqual([2]) - }) + it.live("events in one directory do not reach subscribers in another", () => + Effect.gen(function* () { + const tmpA = yield* tmpdirScoped() + const tmpB = yield* tmpdirScoped() + const receivedA: number[] = [] + const receivedB: number[] = [] + const doneA = yield* Deferred.make() + const doneB = yield* Deferred.make() + + yield* Effect.gen(function* () { + const bus = yield* Bus.Service + yield* bus.subscribeCallback(TestEvent.Ping, (evt) => { + receivedA.push(evt.properties.value) + Deferred.doneUnsafe(doneA, Effect.void) + }) + }).pipe(provideInstance(tmpA)) + + yield* Effect.gen(function* () { + const bus = yield* Bus.Service + yield* bus.subscribeCallback(TestEvent.Ping, (evt) => { + receivedB.push(evt.properties.value) + Deferred.doneUnsafe(doneB, Effect.void) + }) + }).pipe(provideInstance(tmpB)) + + yield* Effect.gen(function* () { + const bus = yield* Bus.Service + yield* bus.publish(TestEvent.Ping, { value: 1 }) + }).pipe(provideInstance(tmpA)) + + yield* Effect.gen(function* () { + const bus = yield* Bus.Service + yield* bus.publish(TestEvent.Ping, { value: 2 }) + }).pipe(provideInstance(tmpB)) + + yield* Deferred.await(doneA).pipe(Effect.timeout("2 seconds")) + yield* Deferred.await(doneB).pipe(Effect.timeout("2 seconds")) + + expect(receivedA).toEqual([1]) + expect(receivedB).toEqual([2]) + }), + ) }) describe("instance disposal", () => { - test("InstanceDisposed is delivered to wildcard subscribers before stream ends", async () => { - await using tmp = await tmpdir() - const received: string[] = [] - - await withInstance(tmp.path, async () => { - Bus.subscribeAll((evt) => { - received.push(evt.type) - }) - await Bun.sleep(10) - await Bus.publish(TestEvent.Ping, { value: 1 }) - await Bun.sleep(10) - }) - - // disposeAllInstances triggers the finalizer which publishes InstanceDisposed - await disposeAllInstances() - await Bun.sleep(50) - - expect(received).toContain("test.ping") - expect(received).toContain(Bus.InstanceDisposed.type) - }) + it.live("InstanceDisposed is delivered to wildcard subscribers before stream ends", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped() + const received: string[] = [] + const seen = yield* Deferred.make() + const disposed = yield* Deferred.make() + + yield* Effect.gen(function* () { + const bus = yield* Bus.Service + yield* bus.subscribeAllCallback((evt) => { + received.push(evt.type) + if (evt.type === TestEvent.Ping.type) Deferred.doneUnsafe(seen, Effect.void) + if (evt.type === Bus.InstanceDisposed.type) Deferred.doneUnsafe(disposed, Effect.void) + }) + yield* bus.publish(TestEvent.Ping, { value: 1 }) + yield* Deferred.await(seen).pipe(Effect.timeout("2 seconds")) + }).pipe(provideInstance(tmp)) + + yield* Effect.promise(disposeAllInstances) + yield* Deferred.await(disposed).pipe(Effect.timeout("2 seconds")) + + expect(received).toContain("test.ping") + expect(received).toContain(Bus.InstanceDisposed.type) + }), + ) }) }) diff --git a/packages/opencode/test/cli/cmd/tui/app-exit.test.ts b/packages/opencode/test/cli/cmd/tui/app-exit.test.ts index d1adf4fb8eb..bd23ee682c3 100644 --- a/packages/opencode/test/cli/cmd/tui/app-exit.test.ts +++ b/packages/opencode/test/cli/cmd/tui/app-exit.test.ts @@ -1,6 +1,7 @@ // kilocode_change - new file import { describe, expect, test } from "bun:test" -import { TuiConfig } from "../../../../src/cli/cmd/tui/config/tui" +import { createBindingLookup } from "@opentui/keymap/extras" +import { TuiKeybind } from "../../../../src/cli/cmd/tui/config/keybind" import * as AppExit from "../../../../src/kilocode/tui/app-exit" const prompt = (focused: boolean, input: string): AppExit.Prompt => ({ @@ -10,7 +11,10 @@ const prompt = (focused: boolean, input: string): AppExit.Prompt => ({ describe("app_exit", () => { test("blocks exit when the command matcher is disabled", () => { - const bindings = TuiConfig.resolve({}).keybinds.gather("app_exit", ["app.exit"]) + const bindings = createBindingLookup(TuiKeybind.toBindingConfig(TuiKeybind.parse({})), { + commandMap: TuiKeybind.CommandMap, + bindingDefaults: TuiKeybind.bindingDefaults(), + }).gather("app_exit", ["app.exit"]) expect(bindings.length).toBeGreaterThan(0) expect(AppExit.enabled(false)).toBe(false) diff --git a/packages/opencode/test/cli/cmd/tui/attention.test.ts b/packages/opencode/test/cli/cmd/tui/attention.test.ts new file mode 100644 index 00000000000..071aabdd793 --- /dev/null +++ b/packages/opencode/test/cli/cmd/tui/attention.test.ts @@ -0,0 +1,484 @@ +import { describe, expect, test } from "bun:test" +import type { AudioPlayOptions, AudioSound } from "@opentui/core" +import { createTuiAttention } from "@/cli/cmd/tui/attention" +import type { TuiConfig } from "@/cli/cmd/tui/config/tui" + +type FocusEvent = "focus" | "blur" + +type AttentionConfig = Pick + +class FakeRenderer { + isDestroyed = false + notificationResult = true + notificationThrows = false + notifications: { message: string; title: string | undefined }[] = [] + listeners: Record void>> = { + focus: new Set(), + blur: new Set(), + } + + on(event: FocusEvent, listener: () => void) { + this.listeners[event].add(listener) + return this + } + + off(event: FocusEvent, listener: () => void) { + this.listeners[event].delete(listener) + return this + } + + emit(event: FocusEvent) { + for (const listener of this.listeners[event]) listener() + } + + listenerCount(event: FocusEvent) { + return this.listeners[event].size + } + + triggerNotification(message: string, title?: string) { + if (this.notificationThrows) throw new Error("notification failed") + this.notifications.push({ message, title }) + return this.notificationResult + } +} + +class FakeAudioEngine { + loadResult: AudioSound | null = 1 + playResult: number | null = 1 + loadCalls = 0 + playCalls = 0 + volumes: (number | undefined)[] = [] + loadPaths: string[] = [] + rejectLoad = false + rejectPaths = new Set() + + async loadSoundFile(path: string) { + this.loadCalls += 1 + this.loadPaths.push(path) + if (this.rejectLoad || this.rejectPaths.has(path)) throw new Error("decode failed") + return this.loadResult + } + + play(_sound: AudioSound, options?: AudioPlayOptions) { + this.playCalls += 1 + this.volumes.push(options?.volume) + return this.playResult + } +} + +class FakeKV { + store: Record = {} + + get ready() { + return true + } + + get(key: string, fallback?: Value) { + return (this.store[key] ?? fallback) as Value + } + + set(key: string, value: unknown) { + this.store[key] = value + } +} + +function config(attention: Partial = {}): AttentionConfig { + return { + attention: { + enabled: true, + notifications: true, + sound: true, + volume: 0.4, + sound_pack: "opencode.default", + sounds: {}, + ...attention, + }, + } +} + +describe("createTuiAttention", () => { + test("defaults to sound always and notification blurred", async () => { + const renderer = new FakeRenderer() + const audio = new FakeAudioEngine() + const attention = createTuiAttention({ renderer, config: config(), audio }) + + expect(await attention.notify({ message: "hello" })).toEqual({ + ok: true, + notification: false, + sound: true, + }) + expect(renderer.notifications).toHaveLength(0) + expect(audio.playCalls).toBe(1) + }) + + test("supports blurred-only requests", async () => { + const renderer = new FakeRenderer() + const audio = new FakeAudioEngine() + const attention = createTuiAttention({ renderer, config: config(), audio }) + + expect(await attention.notify({ message: "unknown", sound: { when: "blurred" } })).toEqual({ + ok: false, + notification: false, + sound: false, + skipped: "focus_unknown", + }) + renderer.emit("focus") + expect(await attention.notify({ message: "focused", sound: { when: "blurred" } })).toEqual({ + ok: false, + notification: false, + sound: false, + skipped: "focused", + }) + renderer.emit("blur") + expect(await attention.notify({ message: "blurred", sound: { when: "blurred" } })).toEqual({ + ok: true, + notification: true, + sound: true, + }) + expect(audio.playCalls).toBe(1) + }) + + test("supports focused-only requests", async () => { + const renderer = new FakeRenderer() + const attention = createTuiAttention({ renderer, config: config(), audio: new FakeAudioEngine() }) + + expect(await attention.notify({ message: "unknown", notification: { when: "focused" }, sound: false })).toEqual({ + ok: false, + notification: false, + sound: false, + skipped: "focus_unknown", + }) + renderer.emit("blur") + expect(await attention.notify({ message: "blurred", notification: { when: "focused" }, sound: false })).toEqual({ + ok: false, + notification: false, + sound: false, + skipped: "blurred", + }) + renderer.emit("focus") + expect(await attention.notify({ message: "focused", notification: { when: "focused" }, sound: false })).toEqual({ + ok: true, + notification: true, + sound: false, + }) + expect(renderer.notifications).toEqual([{ title: "opencode", message: "focused" }]) + }) + + test("notification can deliver while focused when requested", async () => { + const renderer = new FakeRenderer() + const audio = new FakeAudioEngine() + const attention = createTuiAttention({ renderer, config: config(), audio }) + renderer.emit("focus") + + expect(await attention.notify({ message: "hello", notification: { when: "always" } })).toEqual({ + ok: true, + notification: true, + sound: true, + }) + expect(audio.playCalls).toBe(1) + expect(renderer.notifications).toEqual([{ title: "opencode", message: "hello" }]) + }) + + test("notifies while blurred", async () => { + const renderer = new FakeRenderer() + const attention = createTuiAttention({ renderer, config: config(), audio: new FakeAudioEngine() }) + renderer.emit("blur") + + expect(await attention.notify({ title: "opencode", message: "hello", sound: false })).toEqual({ + ok: true, + notification: true, + sound: false, + }) + expect(renderer.notifications).toEqual([{ title: "opencode", message: "hello" }]) + }) + + test("when requested, blurred-only calls do not notify or play sound while focused", async () => { + const renderer = new FakeRenderer() + const audio = new FakeAudioEngine() + const attention = createTuiAttention({ renderer, config: config(), audio }) + renderer.emit("focus") + + expect(await attention.notify({ message: "hello", sound: { when: "blurred" } })).toEqual({ + ok: false, + notification: false, + sound: false, + skipped: "focused", + }) + expect(renderer.notifications).toHaveLength(0) + expect(audio.loadCalls).toBe(0) + }) + + test("can play sound always while notification is blurred-only", async () => { + const renderer = new FakeRenderer() + const audio = new FakeAudioEngine() + const attention = createTuiAttention({ renderer, config: config(), audio }) + renderer.emit("focus") + + expect( + await attention.notify({ + message: "hello", + sound: { name: "question" }, + }), + ).toEqual({ + ok: true, + notification: false, + sound: true, + }) + expect(renderer.notifications).toHaveLength(0) + expect(audio.playCalls).toBe(1) + + renderer.emit("blur") + expect( + await attention.notify({ + message: "hello again", + sound: { name: "question" }, + }), + ).toEqual({ + ok: true, + notification: true, + sound: true, + }) + expect(renderer.notifications).toEqual([{ title: "opencode", message: "hello again" }]) + }) + + test("can disable notification per call while still playing sound", async () => { + const renderer = new FakeRenderer() + const audio = new FakeAudioEngine() + const attention = createTuiAttention({ renderer, config: config(), audio }) + + expect(await attention.notify({ message: "hello", notification: false })).toEqual({ + ok: true, + notification: false, + sound: true, + }) + expect(renderer.notifications).toHaveLength(0) + expect(audio.playCalls).toBe(1) + }) + + test("skips empty messages and disabled attention", async () => { + const empty = new FakeRenderer() + empty.emit("blur") + const disabled = new FakeRenderer() + disabled.emit("blur") + + expect(await createTuiAttention({ renderer: empty, config: config() }).notify({ message: " \n " })).toEqual({ + ok: false, + notification: false, + sound: false, + skipped: "empty_message", + }) + expect( + await createTuiAttention({ renderer: disabled, config: config({ enabled: false }) }).notify({ message: "hello" }), + ).toEqual({ + ok: false, + notification: false, + sound: false, + skipped: "attention_disabled", + }) + }) + + test("respects notification and sound config independently", async () => { + const renderer = new FakeRenderer() + const audio = new FakeAudioEngine() + const attention = createTuiAttention({ renderer, config: config({ notifications: false }), audio }) + renderer.emit("blur") + + expect(await attention.notify({ message: "hello", sound: true })).toEqual({ + ok: true, + notification: false, + sound: true, + }) + expect(renderer.notifications).toHaveLength(0) + expect(audio.playCalls).toBe(1) + + const soundDisabledRenderer = new FakeRenderer() + const soundDisabledAudio = new FakeAudioEngine() + const soundDisabled = createTuiAttention({ + renderer: soundDisabledRenderer, + config: config({ sound: false }), + audio: soundDisabledAudio, + }) + soundDisabledRenderer.emit("blur") + + expect(await soundDisabled.notify({ message: "hello", sound: true })).toEqual({ + ok: true, + notification: true, + sound: false, + }) + expect(soundDisabledAudio.loadCalls).toBe(0) + }) + + test("loads audio lazily only for eligible sound requests", async () => { + const renderer = new FakeRenderer() + const audio = new FakeAudioEngine() + const attention = createTuiAttention({ renderer, config: config(), audio }) + + await attention.notify({ message: "unknown", sound: { when: "blurred" } }) + expect(audio.loadCalls).toBe(0) + + renderer.emit("blur") + expect(await attention.notify({ message: "blurred", sound: { volume: 2 } })).toEqual({ + ok: true, + notification: true, + sound: true, + }) + expect(audio.loadCalls).toBe(1) + expect(audio.volumes).toEqual([1]) + }) + + test("handles unavailable playback and delegates sound loading", async () => { + const unavailableRenderer = new FakeRenderer() + const unavailableAudio = new FakeAudioEngine() + unavailableAudio.playResult = null + const unavailable = createTuiAttention({ renderer: unavailableRenderer, config: config(), audio: unavailableAudio }) + unavailableRenderer.emit("blur") + + expect(await unavailable.notify({ message: "hello", sound: true })).toEqual({ + ok: true, + notification: true, + sound: false, + }) + expect(unavailableAudio.loadCalls).toBe(1) + expect(unavailableAudio.playCalls).toBe(1) + + const repeatedRenderer = new FakeRenderer() + const repeatedAudio = new FakeAudioEngine() + const repeated = createTuiAttention({ renderer: repeatedRenderer, config: config(), audio: repeatedAudio }) + repeatedRenderer.emit("blur") + + await repeated.notify({ message: "one", sound: true }) + await repeated.notify({ message: "two", sound: true }) + expect(repeatedAudio.loadCalls).toBe(2) + expect(repeatedAudio.playCalls).toBe(2) + }) + + test("plays named sounds from the active sound pack", async () => { + const renderer = new FakeRenderer() + const audio = new FakeAudioEngine() + const attention = createTuiAttention({ renderer, config: config(), audio }) + renderer.emit("blur") + + const dispose = attention.soundboard.registerPack({ + id: "acme.soft", + name: "Soft Alerts", + sounds: { + question: "/tmp/question.mp3", + }, + }) + + expect(attention.soundboard.activate("acme.soft")).toBe(true) + expect(attention.soundboard.current()).toBe("acme.soft") + expect(attention.soundboard.list()).toContainEqual({ + id: "acme.soft", + name: "Soft Alerts", + active: true, + builtin: false, + }) + + expect(await attention.notify({ message: "question", sound: { name: "question" } })).toEqual({ + ok: true, + notification: true, + sound: true, + }) + expect(audio.loadPaths).toEqual(["/tmp/question.mp3"]) + + dispose() + expect(attention.soundboard.current()).toBe("opencode.default") + }) + + test("uses config sound overrides before active pack sounds and falls back on load failure", async () => { + const renderer = new FakeRenderer() + const audio = new FakeAudioEngine() + audio.rejectPaths.add("/tmp/bad-question.mp3") + const attention = createTuiAttention({ + renderer, + config: config({ sounds: { question: "/tmp/bad-question.mp3" } }), + audio, + }) + renderer.emit("blur") + + attention.soundboard.registerPack({ + id: "acme.soft", + sounds: { + question: "/tmp/good-question.mp3", + }, + }) + attention.soundboard.activate("acme.soft") + + expect(await attention.notify({ message: "question", sound: { name: "question" } })).toEqual({ + ok: true, + notification: true, + sound: true, + }) + expect(audio.loadPaths).toEqual(["/tmp/bad-question.mp3", "/tmp/good-question.mp3"]) + }) + + test("persists activated sound pack in KV", () => { + const kv = new FakeKV() + const renderer = new FakeRenderer() + const attention = createTuiAttention({ renderer, config: config(), kv }) + + attention.soundboard.registerPack({ id: "acme.soft", sounds: { done: "/tmp/done.mp3" } }) + + expect(attention.soundboard.activate("missing", { persist: true })).toBe(false) + expect(kv.store.attention_sound_pack).toBeUndefined() + expect(attention.soundboard.activate("acme.soft", { persist: true })).toBe(true) + expect(kv.store.attention_sound_pack).toBe("acme.soft") + + const next = createTuiAttention({ renderer: new FakeRenderer(), config: config(), kv }) + next.soundboard.registerPack({ id: "acme.soft", sounds: { done: "/tmp/done.mp3" } }) + expect(next.soundboard.current()).toBe("acme.soft") + }) + + test("does not throw for notification or sound failures", async () => { + const renderer = new FakeRenderer() + const audio = new FakeAudioEngine() + renderer.notificationThrows = true + audio.rejectLoad = true + const attention = createTuiAttention({ renderer, config: config(), audio }) + renderer.emit("blur") + + expect(await attention.notify({ message: "hello", sound: true })).toEqual({ + ok: false, + notification: false, + sound: false, + }) + }) + + test("strips unsafe notification text", async () => { + const renderer = new FakeRenderer() + const attention = createTuiAttention({ renderer, config: config(), audio: new FakeAudioEngine() }) + renderer.emit("blur") + + await attention.notify({ + title: "\u001b[31m danger\n title\u0007", + message: "\u001b[32m hello\n world\u0000", + }) + + expect(renderer.notifications).toEqual([{ title: "danger title", message: "hello world" }]) + }) + + test("disposes renderer listeners", async () => { + const renderer = new FakeRenderer() + const audio = new FakeAudioEngine() + const attention = createTuiAttention({ renderer, config: config(), audio }) + renderer.emit("blur") + await attention.notify({ message: "hello", sound: true }) + + expect(renderer.listenerCount("focus")).toBe(1) + expect(renderer.listenerCount("blur")).toBe(1) + + attention.dispose() + renderer.isDestroyed = true + + expect(renderer.listenerCount("focus")).toBe(0) + expect(renderer.listenerCount("blur")).toBe(0) + expect(audio.loadCalls).toBe(1) + expect(await attention.notify({ message: "hello" })).toEqual({ + ok: false, + notification: false, + sound: false, + skipped: "renderer_destroyed", + }) + }) +}) diff --git a/packages/opencode/test/cli/cmd/tui/notifications.test.ts b/packages/opencode/test/cli/cmd/tui/notifications.test.ts new file mode 100644 index 00000000000..a673e4accc4 --- /dev/null +++ b/packages/opencode/test/cli/cmd/tui/notifications.test.ts @@ -0,0 +1,267 @@ +import { describe, expect, test } from "bun:test" +import Notifications from "@/cli/cmd/tui/feature-plugins/system/notifications" +import type { Event, PermissionRequest, QuestionRequest, Session } from "@kilocode/sdk/v2" +import type { TuiAttentionNotifyInput } from "@kilocode/plugin/tui" +import { createTuiPluginApi } from "../../../fixture/tui-plugin" + +async function setup() { + const notifications: TuiAttentionNotifyInput[] = [] + const handlers = new Map void)[]>() + const session = (id: string, title: string, parentID?: string): Session => ({ + id, + title, + slug: id, + projectID: "project", + directory: "/workspace", + ...(parentID && { parentID }), + version: "0.0.0-test", + time: { created: 0, updated: 0 }, + }) + const sessions: Record = { + session: session("session", "Demo session"), + subagent: session("subagent", "Subagent session", "session"), + abort: session("abort", "Abort session"), + timeout: session("timeout", "Timeout session"), + } + + await Notifications.tui( + createTuiPluginApi({ + attention: { + async notify(input) { + notifications.push(input) + return { ok: true, notification: true, sound: true } + }, + }, + event: { + on: (type: Type, handler: (event: Extract) => void) => { + const list = handlers.get(type) ?? [] + const wrapped = handler as (event: Event) => void + list.push(wrapped) + handlers.set(type, list) + return () => { + handlers.set( + type, + (handlers.get(type) ?? []).filter((item) => item !== wrapped), + ) + } + }, + }, + state: { + session: { + get: (sessionID: string) => sessions[sessionID], + }, + }, + }), + undefined, + {} as never, + ) + + return { + notifications, + emit(event: Event) { + for (const handler of handlers.get(event.type) ?? []) handler(event) + }, + } +} + +function question(id: string, sessionID = "session"): QuestionRequest { + return { + id, + sessionID, + questions: [], + } +} + +function permission(id: string, sessionID = "session"): PermissionRequest { + return { + id, + sessionID, + permission: "edit", + patterns: [], + metadata: {}, + always: [], + } +} + +const questionNotification: TuiAttentionNotifyInput = { + title: "Demo session", + message: "Question needs input", + notification: { when: "blurred" }, + sound: { name: "question", when: "always" }, +} + +const permissionNotification: TuiAttentionNotifyInput = { + title: "Demo session", + message: "Permission needs input", + notification: { when: "blurred" }, + sound: { name: "permission", when: "always" }, +} + +describe("internal notifications TUI plugin", () => { + test("notifies for question and permission requests with blurred notifications and always-on sounds", async () => { + const harness = await setup() + + harness.emit({ id: "event-1", type: "question.asked", properties: question("question-1") }) + harness.emit({ id: "event-2", type: "permission.asked", properties: permission("permission-1") }) + + expect(harness.notifications).toEqual([questionNotification, permissionNotification]) + }) + + test("dedupes pending questions and permissions until they are resolved", async () => { + const harness = await setup() + + harness.emit({ id: "event-1", type: "question.asked", properties: question("question-1") }) + harness.emit({ id: "event-2", type: "question.asked", properties: question("question-1") }) + harness.emit({ + id: "event-3", + type: "question.replied", + properties: { sessionID: "session", requestID: "question-1", answers: [] }, + }) + harness.emit({ id: "event-4", type: "question.asked", properties: question("question-1") }) + + harness.emit({ id: "event-5", type: "permission.asked", properties: permission("permission-1") }) + harness.emit({ id: "event-6", type: "permission.asked", properties: permission("permission-1") }) + harness.emit({ + id: "event-7", + type: "permission.replied", + properties: { sessionID: "session", requestID: "permission-1", reply: "once" }, + }) + harness.emit({ id: "event-8", type: "permission.asked", properties: permission("permission-1") }) + + expect(harness.notifications).toEqual([ + questionNotification, + questionNotification, + permissionNotification, + permissionNotification, + ]) + }) + + test("notifies when an active session becomes idle and suppresses no-op idle", async () => { + const harness = await setup() + + harness.emit({ + id: "event-1", + type: "session.status", + properties: { sessionID: "session", status: { type: "idle" } }, + }) + harness.emit({ + id: "event-2", + type: "session.status", + properties: { sessionID: "session", status: { type: "busy" } }, + }) + harness.emit({ + id: "event-3", + type: "session.status", + properties: { sessionID: "session", status: { type: "idle" } }, + }) + + expect(harness.notifications).toEqual([ + { + title: "Demo session", + message: "Session done", + notification: { when: "blurred" }, + sound: { name: "done", when: "always" }, + }, + ]) + }) + + test("uses sound-only notifications and subagent_done sound for subagent sessions", async () => { + const harness = await setup() + + harness.emit({ id: "event-1", type: "question.asked", properties: question("question-1", "subagent") }) + harness.emit({ + id: "event-2", + type: "session.status", + properties: { sessionID: "subagent", status: { type: "busy" } }, + }) + harness.emit({ + id: "event-3", + type: "session.status", + properties: { sessionID: "subagent", status: { type: "idle" } }, + }) + + expect(harness.notifications).toEqual([ + { + title: "Subagent session", + message: "Question needs input", + notification: false, + sound: { name: "question", when: "always" }, + }, + { + title: "Subagent session", + message: "Session done", + notification: false, + sound: { name: "subagent_done", when: "always" }, + }, + ]) + }) + + test("notifies session errors once and suppresses the following idle done notification", async () => { + const harness = await setup() + + harness.emit({ + id: "event-1", + type: "session.status", + properties: { sessionID: "session", status: { type: "busy" } }, + }) + harness.emit({ + id: "event-2", + type: "session.error", + properties: { sessionID: "session", error: { name: "UnknownError", data: { message: "boom" } } }, + }) + harness.emit({ + id: "event-3", + type: "session.status", + properties: { sessionID: "session", status: { type: "idle" } }, + }) + + expect(harness.notifications).toEqual([ + { + title: "Demo session", + message: "Session error", + notification: { when: "blurred" }, + sound: { name: "error", when: "always" }, + }, + ]) + }) + + test("special-cases aborts and model response timeouts", async () => { + const harness = await setup() + + harness.emit({ + id: "event-1", + type: "session.status", + properties: { sessionID: "abort", status: { type: "busy" } }, + }) + harness.emit({ + id: "event-2", + type: "session.error", + properties: { sessionID: "abort", error: { name: "MessageAbortedError", data: { message: "Aborted" } } }, + }) + harness.emit({ + id: "event-3", + type: "session.status", + properties: { sessionID: "timeout", status: { type: "busy" } }, + }) + harness.emit({ + id: "event-4", + type: "session.error", + properties: { sessionID: "timeout", error: { name: "UnknownError", data: { message: "SSE read timed out" } } }, + }) + + expect(harness.notifications).toEqual([ + { + title: "Abort session", + message: "Session aborted", + notification: { when: "blurred" }, + sound: { name: "error", when: "always" }, + }, + { + title: "Timeout session", + message: "Model stopped responding", + notification: { when: "blurred" }, + sound: { name: "error", when: "always" }, + }, + ]) + }) +}) diff --git a/packages/opencode/test/cli/cmd/tui/sync-fixture.tsx b/packages/opencode/test/cli/cmd/tui/sync-fixture.tsx index 47cf45f7f15..f152b649c7b 100644 --- a/packages/opencode/test/cli/cmd/tui/sync-fixture.tsx +++ b/packages/opencode/test/cli/cmd/tui/sync-fixture.tsx @@ -4,10 +4,11 @@ import { onMount } from "solid-js" import { ArgsProvider } from "../../../../src/cli/cmd/tui/context/args" import { ExitProvider } from "../../../../src/cli/cmd/tui/context/exit" import { KVProvider, useKV } from "../../../../src/cli/cmd/tui/context/kv" -import { ProjectProvider } from "../../../../src/cli/cmd/tui/context/project" +import { ProjectProvider, useProject } from "../../../../src/cli/cmd/tui/context/project" import { SDKProvider, type EventSource } from "../../../../src/cli/cmd/tui/context/sdk" import { SyncProvider, useSync } from "../../../../src/cli/cmd/tui/context/sync" import { ToastProvider } from "../../../../src/cli/cmd/tui/ui/toast" // kilocode_change +import type { GlobalEvent } from "@kilocode/sdk/v2" export const worktree = "/tmp/opencode" export const directory = `${worktree}/packages/opencode` @@ -31,6 +32,25 @@ export function eventSource(): EventSource { return { subscribe: async () => () => {} } } +export function createEventSource() { + let fn: ((event: GlobalEvent) => void) | undefined + + return { + source: { + subscribe: async (handler: (event: GlobalEvent) => void) => { + fn = handler + return () => { + if (fn === handler) fn = undefined + } + }, + } satisfies EventSource, + emit(event: GlobalEvent) { + if (!fn) throw new Error("event source not ready") + fn(event) + }, + } +} + type FetchHandler = (url: URL) => Response | Promise | undefined export function createFetch(override?: FetchHandler) { @@ -45,8 +65,6 @@ export function createFetch(override?: FetchHandler) { switch (url.pathname) { case "/agent": case "/command": - case "/experimental/workspace": - case "/experimental/workspace/status": case "/formatter": case "/lsp": case "/network": // kilocode_change @@ -69,6 +87,15 @@ export function createFetch(override?: FetchHandler) { return json({ id: "proj_test" }) case "/provider": return json({ all: [], default: {}, connected: [] }) + case "/experimental/workspace": + return json([ + { id: "ws_a", type: "local", branch: "a", name: "a", directory: "/tmp/a", projectID: "proj_test" }, + { id: "ws_b", type: "local", branch: "b", name: "b", directory: "/tmp/b", projectID: "proj_test" }, + ]) + case "/experimental/workspace/status": + return json([]) + case "/indexing/status": // kilocode_change + return json({ state: "Disabled", message: "Indexing disabled.", processedFiles: 0, totalFiles: 0, percent: 0 }) case "/session": return json([]) case "/vcs": @@ -81,11 +108,13 @@ export function createFetch(override?: FetchHandler) { return { fetch, session } } -type Ctx = { kv: ReturnType; sync: ReturnType } +type Ctx = { kv: ReturnType; project: ReturnType; sync: ReturnType } export async function mount(override?: FetchHandler) { const calls = createFetch(override) + const events = createEventSource() let sync!: ReturnType + let project!: ReturnType let kv!: ReturnType let done!: () => void const ready = new Promise((resolve) => { @@ -93,9 +122,10 @@ export async function mount(override?: FetchHandler) { }) function Probe() { - const ctx: Ctx = { kv: useKV(), sync: useSync() } + const ctx: Ctx = { kv: useKV(), project: useProject(), sync: useSync() } onMount(() => { sync = ctx.sync + project = ctx.project kv = ctx.kv done() }) @@ -109,7 +139,7 @@ export async function mount(override?: FetchHandler) { {/* kilocode_change start */} {/* kilocode_change end */} - + @@ -126,5 +156,5 @@ export async function mount(override?: FetchHandler) { await ready await wait(() => sync.status === "complete") - return { app, kv, sync, session: calls.session } + return { app, emit: events.emit, kv, project, sync, session: calls.session } } diff --git a/packages/opencode/test/cli/cmd/tui/sync.test.tsx b/packages/opencode/test/cli/cmd/tui/sync.test.tsx index 24bf49a9277..72d11db08f6 100644 --- a/packages/opencode/test/cli/cmd/tui/sync.test.tsx +++ b/packages/opencode/test/cli/cmd/tui/sync.test.tsx @@ -3,7 +3,21 @@ import { describe, expect, test } from "bun:test" import { Global } from "@opencode-ai/core/global" import { WithInstance } from "../../../../src/project/with-instance" // kilocode_change import { disposeAllInstances, tmpdir } from "../../../fixture/fixture" -import { mount } from "./sync-fixture" +import { mount, wait } from "./sync-fixture" +import type { GlobalEvent } from "@kilocode/sdk/v2" + +function branchEvent(branch: string, workspace?: string): GlobalEvent { + return { + directory: "/tmp/other", + project: "proj_test", + workspace, + payload: { + id: `evt_vcs_${branch}`, + type: "vcs.branch.updated", + properties: { branch }, + }, + } +} describe("tui sync", () => { test("refresh scopes sessions by default and lists project sessions when disabled", async () => { @@ -29,4 +43,30 @@ describe("tui sync", () => { Global.Path.state = previous } }) + + test("vcs branch updates only apply for the active workspace", async () => { + const previous = Global.Path.state + await using tmp = await tmpdir() + Global.Path.state = tmp.path + await Bun.write(`${tmp.path}/kv.json`, "{}") + const { app, emit, project, sync } = await mount() + + try { + expect(sync.data.vcs?.branch).toBe("main") + + project.workspace.set("ws_a") + emit(branchEvent("other", "ws_b")) + await Bun.sleep(30) + + expect(sync.data.vcs?.branch).toBe("main") + + emit(branchEvent("feature", "ws_a")) + await wait(() => sync.data.vcs?.branch === "feature") + + expect(sync.data.vcs?.branch).toBe("feature") + } finally { + app.renderer.destroy() + Global.Path.state = previous + } + }) }) diff --git a/packages/opencode/test/cli/effect-cmd-instance-als.test.ts b/packages/opencode/test/cli/effect-cmd-instance-als.test.ts index de6fed8daa6..122b87f1742 100644 --- a/packages/opencode/test/cli/effect-cmd-instance-als.test.ts +++ b/packages/opencode/test/cli/effect-cmd-instance-als.test.ts @@ -1,8 +1,13 @@ -import { afterEach, expect, test } from "bun:test" +import { afterEach, expect } from "bun:test" +import { AppFileSystem } from "@opencode-ai/core/filesystem" import { Effect } from "effect" -import fs from "fs/promises" +import { fileURLToPath } from "url" +import { InstanceRef } from "../../src/effect/instance-ref" import { Instance } from "../../src/project/instance" -import { disposeAllInstances, provideTestInstance, tmpdir } from "../fixture/fixture" +import { disposeAllInstances, TestInstance } from "../fixture/fixture" +import { testEffect } from "../lib/effect" + +const it = testEffect(AppFileSystem.defaultLayer) afterEach(async () => { await disposeAllInstances() @@ -14,35 +19,40 @@ afterEach(async () => { // has lost the outer InstanceRef. Services that read `InstanceState.context` // then fall back to `Instance.current` ALS, which must be installed at the JS // callback boundary (Node ALS persists across awaits, Effect's fiber context -// does not). `provideTestInstance` mirrors effectCmd's load + ALS-restore wrap. +// does not). `it.instance` provides the loaded InstanceRef; the explicit +// Instance.restore mirrors effectCmd's load + ALS-restore wrap. // Pins effect-cmd.ts directly: the pattern test below exercises the load + -// Instance.restore + dispose triple via the shared `provideTestInstance` fixture, +// Instance.restore boundary via the shared `it.instance` fixture, // so a regression that removed `Instance.restore` from effect-cmd.ts wouldn't // fail it. This grep guards the actual production callsite. -test("effect-cmd.ts wraps the handler body in Instance.restore", async () => { - const source = await fs.readFile(new URL("../../src/cli/effect-cmd.ts", import.meta.url), "utf8") - expect(source).toContain("Instance.restore(ctx") -}) +it.live("effect-cmd.ts wraps the handler body in Instance.restore", () => + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const source = yield* fs.readFileString(fileURLToPath(new URL("../../src/cli/effect-cmd.ts", import.meta.url))) + expect(source).toContain("Instance.restore(ctx") + }), +) + +it.instance( + "Instance.current reachable after await inside restored Effect.promise(async)", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const ctx = yield* InstanceRef + if (!ctx) throw new Error("InstanceRef not provided") -test("Instance.current reachable from inner runPromise inside Effect.promise(async)", async () => { - await using dir = await tmpdir({ git: true }) - await provideTestInstance({ - directory: dir.path, - fn: () => - Effect.runPromise( - Effect.promise(async () => { - await new Promise((r) => setTimeout(r, 5)) - const current = await Effect.runPromise( - Effect.sync(() => { - try { - return Instance.current - } catch { - return undefined - } - }), - ) - expect(current?.directory).toBe(dir.path) + const current = yield* Effect.promise(() => + Instance.restore(ctx, async () => { + await Promise.resolve() + try { + return Instance.current + } catch { + return undefined + } }), - ), - }) -}) + ) + + expect(current?.directory).toBe(test.directory) + }), + { git: true }, +) diff --git a/packages/opencode/test/cli/error.test.ts b/packages/opencode/test/cli/error.test.ts index 6af2633ce62..20d63c64bad 100644 --- a/packages/opencode/test/cli/error.test.ts +++ b/packages/opencode/test/cli/error.test.ts @@ -1,8 +1,56 @@ import { describe, expect, test } from "bun:test" import { AccountTransportError } from "../../src/account/schema" import { FormatError } from "../../src/cli/error" +import { UI } from "../../src/cli/ui" describe("cli.error", () => { + test("formats legacy and tagged config errors the same way", () => { + const cases = [ + { + tag: "ConfigJsonError", + data: { path: "/tmp/opencode.jsonc", message: "Unexpected token" }, + expected: "Config file at /tmp/opencode.jsonc is not valid JSON(C): Unexpected token", + }, + { + tag: "ConfigDirectoryTypoError", + data: { path: "/tmp/opencode.jsonc", dir: ".opencode", suggestion: "opencode" }, + expected: + 'Directory ".opencode" in /tmp/opencode.jsonc is not valid. Rename the directory to "opencode" or remove it. This is a common typo.', + }, + { + tag: "ConfigFrontmatterError", + data: { path: "/tmp/AGENTS.md", message: "failed frontmatter" }, + expected: "failed frontmatter", + }, + { + tag: "ConfigInvalidError", + data: { + path: "/tmp/opencode.jsonc", + message: "schema mismatch", + issues: [{ message: "Expected string", path: ["provider", "id"] }], + }, + expected: "Configuration is invalid at /tmp/opencode.jsonc: schema mismatch\n↳ Expected string provider.id", + }, + ] + + for (const item of cases) { + expect(FormatError({ name: item.tag, data: item.data })).toBe(item.expected) + expect(FormatError({ _tag: item.tag, ...item.data })).toBe(item.expected) + } + }) + + test("preserves multiline JSONC diagnostics for tagged config errors", () => { + const data = { + path: "/tmp/opencode.jsonc", + message: + '\n--- JSONC Input ---\n{\n "model": \n}\n--- Errors ---\nValueExpected at line 3, column 1\n Line 3: }\n ^\n--- End ---', + } + const expected = `Config file at ${data.path} is not valid JSON(C): ${data.message}` + + expect(FormatError({ name: "ConfigJsonError", data })).toBe(expected) + expect(FormatError({ _tag: "ConfigJsonError", ...data })).toBe(expected) + }) + test("formats account transport errors clearly", () => { const error = new AccountTransportError({ method: "POST", @@ -15,4 +63,33 @@ describe("cli.error", () => { expect(formatted).toContain("This failed before the server returned an HTTP response.") expect(formatted).toContain("Check your network, proxy, or VPN configuration and try again.") }) + + test("formats legacy and tagged provider model errors the same way", () => { + const data = { + providerID: "anthropic", + modelID: "claude-sonet-4", + suggestions: ["claude-sonnet-4"], + } + const expected = [ + "Model not found: anthropic/claude-sonet-4", + "Did you mean: claude-sonnet-4", + "Try: `kilo models` to list available models", + "Or check your config (opencode.json) provider/model names", + ].join("\n") + + expect(FormatError({ name: "ProviderModelNotFoundError", data })).toBe(expected) + expect(FormatError({ _tag: "ProviderModelNotFoundError", ...data })).toBe(expected) + }) + + test("formats legacy and tagged provider init errors the same way", () => { + const data = { providerID: "anthropic" } + const expected = 'Failed to initialize provider "anthropic". Check credentials and configuration.' + + expect(FormatError({ name: "ProviderInitError", data })).toBe(expected) + expect(FormatError({ _tag: "ProviderInitError", ...data })).toBe(expected) + }) + + test("formats cancelled UI errors as empty output", () => { + expect(FormatError(new UI.CancelledError())).toBe("") + }) }) diff --git a/packages/opencode/test/cli/install-artifact.test.ts b/packages/opencode/test/cli/install-artifact.test.ts index 560f72d245d..c15a1a9e3de 100644 --- a/packages/opencode/test/cli/install-artifact.test.ts +++ b/packages/opencode/test/cli/install-artifact.test.ts @@ -35,7 +35,10 @@ describe("npm install artifact behavior", () => { await fs.mkdir(path.join(bin, "tree-sitter"), { recursive: true }) await fs.mkdir(path.join(bin, "console", "assets"), { recursive: true }) await fs.copyFile(postinstall, path.join(pkg, "postinstall.mjs")) - await Bun.write(path.join(native, "package.json"), JSON.stringify({ name: `@kilocode/cli-${process.platform}-${process.arch}` })) + await Bun.write( + path.join(native, "package.json"), + JSON.stringify({ name: `@kilocode/cli-${process.platform}-${process.arch}` }), + ) await Bun.write(path.join(bin, "kilo"), "binary") await Bun.write(path.join(bin, "tree-sitter", "tree-sitter.wasm"), "wasm") await Bun.write(path.join(bin, "console", "index.html"), "console") diff --git a/packages/opencode/test/cli/run/prompt.shared.test.ts b/packages/opencode/test/cli/run/prompt.shared.test.ts index 85a9dfa4068..299751eaa34 100644 --- a/packages/opencode/test/cli/run/prompt.shared.test.ts +++ b/packages/opencode/test/cli/run/prompt.shared.test.ts @@ -1,8 +1,11 @@ import { describe, expect, test } from "bun:test" import { createPromptHistory, + displayCharAt, + displaySlice, isExitCommand, isNewCommand, + mentionTriggerIndex, movePromptHistory, printableBinding, promptCycle, @@ -85,6 +88,53 @@ describe("run prompt shared", () => { expect(draft.state.index).toBeNull() }) + test("uses display-width cursors for history restoration", () => { + const base = createPromptHistory([prompt("one"), prompt("中文")]) + + const latest = movePromptHistory(base, -1, "草稿", 0) + expect(latest.apply).toBe(true) + expect(latest.text).toBe("中文") + expect(latest.cursor).toBe(0) + + const older = movePromptHistory(latest.state, -1, "中文", 0) + expect(older.apply).toBe(true) + expect(older.text).toBe("one") + expect(older.cursor).toBe(0) + + const newer = movePromptHistory(older.state, 1, "one", Bun.stringWidth("one")) + expect(newer.apply).toBe(true) + expect(newer.text).toBe("中文") + expect(newer.cursor).toBe(Bun.stringWidth("中文")) + + const draft = movePromptHistory(newer.state, 1, "中文", Bun.stringWidth("中文")) + expect(draft.apply).toBe(true) + expect(draft.text).toBe("草稿") + expect(draft.cursor).toBe(Bun.stringWidth("草稿")) + }) + + test("uses display-width offsets for mention helpers", () => { + expect(mentionTriggerIndex("@")).toBe(0) + expect(mentionTriggerIndex("test @")).toBe(5) + expect(mentionTriggerIndex("中文 @")).toBe(5) + expect(mentionTriggerIndex("こんにちは @")).toBe(11) + expect(mentionTriggerIndex("한국어 @")).toBe(7) + expect(mentionTriggerIndex("🙂 @")).toBe(3) + expect(mentionTriggerIndex("中文 @src file", Bun.stringWidth("中文 @src"))).toBe(5) + expect(displayCharAt("中文 @src", Bun.stringWidth("中文 @"))).toBe("s") + expect(displaySlice("中文 @src", 5, Bun.stringWidth("中文 @src"))).toBe("@src") + expect(displaySlice("中文 @src", 6, Bun.stringWidth("中文 @src"))).toBe("src") + expect(mentionTriggerIndex("👨‍👩‍👧‍👦 @src", Bun.stringWidth("👨‍👩‍👧‍👦 @src"))).toBe(3) + expect(displayCharAt("👨‍👩‍👧‍👦 @src", Bun.stringWidth("👨‍👩‍👧‍👦 @"))).toBe("s") + expect(displaySlice("👨‍👩‍👧‍👦 @src", 3, Bun.stringWidth("👨‍👩‍👧‍👦 @src"))).toBe("@src") + expect(mentionTriggerIndex("中文@")).toBeUndefined() + expect(mentionTriggerIndex("こんにちは@")).toBeUndefined() + expect(mentionTriggerIndex("한국어@")).toBeUndefined() + expect(mentionTriggerIndex("🙂@")).toBeUndefined() + expect(mentionTriggerIndex("hello@")).toBeUndefined() + expect(mentionTriggerIndex("foo@bar.com")).toBeUndefined() + expect(mentionTriggerIndex("中文 @src file")).toBeUndefined() + }) + test("handles direct and leader-based variant cycling", () => { const keys = promptKeys(keybinds) diff --git a/packages/opencode/test/cli/run/runtime.boot.test.ts b/packages/opencode/test/cli/run/runtime.boot.test.ts index 4ae2aa21ba1..886f6af25ec 100644 --- a/packages/opencode/test/cli/run/runtime.boot.test.ts +++ b/packages/opencode/test/cli/run/runtime.boot.test.ts @@ -1,14 +1,9 @@ import { afterEach, describe, expect, mock, spyOn, test } from "bun:test" -import type { KeyEvent, Renderable } from "@opentui/core" -import type { Binding } from "@opentui/keymap" -import { createBindingLookup } from "@opentui/keymap/extras" import { KiloClient, type Provider } from "@kilocode/sdk/v2" import { TuiConfig, type Resolved } from "@/cli/cmd/tui/config/tui" import { formatBindings } from "@/cli/cmd/run/keymap.shared" -import { TuiKeybind } from "@/cli/cmd/tui/config/keybind" import { resolveDiffStyle, resolveFooterKeybinds, resolveModelInfo } from "@/cli/cmd/run/runtime.boot" - -type RunBinding = Binding +import { createTuiResolvedConfig } from "../../fixture/tui-runtime" function model(id: string, providerID: string, context: number, variants?: Record>) { return { @@ -61,45 +56,37 @@ function model(id: string, providerID: string, context: number, variants?: Recor } } -function bindings(...keys: string[]) { - return keys.map((key) => ({ key })) -} - function config(input?: { leader?: string leaderTimeout?: number diff_style?: "auto" | "stacked" bindings?: Partial<{ - commandList: RunBinding[] - variantCycle: RunBinding[] - interrupt: RunBinding[] - historyPrevious: RunBinding[] - historyNext: RunBinding[] - inputClear: RunBinding[] - inputSubmit: RunBinding[] - inputNewline: RunBinding[] + commandList: string[] + variantCycle: string[] + interrupt: string[] + historyPrevious: string[] + historyNext: string[] + inputClear: string[] + inputSubmit: string[] + inputNewline: string[] }> }): Resolved { const bind = input?.bindings - const keybinds = TuiKeybind.Keybinds.parse({ - ...(input?.leader && { leader: input.leader }), - ...(bind?.commandList && { command_list: bind.commandList }), - ...(bind?.variantCycle && { variant_cycle: bind.variantCycle }), - ...(bind?.interrupt && { session_interrupt: bind.interrupt }), - ...(bind?.historyPrevious && { history_previous: bind.historyPrevious }), - ...(bind?.historyNext && { history_next: bind.historyNext }), - ...(bind?.inputClear && { input_clear: bind.inputClear }), - ...(bind?.inputSubmit && { input_submit: bind.inputSubmit }), - ...(bind?.inputNewline && { input_newline: bind.inputNewline }), - }) - return { + return createTuiResolvedConfig({ diff_style: input?.diff_style, - keybinds: createBindingLookup(TuiKeybind.toBindingConfig(keybinds), { - commandMap: TuiKeybind.CommandMap, - bindingDefaults: TuiKeybind.bindingDefaults(), - }), - leader_timeout: input?.leaderTimeout ?? 2000, - } + leader_timeout: input?.leaderTimeout, + keybinds: { + ...(input?.leader && { leader: input.leader }), + ...(bind?.commandList && { command_list: bind.commandList }), + ...(bind?.variantCycle && { variant_cycle: bind.variantCycle }), + ...(bind?.interrupt && { session_interrupt: bind.interrupt }), + ...(bind?.historyPrevious && { history_previous: bind.historyPrevious }), + ...(bind?.historyNext && { history_next: bind.historyNext }), + ...(bind?.inputClear && { input_clear: bind.inputClear }), + ...(bind?.inputSubmit && { input_submit: bind.inputSubmit }), + ...(bind?.inputNewline && { input_newline: bind.inputNewline }), + }, + }) } describe("run runtime boot", () => { @@ -112,14 +99,14 @@ describe("run runtime boot", () => { config({ leader: "ctrl+g", bindings: { - commandList: bindings("ctrl+p"), - variantCycle: bindings("ctrl+t", "alt+t"), - interrupt: bindings("ctrl+c"), - historyPrevious: bindings("k"), - historyNext: bindings("j"), - inputClear: bindings("ctrl+l"), - inputSubmit: bindings("ctrl+s"), - inputNewline: bindings("alt+return"), + commandList: ["ctrl+p"], + variantCycle: ["ctrl+t", "alt+t"], + interrupt: ["ctrl+c"], + historyPrevious: ["k"], + historyNext: ["j"], + inputClear: ["ctrl+l"], + inputSubmit: ["ctrl+s"], + inputNewline: ["alt+return"], }, }), ) diff --git a/packages/opencode/test/cli/run/stream.transport.test.ts b/packages/opencode/test/cli/run/stream.transport.test.ts index 0a551beb4f8..1934261374d 100644 --- a/packages/opencode/test/cli/run/stream.transport.test.ts +++ b/packages/opencode/test/cli/run/stream.transport.test.ts @@ -3,9 +3,9 @@ import { KiloClient, type GlobalEvent } from "@kilocode/sdk/v2" import { createSessionTransport } from "@/cli/cmd/run/stream.transport" import type { FooterApi, FooterEvent, RunFilePart, StreamCommit } from "@/cli/cmd/run/types" -type EventStream = Awaited>["stream"] +type SdkEvent = GlobalEvent["payload"] +type EventStream = AsyncGenerator type GlobalEventStream = Awaited>["stream"] -type SdkEvent = EventStream extends AsyncGenerator ? T : never type SessionMessage = NonNullable>["data"]>[number] type SessionChild = NonNullable>["data"]>[number] type SessionToolPart = Extract @@ -67,19 +67,22 @@ function idle(sessionID = "session-1") { } satisfies SdkEvent } -function assistant(id: string) { +function assistant(id: string, sessionID = "session-1"): SdkEvent { return { id: `evt-${id}`, - type: "message.updated", - properties: { - sessionID: "session-1", + type: "sync", + name: "message.updated.1", + seq: 1, + aggregateID: "sessionID", + data: { + sessionID, info: assistantMessage({ - sessionID: "session-1", + sessionID, id, parts: [], }).info, }, - } satisfies SdkEvent + } } function feed() { @@ -269,8 +272,11 @@ function textPart(id: string, messageID: string, text: string, sessionID = "sess function textUpdated(part: TextPart): SdkEvent { return { id: `evt-${part.id}-updated`, - type: "message.part.updated", - properties: { + type: "sync", + name: "message.part.updated.1", + seq: 1, + aggregateID: "sessionID", + data: { sessionID: part.sessionID, part, time: 1, @@ -281,8 +287,11 @@ function textUpdated(part: TextPart): SdkEvent { function toolUpdated(part: SessionToolPart): SdkEvent { return { id: `evt-${part.id}-updated`, - type: "message.part.updated", - properties: { + type: "sync", + name: "message.part.updated.1", + seq: 1, + aggregateID: "sessionID", + data: { sessionID: part.sessionID, part, time: 1, @@ -363,7 +372,6 @@ function sdk( input: { stream?: EventStream globalStream?: GlobalEventStream - subscribe?: KiloClient["event"]["subscribe"] globalEvent?: KiloClient["global"]["event"] promptAsync?: KiloClient["session"]["promptAsync"] status?: KiloClient["session"]["status"] @@ -375,7 +383,6 @@ function sdk( ) { const client = new KiloClient() - const subscribe: KiloClient["event"]["subscribe"] = input.subscribe ?? (() => sse(input.stream ?? emptyStream())) const globalEvent: KiloClient["global"]["event"] = input.globalEvent ?? (() => globalSse(input.globalStream ?? wrapGlobalStream(input.stream ?? emptyStream()))) const promptAsync: KiloClient["session"]["promptAsync"] = input.promptAsync ?? (() => ok(undefined)) @@ -385,7 +392,6 @@ function sdk( const permissions: KiloClient["permission"]["list"] = input.permissions ?? (() => ok([])) const questions: KiloClient["question"]["list"] = input.questions ?? (() => ok([])) - spyOn(client.event, "subscribe").mockImplementation(subscribe) spyOn(client.global, "event").mockImplementation(globalEvent) spyOn(client.session, "promptAsync").mockImplementation(promptAsync) spyOn(client.session, "status").mockImplementation(status) @@ -757,18 +763,7 @@ describe("run stream transport", () => { transport.selectSubagent("child-1") global.push( - globalEvent({ - id: "evt-child-message", - type: "message.updated", - properties: { - sessionID: "child-1", - info: assistantMessage({ - sessionID: "child-1", - id: "msg-child-1", - parts: [], - }).info, - }, - }), + globalEvent(assistant("msg-child-1", "child-1")), ) global.push(globalEvent(textUpdated(textPart("txt-child-1", "msg-child-1", "hello", "child-1")))) diff --git a/packages/opencode/test/cli/tui/plugin-loader.test.ts b/packages/opencode/test/cli/tui/plugin-loader.test.ts index 43f1fa7a355..ccf4f02d047 100644 --- a/packages/opencode/test/cli/tui/plugin-loader.test.ts +++ b/packages/opencode/test/cli/tui/plugin-loader.test.ts @@ -3,9 +3,10 @@ import fs from "fs/promises" import path from "path" import { pathToFileURL } from "url" import { createTestKeymap } from "@opentui/keymap/testing" +import type { TuiAttentionSoundPack } from "@kilocode/plugin/tui" import { tmpdir } from "../../fixture/fixture" import { createTuiPluginApi } from "../../fixture/tui-plugin" -import { createTuiResolvedConfig } from "../../fixture/tui-runtime" +import { createTuiResolvedConfig, mockTuiRuntime } from "../../fixture/tui-runtime" import { Global } from "@opencode-ai/core/global" import { TuiConfig } from "../../../src/cli/cmd/tui/config/tui" import { Filesystem } from "@/util/filesystem" @@ -647,6 +648,36 @@ export default { } }) +test("does not bootstrap server plugins while initializing tui plugins", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + const marker = path.join(dir, "server-plugin-called.txt") + const plugin = path.join(dir, "server-plugin.ts") + await Bun.write( + plugin, + [ + "export default async () => {", + ` await Bun.write(${JSON.stringify(marker)}, "called")`, + " return {}", + "}", + "", + ].join("\n"), + ) + await Bun.write(path.join(dir, "opencode.json"), JSON.stringify({ plugin: [pathToFileURL(plugin).href] })) + return { marker } + }, + }) + + const mock = mockTuiRuntime(tmp.path, []) + try { + await TuiPluginRuntime.init({ api: createTuiPluginApi(), config: mock.config }) + await expect(fs.stat(tmp.extra.marker)).rejects.toThrow() + } finally { + await TuiPluginRuntime.dispose() + mock.restore() + } +}) + describe("tui.plugin.loader", () => { let data: Data @@ -824,6 +855,85 @@ test("plugin keymap proxy preserves real keymap receiver", async () => { } }) +test("auto-disposes plugin attention sound packs and resolves sound paths", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + const file = path.join(dir, "attention-soundpack-plugin.ts") + const spec = pathToFileURL(file).href + const absolute = path.join(dir, "sounds", "default.mp3") + const url = pathToFileURL(path.join(dir, "sounds", "error.mp3")).href + + await Bun.write( + file, + `export default { + id: "demo.attention.soundpack", + tui: async (api) => { + api.attention.soundboard.registerPack({ + id: "demo.pack", + sounds: { + default: ${JSON.stringify(absolute)}, + question: "sounds/question.mp3", + done: " sounds/done.mp3 ", + subagent_done: "sounds/subagent-done.mp3", + error: ${JSON.stringify(url)}, + nope: "sounds/nope.mp3", + permission: "", + }, + }) + }, +} +`, + ) + + return { spec } + }, + }) + + const packs: TuiAttentionSoundPack[] = [] + let dropped = 0 + const attention = { + soundboard: { + registerPack(pack: TuiAttentionSoundPack) { + packs.push(pack) + return () => { + dropped += 1 + } + }, + }, + } + const wait = spyOn(TuiConfig, "waitForDependencies").mockResolvedValue() + const cwd = spyOn(process, "cwd").mockImplementation(() => tmp.path) + + try { + await TuiPluginRuntime.init({ + api: createTuiPluginApi({ attention }), + config: createTuiResolvedConfig({ + plugin: [tmp.extra.spec], + plugin_origins: [{ spec: tmp.extra.spec, scope: "local", source: path.join(tmp.path, "tui.json") }], + }), + }) + + expect(packs).toEqual([ + { + id: "demo.pack", + sounds: { + default: path.join(tmp.path, "sounds", "default.mp3"), + question: path.join(tmp.path, "sounds", "question.mp3"), + done: path.join(tmp.path, "sounds", "done.mp3"), + subagent_done: path.join(tmp.path, "sounds", "subagent-done.mp3"), + error: path.join(tmp.path, "sounds", "error.mp3"), + }, + }, + ]) + expect(dropped).toBe(0) + } finally { + await TuiPluginRuntime.dispose() + expect(dropped).toBe(1) + cwd.mockRestore() + wait.mockRestore() + } +}) + test("auto-disposes plugin keymap transformers", async () => { await using tmp = await tmpdir({ init: async (dir) => { diff --git a/packages/opencode/test/cli/tui/prompt-submit-race.test.ts b/packages/opencode/test/cli/tui/prompt-submit-race.test.ts new file mode 100644 index 00000000000..df659a01d77 --- /dev/null +++ b/packages/opencode/test/cli/tui/prompt-submit-race.test.ts @@ -0,0 +1,98 @@ +import { describe, expect, test } from "bun:test" + +// Regression test for the prompt submit race in +// packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx (`submit`). +// +// Before the fix, two concurrent `submit()` calls (e.g. a double-pressed +// Enter, or the input's native onSubmit racing another dispatch) each +// passed the `if (!store.prompt.input) return false` guard, each +// `await sdk.client.session.create(...)`, and each only captured +// `inputText = store.prompt.input` AFTER that await. The first invocation +// finished, sent the prompt, and cleared the store; the second invocation, +// now past its await, read the cleared store and sent an empty prompt to a +// second freshly-created session - leaving an orphaned session with the +// user's actual text and a phantom session visible to the user containing +// only an assistant reply. +// +// `submitMirror` below has the exact shape of the production `submit()` +// after the fix: an in-flight `submitting` guard wraps the original body. +// Two concurrent invocations must result in exactly one submission carrying +// the user's text, with no empty-text submission. + +type Store = { input: string } + +type SubmitResult = { sessionID: string; text: string } + +type Harness = { + store: Store + submissions: SubmitResult[] + createSession(): Promise + sendPrompt(sessionID: string, text: string): Promise +} + +function createHarness(opts: { sessionCreateDelayMs: number }): Harness { + let sessionCounter = 0 + const submissions: SubmitResult[] = [] + + return { + store: { input: "" }, + submissions, + async createSession() { + sessionCounter += 1 + const id = `ses_${sessionCounter}` + await Bun.sleep(opts.sessionCreateDelayMs) + return id + }, + async sendPrompt(sessionID, text) { + submissions.push({ sessionID, text }) + }, + } +} + +function createSubmit() { + let submitting = false + return async function submit(h: Harness) { + if (submitting) return false + submitting = true + try { + if (!h.store.input) return false + const sessionID = await h.createSession() + const inputText = h.store.input + await h.sendPrompt(sessionID, inputText) + h.store.input = "" + return true + } finally { + submitting = false + } + } +} + +describe("Prompt.submit race", () => { + test("concurrent submits must not lose the user's text", async () => { + const submit = createSubmit() + const h = createHarness({ sessionCreateDelayMs: 5 }) + h.store.input = "Hello there." + + // Two invocations back-to-back, mimicking a double-Enter. + await Promise.all([submit(h), submit(h)]) + + // Every submission that did make it through must carry the actual user + // text, and no submission may have an empty text payload. + expect(h.submissions.every((s) => s.text === "Hello there.")).toBe(true) + expect(h.submissions.some((s) => s.text === "")).toBe(false) + }) + + test("a sequential second submit after clear is a no-op, not a phantom session", async () => { + const submit = createSubmit() + const h = createHarness({ sessionCreateDelayMs: 1 }) + h.store.input = "Hello there." + + await submit(h) + // After the first submission completes, the store is cleared; a second + // Enter on an empty input must not create a phantom session. + await submit(h) + + expect(h.submissions).toHaveLength(1) + expect(h.submissions[0].text).toBe("Hello there.") + }) +}) diff --git a/packages/opencode/test/cli/tui/use-event.test.tsx b/packages/opencode/test/cli/tui/use-event.test.tsx index 97dedc6489b..cf325d9c327 100644 --- a/packages/opencode/test/cli/tui/use-event.test.tsx +++ b/packages/opencode/test/cli/tui/use-event.test.tsx @@ -7,6 +7,8 @@ import { ProjectProvider, useProject } from "../../../src/cli/cmd/tui/context/pr import { SDKProvider } from "../../../src/cli/cmd/tui/context/sdk" import { useEvent } from "../../../src/cli/cmd/tui/context/event" +const projectID = "proj_test" + async function wait(fn: () => boolean, timeout = 2000) { const start = Date.now() while (!fn()) { @@ -15,9 +17,10 @@ async function wait(fn: () => boolean, timeout = 2000) { } } -function event(payload: Event, input: { directory: string; workspace?: string }): GlobalEvent { +function event(payload: Event, input: { directory: string; project?: string; workspace?: string }): GlobalEvent { return { directory: input.directory, + project: input.project, workspace: input.workspace, payload, } @@ -65,6 +68,13 @@ function createSource() { async function mount() { const source = createSource() const seen: Event[] = [] + const workspaces: Array = [] + const fetch = (async (input: RequestInfo | URL) => { + const url = new URL(input instanceof Request ? input.url : String(input)) + if (url.pathname === "/path") return Response.json({ home: "", state: "", config: "", directory: "/tmp/root" }) + if (url.pathname === "/project/current") return Response.json({ id: projectID }) + throw new Error(`unexpected request: ${url.pathname}`) + }) as typeof globalThis.fetch let project!: ReturnType let done!: () => void const ready = new Promise((resolve) => { @@ -72,30 +82,37 @@ async function mount() { }) const app = await testRender(() => ( - + { + onReady={async (ctx) => { project = ctx.project + await project.sync() done() }} seen={seen} + workspaces={workspaces} /> )) await ready - return { app, emit: source.emit, project, seen } + return { app, emit: source.emit, project, seen, workspaces } } -function Probe(props: { seen: Event[]; onReady: (ctx: { project: ReturnType }) => void }) { +function Probe(props: { + seen: Event[] + workspaces: Array + onReady: (ctx: { project: ReturnType }) => void +}) { const project = useProject() const event = useEvent() onMount(() => { - event.subscribe((evt) => { + event.subscribe((evt, { workspace }) => { props.seen.push(evt) + props.workspaces.push(workspace) }) props.onReady({ project }) }) @@ -104,25 +121,26 @@ function Probe(props: { seen: Event[]; onReady: (ctx: { project: ReturnType { - test("delivers matching directory events without an active workspace", async () => { - const { app, emit, seen } = await mount() + test("delivers events for the current project", async () => { + const { app, emit, seen, workspaces } = await mount() try { - emit(event(vcs("main"), { directory: "/tmp/root" })) + emit(event(vcs("main"), { directory: "/tmp/other", project: projectID, workspace: "ws_a" })) await wait(() => seen.length === 1) expect(seen).toEqual([vcs("main")]) + expect(workspaces).toEqual(["ws_a"]) } finally { app.renderer.destroy() } }) - test("ignores non-matching directory events without an active workspace", async () => { + test("ignores events for other projects", async () => { const { app, emit, seen } = await mount() try { - emit(event(vcs("other"), { directory: "/tmp/other" })) + emit(event(vcs("other"), { directory: "/tmp/root", project: "proj_other" })) await Bun.sleep(30) expect(seen).toHaveLength(0) @@ -131,12 +149,12 @@ describe("useEvent", () => { } }) - test("delivers matching workspace events when a workspace is active", async () => { + test("delivers current project events regardless of active workspace", async () => { const { app, emit, project, seen } = await mount() try { project.workspace.set("ws_a") - emit(event(vcs("ws"), { directory: "/tmp/other", workspace: "ws_a" })) + emit(event(vcs("ws"), { directory: "/tmp/other", project: projectID, workspace: "ws_b" })) await wait(() => seen.length === 1) @@ -146,20 +164,6 @@ describe("useEvent", () => { } }) - test("ignores non-matching workspace events when a workspace is active", async () => { - const { app, emit, project, seen } = await mount() - - try { - project.workspace.set("ws_a") - emit(event(vcs("ws"), { directory: "/tmp/root", workspace: "ws_b" })) - await Bun.sleep(30) - - expect(seen).toHaveLength(0) - } finally { - app.renderer.destroy() - } - }) - test("delivers truly global events even when a workspace is active", async () => { const { app, emit, project, seen } = await mount() diff --git a/packages/opencode/test/config/agent-color.test.ts b/packages/opencode/test/config/agent-color.test.ts index 1255b372990..d198080591b 100644 --- a/packages/opencode/test/config/agent-color.test.ts +++ b/packages/opencode/test/config/agent-color.test.ts @@ -1,73 +1,47 @@ -import { test, expect } from "bun:test" +import { expect } from "bun:test" import { Effect, Layer } from "effect" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" -import path from "path" -import { provideInstance, tmpdirScoped } from "../fixture/fixture" import { Config } from "@/config/config" import { Agent as AgentSvc } from "../../src/agent/agent" -import { Color } from "@/util/color" -import { AppRuntime } from "../../src/effect/app-runtime" import { testEffect } from "../lib/effect" -const it = testEffect(Layer.mergeAll(AgentSvc.defaultLayer, CrossSpawnSpawner.defaultLayer)) +const it = testEffect(Layer.mergeAll(Config.defaultLayer, AgentSvc.defaultLayer, CrossSpawnSpawner.defaultLayer)) -const writeConfig = (dir: string, agent: Config.Info["agent"]) => - Effect.promise(() => - Bun.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", // kilocode_change - agent, - }), - ), - ) - -// kilocode_change start -it.live("agent color parsed from project config", () => - Effect.gen(function* () { - const dir = yield* tmpdirScoped() - yield* writeConfig(dir, { - code: { color: "#FFA500" }, - plan: { color: "primary" }, - }) - - yield* Effect.gen(function* () { - const cfg = yield* Effect.promise(() => AppRuntime.runPromise(Config.Service.use((svc) => svc.get()))) - expect(cfg.agent?.["code"]?.color).toBe("#FFA500") +it.instance( + "agent color parsed from project config", + () => + Effect.gen(function* () { + const cfg = yield* Config.Service.use((svc) => svc.get()) + expect(cfg.agent?.["build"]?.color).toBe("#FFA500") expect(cfg.agent?.["plan"]?.color).toBe("primary") - }).pipe(provideInstance(dir)) - }), + }), + { + git: true, + config: { + agent: { + build: { color: "#FFA500" }, + plan: { color: "primary" }, + }, + }, + }, ) -// kilocode_change end - -// kilocode_change start -it.live("Agent.get includes color from config", () => - Effect.gen(function* () { - const dir = yield* tmpdirScoped() - yield* writeConfig(dir, { - plan: { color: "#A855F7" }, - build: { color: "accent" }, - }) - yield* Effect.gen(function* () { +it.instance( + "Agent.get includes color from config", + () => + Effect.gen(function* () { const plan = yield* AgentSvc.Service.use((svc) => svc.get("plan")) expect(plan?.color).toBe("#A855F7") const build = yield* AgentSvc.Service.use((svc) => svc.get("build")) expect(build?.color).toBe("accent") - }).pipe(provideInstance(dir)) - }), + }), + { + git: true, + config: { + agent: { + plan: { color: "#A855F7" }, + build: { color: "accent" }, + }, + }, + }, ) -// kilocode_change end - -test("Color.hexToAnsiBold converts valid hex to ANSI", () => { - const result = Color.hexToAnsiBold("#FFA500") - expect(result).toBe("\x1b[38;2;255;165;0m\x1b[1m") -}) - -test("Color.hexToAnsiBold returns undefined for invalid hex", () => { - expect(Color.hexToAnsiBold(undefined)).toBeUndefined() - expect(Color.hexToAnsiBold("")).toBeUndefined() - expect(Color.hexToAnsiBold("#FFF")).toBeUndefined() - expect(Color.hexToAnsiBold("FFA500")).toBeUndefined() - expect(Color.hexToAnsiBold("#GGGGGG")).toBeUndefined() -}) diff --git a/packages/opencode/test/config/config.test.ts b/packages/opencode/test/config/config.test.ts index d2d074336c7..746423f7bf6 100644 --- a/packages/opencode/test/config/config.test.ts +++ b/packages/opencode/test/config/config.test.ts @@ -145,6 +145,54 @@ test("loads config with defaults when no files exist", async () => { }) }) +test("creates global jsonc config with schema when no global configs exist", async () => { + await using tmp = await tmpdir() + const prev = Global.Path.config + ;(Global.Path as { config: string }).config = tmp.path + await clear(true) + + try { + await WithInstance.provide({ + directory: tmp.path, + fn: async () => { + await load() + }, + }) + + const content = await Filesystem.readText(path.join(tmp.path, "kilo.jsonc")) + expect(content).toContain('"$schema": "https://app.kilo.ai/config.json"') + } finally { + ;(Global.Path as { config: string }).config = prev + await clear(true) + } +}) + +test("does not create global config when KILO_CONFIG_DIR is set", async () => { + await using tmp = await tmpdir() + await using custom = await tmpdir() + const prevConfig = Global.Path.config + const prevEnv = process.env.KILO_CONFIG_DIR + ;(Global.Path as { config: string }).config = tmp.path + process.env.KILO_CONFIG_DIR = custom.path + await clear(true) + + try { + await WithInstance.provide({ + directory: tmp.path, + fn: async () => { + await load() + }, + }) + + expect(await Filesystem.exists(path.join(tmp.path, "opencode.jsonc"))).toBe(false) + } finally { + ;(Global.Path as { config: string }).config = prevConfig + if (prevEnv === undefined) delete process.env.KILO_CONFIG_DIR + else process.env.KILO_CONFIG_DIR = prevEnv + await clear(true) + } +}) + // kilocode_change start test("loads JSON config file", async () => { await using tmp = await tmpdir({ @@ -288,7 +336,7 @@ test("updates global config and omits empty shell key in jsonc", async () => { const file = path.join(tmp.path, "opencode.jsonc") const writtenConfig = await Filesystem.readText(file) - const parsed = ConfigParse.schema(Config.Info.zod, ConfigParse.jsonc(writtenConfig, file), file) + const parsed = ConfigParse.schema(Config.Info, ConfigParse.jsonc(writtenConfig, file), file) expect(writtenConfig).not.toContain('"shell"') expect(parsed.shell).toBeUndefined() expect(parsed.model).toBe("test/model") @@ -1801,8 +1849,8 @@ test("permission config preserves user key order", async () => { // kilocode_change end }) -test("Effect config parser preserves permission order while rejecting unknown top-level keys", () => { - const config = ConfigParse.effectSchema( +test("config parser preserves permission order while rejecting unknown top-level keys", () => { + const config = ConfigParse.schema( Config.Info, { permission: { @@ -1816,7 +1864,7 @@ test("Effect config parser preserves permission order while rejecting unknown to expect(Object.keys(config.permission!)).toEqual(["bash", "*", "edit"]) try { - ConfigParse.effectSchema(Config.Info, { invalid_field: true }, "test") + ConfigParse.schema(Config.Info, { invalid_field: true }, "test") throw new Error("expected config parse to fail") } catch (err) { const error = err as { data?: { issues?: Array<{ code?: string; keys?: string[]; path?: string[] }> } } @@ -2655,7 +2703,7 @@ describe("KILO_CONFIG_CONTENT token substitution", () => { // parseManagedPlist unit tests — pure function, no OS interaction test("parseManagedPlist strips MDM metadata keys", async () => { - const config = ConfigParse.effectSchema( + const config = ConfigParse.schema( Config.Info, ConfigParse.jsonc( await ConfigManaged.parseManagedPlist( @@ -2683,7 +2731,7 @@ test("parseManagedPlist strips MDM metadata keys", async () => { }) test("parseManagedPlist parses server settings", async () => { - const config = ConfigParse.effectSchema( + const config = ConfigParse.schema( Config.Info, ConfigParse.jsonc( await ConfigManaged.parseManagedPlist( @@ -2703,7 +2751,7 @@ test("parseManagedPlist parses server settings", async () => { }) test("parseManagedPlist parses permission rules", async () => { - const config = ConfigParse.effectSchema( + const config = ConfigParse.schema( Config.Info, ConfigParse.jsonc( await ConfigManaged.parseManagedPlist( @@ -2733,7 +2781,7 @@ test("parseManagedPlist parses permission rules", async () => { }) test("parseManagedPlist parses enabled_providers", async () => { - const config = ConfigParse.effectSchema( + const config = ConfigParse.schema( Config.Info, ConfigParse.jsonc( await ConfigManaged.parseManagedPlist( @@ -2750,7 +2798,7 @@ test("parseManagedPlist parses enabled_providers", async () => { }) test("parseManagedPlist handles empty config", async () => { - const config = ConfigParse.effectSchema( + const config = ConfigParse.schema( Config.Info, ConfigParse.jsonc( await ConfigManaged.parseManagedPlist(JSON.stringify({ $schema: "https://opencode.ai/config.json" })), diff --git a/packages/opencode/test/config/lsp.test.ts b/packages/opencode/test/config/lsp.test.ts index 1d24fe124de..ff0048a190a 100644 --- a/packages/opencode/test/config/lsp.test.ts +++ b/packages/opencode/test/config/lsp.test.ts @@ -7,10 +7,6 @@ import { ConfigLSP } from "../../src/config/lsp" // the server should attach to. Builtin server IDs and explicitly disabled // entries are exempt. // -// Both validation paths must honor this rule: -// - `Schema.decodeUnknownSync(ConfigLSP.Info)` (Effect layer) -// - `ConfigLSP.Info.zod.parse(...)` (derived Zod) -// // `typescript` is a builtin server id (see src/lsp/server.ts). describe("ConfigLSP.Info refinement", () => { const decodeEffect = Schema.decodeUnknownSync(ConfigLSP.Info) @@ -19,14 +15,11 @@ describe("ConfigLSP.Info refinement", () => { test("true and false pass (top-level toggle)", () => { expect(decodeEffect(true)).toBe(true) expect(decodeEffect(false)).toBe(false) - expect(ConfigLSP.Info.zod.parse(true)).toBe(true) - expect(ConfigLSP.Info.zod.parse(false)).toBe(false) }) test("builtin server with no extensions passes", () => { const input = { typescript: { command: ["typescript-language-server", "--stdio"] } } expect(decodeEffect(input)).toEqual(input) - expect(ConfigLSP.Info.zod.parse(input)).toEqual(input) }) test("custom server WITH extensions passes", () => { @@ -34,13 +27,11 @@ describe("ConfigLSP.Info refinement", () => { "my-lsp": { command: ["my-lsp-bin"], extensions: [".ml"] }, } expect(decodeEffect(input)).toEqual(input) - expect(ConfigLSP.Info.zod.parse(input)).toEqual(input) }) test("disabled custom server passes (no extensions needed)", () => { const input = { "my-lsp": { disabled: true as const } } expect(decodeEffect(input)).toEqual(input) - expect(ConfigLSP.Info.zod.parse(input)).toEqual(input) }) test("mix of builtin and custom with extensions passes", () => { @@ -49,7 +40,6 @@ describe("ConfigLSP.Info refinement", () => { "my-lsp": { command: ["my-lsp-bin"], extensions: [".ml"] }, } expect(decodeEffect(input)).toEqual(input) - expect(ConfigLSP.Info.zod.parse(input)).toEqual(input) }) }) @@ -60,19 +50,12 @@ describe("ConfigLSP.Info refinement", () => { expect(() => decodeEffect({ "my-lsp": { command: ["my-lsp-bin"] } })).toThrow(expectedMessage) }) - test("custom server WITHOUT extensions fails via derived Zod", () => { - const result = ConfigLSP.Info.zod.safeParse({ "my-lsp": { command: ["my-lsp-bin"] } }) - expect(result.success).toBe(false) - expect(result.error!.issues.some((i) => i.message === expectedMessage)).toBe(true) - }) - test("custom server with empty extensions array fails (extensions must be non-empty-truthy)", () => { // Boolean(['']) is true, so a non-empty array of strings is fine. // Boolean([]) is also true in JS, so empty arrays are accepted by the // refinement. This test documents current behavior. const input = { "my-lsp": { command: ["my-lsp-bin"], extensions: [] } } expect(decodeEffect(input)).toEqual(input) - expect(ConfigLSP.Info.zod.parse(input)).toEqual(input) }) test("custom server without extensions mixed with a valid builtin still fails", () => { @@ -81,7 +64,6 @@ describe("ConfigLSP.Info refinement", () => { "my-lsp": { command: ["my-lsp-bin"] }, } expect(() => decodeEffect(input)).toThrow(expectedMessage) - expect(ConfigLSP.Info.zod.safeParse(input).success).toBe(false) }) }) }) diff --git a/packages/opencode/test/config/tui.test.ts b/packages/opencode/test/config/tui.test.ts index 6e09c78163b..4a7cda2f813 100644 --- a/packages/opencode/test/config/tui.test.ts +++ b/packages/opencode/test/config/tui.test.ts @@ -1,115 +1,110 @@ -import { afterEach, beforeEach, expect, test } from "bun:test" +import { expect } from "bun:test" import path from "path" -import fs from "fs/promises" -import { provideTestInstance, tmpdir } from "../fixture/fixture" -import { InstanceRuntime } from "@/project/instance-runtime" -import { TuiConfig } from "../../src/cli/cmd/tui/config/tui" -import { Config } from "@/config/config" -import { Global } from "@opencode-ai/core/global" -import { Filesystem } from "@/util/filesystem" -import { AppRuntime } from "../../src/effect/app-runtime" +import { pathToFileURL } from "url" import { Effect, Layer } from "effect" -import { CurrentWorkingDirectory } from "@/cli/cmd/tui/config/cwd" +import { AppFileSystem } from "@opencode-ai/core/filesystem" +import { Global } from "@opencode-ai/core/global" +import { Flag } from "@opencode-ai/core/flag/flag" +import { Config } from "@/config/config" import { ConfigPlugin } from "@/config/plugin" +import { CurrentWorkingDirectory } from "@/cli/cmd/tui/config/cwd" +import { TuiConfig } from "../../src/cli/cmd/tui/config/tui" +import { TestInstance } from "../fixture/fixture" +import { testEffect } from "../lib/effect" -const wintest = process.platform === "win32" ? test : test.skip -const clear = async (wait = false) => { - await AppRuntime.runPromise(Config.Service.use((svc) => svc.invalidate())) - if (wait) await InstanceRuntime.disposeAllInstances() -} -const load = () => AppRuntime.runPromise(Config.Service.use((svc) => svc.get())) - -beforeEach(async () => { - await clear(true) -}) - -const getTuiConfig = async (directory: string) => - Effect.runPromise( - TuiConfig.Service.use((svc) => svc.get()).pipe( - Effect.provide(TuiConfig.defaultLayer.pipe(Layer.provide(Layer.succeed(CurrentWorkingDirectory, directory)))), - ), - ) +const it = testEffect(Layer.mergeAll(Config.defaultLayer, AppFileSystem.defaultLayer)) +const winIt = process.platform === "win32" ? it.instance : it.instance.skip -async function withPlatform(platform: typeof process.platform, fn: () => Promise) { - const original = Object.getOwnPropertyDescriptor(process, "platform") - Object.defineProperty(process, "platform", { - ...original, - value: platform, - }) - try { - return await fn() - } finally { - if (original) Object.defineProperty(process, "platform", original) - } -} +const globalConfigFiles = ["kilo.json", "kilo.jsonc", "tui.json", "tui.jsonc"].map((file) => + path.join(Global.Path.config, file), +) -afterEach(async () => { +const cleanState = Effect.gen(function* () { + const fs = yield* AppFileSystem.Service delete process.env.KILO_CONFIG delete process.env.KILO_TUI_CONFIG - // kilocode_change start - await fs.rm(path.join(Global.Path.config, "kilo.json"), { force: true }).catch(() => {}) - await fs.rm(path.join(Global.Path.config, "kilo.jsonc"), { force: true }).catch(() => {}) - // kilocode_change end - await fs.rm(path.join(Global.Path.config, "tui.json"), { force: true }).catch(() => {}) - await fs.rm(path.join(Global.Path.config, "tui.jsonc"), { force: true }).catch(() => {}) - await clear(true) -}) + yield* Effect.forEach(globalConfigFiles, (file) => fs.remove(file, { force: true }).pipe(Effect.ignore), { + discard: true, + }) +}) + +const withCleanState = (self: Effect.Effect) => + Effect.acquireUseRelease( + Effect.gen(function* () { + const disabled = Flag.KILO_DISABLE_DEFAULT_PLUGINS + Flag.KILO_DISABLE_DEFAULT_PLUGINS = true + yield* cleanState + return disabled + }), + () => self, + (disabled) => + Effect.gen(function* () { + Flag.KILO_DISABLE_DEFAULT_PLUGINS = disabled + yield* cleanState + }), + ) -test("keeps server and tui plugin merge semantics aligned", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - const local = path.join(dir, ".kilo") // kilocode_change - await fs.mkdir(local, { recursive: true }) - - await Bun.write( - path.join(Global.Path.config, "kilo.json"), // kilocode_change - JSON.stringify( - { - plugin: [["shared-plugin@1.0.0", { source: "global" }], "global-only@1.0.0"], - }, - null, - 2, - ), - ) - await Bun.write( - path.join(Global.Path.config, "tui.json"), - JSON.stringify( - { - plugin: [["shared-plugin@1.0.0", { source: "global" }], "global-only@1.0.0"], - }, - null, - 2, - ), - ) +const withEnv = (name: string, value: string | undefined, self: Effect.Effect) => + Effect.acquireUseRelease( + Effect.sync(() => { + const previous = process.env[name] + if (value === undefined) delete process.env[name] + else process.env[name] = value + return previous + }), + () => self, + (previous) => + Effect.sync(() => { + if (previous === undefined) delete process.env[name] + else process.env[name] = previous + }), + ) - await Bun.write( - path.join(local, "kilo.json"), // kilocode_change - JSON.stringify( - { - plugin: [["shared-plugin@2.0.0", { source: "local" }], "local-only@1.0.0"], - }, - null, - 2, - ), - ) - await Bun.write( - path.join(local, "tui.json"), - JSON.stringify( - { - plugin: [["shared-plugin@2.0.0", { source: "local" }], "local-only@1.0.0"], - }, - null, - 2, - ), - ) - }, - }) +const withPlatform = (platform: typeof process.platform, self: Effect.Effect) => + Effect.acquireUseRelease( + Effect.sync(() => { + const original = Object.getOwnPropertyDescriptor(process, "platform") + Object.defineProperty(process, "platform", { + ...original, + value: platform, + }) + return original + }), + () => self, + (original) => + Effect.sync(() => { + if (original) Object.defineProperty(process, "platform", original) + }), + ) + +const getTuiConfig = (directory: string) => + TuiConfig.Service.use((svc) => svc.get()).pipe( + Effect.provide(TuiConfig.defaultLayer.pipe(Layer.provide(Layer.succeed(CurrentWorkingDirectory, directory)))), + ) - await provideTestInstance({ - directory: tmp.path, - fn: async () => { - const server = await load() - const tui = await getTuiConfig(tmp.path) +it.instance("keeps server and tui plugin merge semantics aligned", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + const local = path.join(test.directory, ".opencode") + yield* fs.makeDirectory(local, { recursive: true }) + + yield* fs.writeJson(path.join(Global.Path.config, "kilo.json"), { + plugin: [["shared-plugin@1.0.0", { source: "global" }], "global-only@1.0.0"], + }) + yield* fs.writeJson(path.join(Global.Path.config, "tui.json"), { + plugin: [["shared-plugin@1.0.0", { source: "global" }], "global-only@1.0.0"], + }) + yield* fs.writeJson(path.join(local, "kilo.json"), { + plugin: [["shared-plugin@2.0.0", { source: "local" }], "local-only@1.0.0"], + }) + yield* fs.writeJson(path.join(local, "tui.json"), { + plugin: [["shared-plugin@2.0.0", { source: "local" }], "local-only@1.0.0"], + }) + + const server = yield* Config.Service.use((svc) => svc.get()) + const tui = yield* getTuiConfig(test.directory) const serverPlugins = (server.plugin ?? []).map((item) => ConfigPlugin.pluginSpecifier(item)) const tuiPlugins = (tui.plugin ?? []).map((item) => ConfigPlugin.pluginSpecifier(item)) @@ -122,186 +117,228 @@ test("keeps server and tui plugin merge semantics aligned", async () => { expect(serverOrigins.map((item) => ConfigPlugin.pluginSpecifier(item.spec))).toEqual(serverPlugins) expect(tuiOrigins.map((item) => ConfigPlugin.pluginSpecifier(item.spec))).toEqual(tuiPlugins) expect(serverOrigins.map((item) => item.scope)).toEqual(tuiOrigins.map((item) => item.scope)) - }, - }) -}) - -test("loads tui config with the same precedence order as server config paths", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(Global.Path.config, "tui.json"), JSON.stringify({ theme: "global" }, null, 2)) - await Bun.write(path.join(dir, "tui.json"), JSON.stringify({ theme: "project" }, null, 2)) - await fs.mkdir(path.join(dir, ".kilo"), { recursive: true }) // kilocode_change - await Bun.write( - path.join(dir, ".kilo", "tui.json"), // kilocode_change + }), + ), +) + +it.instance("loads tui config with the same precedence order as server config paths", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeJson(path.join(Global.Path.config, "tui.json"), { theme: "global" }) + yield* fs.writeJson(path.join(test.directory, "tui.json"), { theme: "project" }) + yield* fs.writeWithDirs( + path.join(test.directory, ".opencode", "tui.json"), JSON.stringify({ theme: "local", diff_style: "stacked" }, null, 2), ) - }, - }) - - const config = await getTuiConfig(tmp.path) - expect(config.theme).toBe("local") - expect(config.diff_style).toBe("stacked") -}) - -test("migrates tui-specific keys from kilo.json when tui.json does not exist", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "kilo.json"), - JSON.stringify( - { - theme: "migrated-theme", - tui: { scroll_speed: 5 }, - keybinds: { app_exit: "ctrl+q" }, - }, - null, - 2, - ), - ) - }, - }) - - const config = await getTuiConfig(tmp.path) - expect(config.theme).toBe("migrated-theme") - expect(config.scroll_speed).toBe(5) - expect(config.keybinds.get("app.exit")?.[0]?.key).toBe("ctrl+q") - const text = await Filesystem.readText(path.join(tmp.path, "tui.json")) - expect(JSON.parse(text)).toMatchObject({ - theme: "migrated-theme", - scroll_speed: 5, - }) - const server = JSON.parse(await Filesystem.readText(path.join(tmp.path, "kilo.json"))) // kilocode_change - expect(server.theme).toBeUndefined() - expect(server.keybinds).toBeUndefined() - expect(server.tui).toBeUndefined() - expect(await Filesystem.exists(path.join(tmp.path, "kilo.json.tui-migration.bak"))).toBe(true) // kilocode_change - expect(await Filesystem.exists(path.join(tmp.path, "tui.json"))).toBe(true) -}) - -test("migrates project legacy tui keys even when global tui.json already exists", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(Global.Path.config, "tui.json"), JSON.stringify({ theme: "global" }, null, 2)) - await Bun.write( - path.join(dir, "kilo.json"), - JSON.stringify( - { - theme: "project-migrated", - tui: { scroll_speed: 2 }, - }, - null, - 2, - ), - ) - }, - }) - - const config = await getTuiConfig(tmp.path) - expect(config.theme).toBe("project-migrated") - expect(config.scroll_speed).toBe(2) - expect(await Filesystem.exists(path.join(tmp.path, "tui.json"))).toBe(true) - const server = JSON.parse(await Filesystem.readText(path.join(tmp.path, "kilo.json"))) - expect(server.theme).toBeUndefined() - expect(server.tui).toBeUndefined() -}) - -test("drops unknown legacy tui keys during migration", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "kilo.json"), - JSON.stringify( - { - theme: "migrated-theme", - tui: { scroll_speed: 2, foo: 1 }, + const config = yield* getTuiConfig(test.directory) + expect(config.theme).toBe("local") + expect(config.diff_style).toBe("stacked") + }), + ), +) + +it.instance("resolves attention config defaults and overrides", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + + expect((yield* getTuiConfig(test.directory)).attention).toEqual({ + enabled: false, + notifications: true, + sound: true, + volume: 0.4, + sound_pack: "opencode.default", + sounds: {}, + }) + + yield* fs.writeJson(path.join(test.directory, "tui.json"), { + attention: { + enabled: false, + notifications: false, + sound: false, + volume: 0.7, + sound_pack: "acme.soft", + sounds: { + default: path.join(test.directory, "default.mp3"), + question: pathToFileURL(path.join(test.directory, "question.mp3")).href, + error: "./error.mp3", + subagent_done: "./subagent-done.mp3", }, - null, - 2, - ), - ) - }, - }) - - const config = await getTuiConfig(tmp.path) - expect(config.theme).toBe("migrated-theme") - expect(config.scroll_speed).toBe(2) - - const text = await Filesystem.readText(path.join(tmp.path, "tui.json")) - const migrated = JSON.parse(text) - expect(migrated.scroll_speed).toBe(2) - expect(migrated.foo).toBeUndefined() -}) - -test("skips migration when kilo.jsonc is syntactically invalid", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "kilo.jsonc"), + }, + }) + + expect((yield* getTuiConfig(test.directory)).attention).toEqual({ + enabled: false, + notifications: false, + sound: false, + volume: 0.7, + sound_pack: "acme.soft", + sounds: { + default: path.join(test.directory, "default.mp3"), + question: path.join(test.directory, "question.mp3"), + error: path.join(test.directory, "error.mp3"), + subagent_done: path.join(test.directory, "subagent-done.mp3"), + }, + }) + }), + ), +) + +it.instance("migrates tui-specific keys from kilo.json when tui.json does not exist", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + const source = path.join(test.directory, "kilo.json") + yield* fs.writeJson(source, { + theme: "migrated-theme", + tui: { scroll_speed: 5 }, + keybinds: { app_exit: "ctrl+q" }, + }) + + const config = yield* getTuiConfig(test.directory) + expect(config.theme).toBe("migrated-theme") + expect(config.scroll_speed).toBe(5) + expect(config.keybinds.get("app.exit")?.[0]?.key).toBe("ctrl+q") + expect(JSON.parse(yield* fs.readFileString(path.join(test.directory, "tui.json")))).toMatchObject({ + theme: "migrated-theme", + scroll_speed: 5, + }) + const server = JSON.parse(yield* fs.readFileString(source)) + expect(server.theme).toBeUndefined() + expect(server.keybinds).toBeUndefined() + expect(server.tui).toBeUndefined() + expect(yield* fs.existsSafe(path.join(test.directory, "kilo.json.tui-migration.bak"))).toBe(true) + expect(yield* fs.existsSafe(path.join(test.directory, "tui.json"))).toBe(true) + }), + ), +) + +it.instance("migrates project legacy tui keys even when global tui.json already exists", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeJson(path.join(Global.Path.config, "tui.json"), { theme: "global" }) + yield* fs.writeJson(path.join(test.directory, "kilo.json"), { + theme: "project-migrated", + tui: { scroll_speed: 2 }, + }) + + const config = yield* getTuiConfig(test.directory) + expect(config.theme).toBe("project-migrated") + expect(config.scroll_speed).toBe(2) + expect(yield* fs.existsSafe(path.join(test.directory, "tui.json"))).toBe(true) + + const server = JSON.parse(yield* fs.readFileString(path.join(test.directory, "kilo.json"))) + expect(server.theme).toBeUndefined() + expect(server.tui).toBeUndefined() + }), + ), +) + +it.instance("drops unknown legacy tui keys during migration", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeJson(path.join(test.directory, "kilo.json"), { + theme: "migrated-theme", + tui: { scroll_speed: 2, foo: 1 }, + }) + + const config = yield* getTuiConfig(test.directory) + expect(config.theme).toBe("migrated-theme") + expect(config.scroll_speed).toBe(2) + + const migrated = JSON.parse(yield* fs.readFileString(path.join(test.directory, "tui.json"))) + expect(migrated.scroll_speed).toBe(2) + expect(migrated.foo).toBeUndefined() + }), + ), +) + +it.instance("skips migration when kilo.jsonc is syntactically invalid", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeFileString( + path.join(test.directory, "kilo.jsonc"), `{ "theme": "broken-theme", "tui": { "scroll_speed": 2 } "username": "still-broken" }`, ) - }, - }) - - const config = await getTuiConfig(tmp.path) - expect(config.theme).toBeUndefined() - expect(config.scroll_speed).toBeUndefined() - expect(await Filesystem.exists(path.join(tmp.path, "tui.json"))).toBe(false) - expect(await Filesystem.exists(path.join(tmp.path, "kilo.jsonc.tui-migration.bak"))).toBe(false) // kilocode_change - const source = await Filesystem.readText(path.join(tmp.path, "kilo.jsonc")) // kilocode_change - expect(source).toContain('"theme": "broken-theme"') - expect(source).toContain('"tui": { "scroll_speed": 2 }') -}) - -test("skips migration when tui.json already exists", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "kilo.json"), JSON.stringify({ theme: "legacy" }, null, 2)) - await Bun.write(path.join(dir, "tui.json"), JSON.stringify({ diff_style: "stacked" }, null, 2)) - }, - }) - - const config = await getTuiConfig(tmp.path) - expect(config.diff_style).toBe("stacked") - expect(config.theme).toBeUndefined() - - const server = JSON.parse(await Filesystem.readText(path.join(tmp.path, "kilo.json"))) // kilocode_change - expect(server.theme).toBe("legacy") - expect(await Filesystem.exists(path.join(tmp.path, "kilo.json.tui-migration.bak"))).toBe(false) // kilocode_change -}) - -test("continues loading tui config when legacy source cannot be stripped", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "kilo.json"), JSON.stringify({ theme: "readonly-theme" }, null, 2)) - }, - }) - const source = path.join(tmp.path, "kilo.json") - await fs.chmod(source, 0o444) - - try { - const config = await getTuiConfig(tmp.path) - expect(config.theme).toBe("readonly-theme") - expect(await Filesystem.exists(path.join(tmp.path, "tui.json"))).toBe(true) - - const server = JSON.parse(await Filesystem.readText(source)) - expect(server.theme).toBe("readonly-theme") - } finally { - await fs.chmod(source, 0o644) - } -}) - -test("migration backup preserves JSONC comments", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "kilo.jsonc"), + const config = yield* getTuiConfig(test.directory) + expect(config.theme).toBeUndefined() + expect(config.scroll_speed).toBeUndefined() + expect(yield* fs.existsSafe(path.join(test.directory, "tui.json"))).toBe(false) + expect(yield* fs.existsSafe(path.join(test.directory, "kilo.jsonc.tui-migration.bak"))).toBe(false) + const source = yield* fs.readFileString(path.join(test.directory, "kilo.jsonc")) + expect(source).toContain('"theme": "broken-theme"') + expect(source).toContain('"tui": { "scroll_speed": 2 }') + }), + ), +) + +it.instance("skips migration when tui.json already exists", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeJson(path.join(test.directory, "kilo.json"), { theme: "legacy" }) + yield* fs.writeJson(path.join(test.directory, "tui.json"), { diff_style: "stacked" }) + + const config = yield* getTuiConfig(test.directory) + expect(config.diff_style).toBe("stacked") + expect(config.theme).toBeUndefined() + + const server = JSON.parse(yield* fs.readFileString(path.join(test.directory, "kilo.json"))) + expect(server.theme).toBe("legacy") + expect(yield* fs.existsSafe(path.join(test.directory, "kilo.json.tui-migration.bak"))).toBe(false) + }), + ), +) + +it.instance("continues loading tui config when legacy source cannot be stripped", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + const source = path.join(test.directory, "kilo.json") + yield* fs.writeJson(source, { theme: "readonly-theme" }) + + yield* Effect.acquireUseRelease( + fs.chmod(source, 0o444), + () => + Effect.gen(function* () { + const config = yield* getTuiConfig(test.directory) + expect(config.theme).toBe("readonly-theme") + expect(yield* fs.existsSafe(path.join(test.directory, "tui.json"))).toBe(true) + + const server = JSON.parse(yield* fs.readFileString(source)) + expect(server.theme).toBe("readonly-theme") + }), + () => fs.chmod(source, 0o644).pipe(Effect.ignore), + ) + }), + ), +) + +it.instance("migration backup preserves JSONC comments", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeFileString( + path.join(test.directory, "kilo.jsonc"), `{ // top-level comment "theme": "jsonc-theme", @@ -311,502 +348,539 @@ test("migration backup preserves JSONC comments", async () => { } }`, ) - }, - }) - - await getTuiConfig(tmp.path) - const backup = await Filesystem.readText(path.join(tmp.path, "kilo.jsonc.tui-migration.bak")) // kilocode_change - expect(backup).toContain("// top-level comment") - expect(backup).toContain("// nested comment") - expect(backup).toContain('"theme": "jsonc-theme"') - expect(backup).toContain('"scroll_speed": 1.5') -}) - -// kilocode_change start -test("migrates legacy tui keys across multiple kilo.json levels", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - const nested = path.join(dir, "apps", "client") - await fs.mkdir(nested, { recursive: true }) - await Bun.write(path.join(dir, "kilo.json"), JSON.stringify({ theme: "root-theme" }, null, 2)) - await Bun.write(path.join(nested, "kilo.json"), JSON.stringify({ theme: "nested-theme" }, null, 2)) - // kilocode_change end - }, - }) - const config = await getTuiConfig(path.join(tmp.path, "apps", "client")) - expect(config.theme).toBe("nested-theme") - expect(await Filesystem.exists(path.join(tmp.path, "tui.json"))).toBe(true) - expect(await Filesystem.exists(path.join(tmp.path, "apps", "client", "tui.json"))).toBe(true) -}) -test("flattens nested tui key inside tui.json", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "tui.json"), - JSON.stringify({ - theme: "outer", - tui: { scroll_speed: 3, diff_style: "stacked" }, + yield* getTuiConfig(test.directory) + const backup = yield* fs.readFileString(path.join(test.directory, "kilo.jsonc.tui-migration.bak")) + expect(backup).toContain("// top-level comment") + expect(backup).toContain("// nested comment") + expect(backup).toContain('"theme": "jsonc-theme"') + expect(backup).toContain('"scroll_speed": 1.5') + }), + ), +) + +it.instance("migrates legacy tui keys across multiple kilo.json levels", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + const nested = path.join(test.directory, "apps", "client") + yield* fs.makeDirectory(nested, { recursive: true }) + yield* fs.writeJson(path.join(test.directory, "kilo.json"), { theme: "root-theme" }) + yield* fs.writeJson(path.join(nested, "kilo.json"), { theme: "nested-theme" }) + + const config = yield* getTuiConfig(nested) + expect(config.theme).toBe("nested-theme") + expect(yield* fs.existsSafe(path.join(test.directory, "tui.json"))).toBe(true) + expect(yield* fs.existsSafe(path.join(nested, "tui.json"))).toBe(true) + }), + ), +) + +it.instance("flattens nested tui key inside tui.json", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeJson(path.join(test.directory, "tui.json"), { + theme: "outer", + tui: { scroll_speed: 3, diff_style: "stacked" }, + }) + + const config = yield* getTuiConfig(test.directory) + expect(config.scroll_speed).toBe(3) + expect(config.diff_style).toBe("stacked") + expect(config.theme).toBe("outer") + }), + ), +) + +it.instance("top-level keys in tui.json take precedence over nested tui key", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeJson(path.join(test.directory, "tui.json"), { + diff_style: "auto", + tui: { diff_style: "stacked", scroll_speed: 2 }, + }) + + const config = yield* getTuiConfig(test.directory) + expect(config.diff_style).toBe("auto") + expect(config.scroll_speed).toBe(2) + }), + ), +) + +it.instance("project config takes precedence over KILO_TUI_CONFIG (matches KILO_CONFIG)", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + const custom = path.join(test.directory, "custom-tui.json") + yield* fs.writeJson(path.join(test.directory, "tui.json"), { theme: "project", diff_style: "auto" }) + yield* fs.writeJson(custom, { theme: "custom", diff_style: "stacked" }) + + yield* withEnv( + "KILO_TUI_CONFIG", + custom, + Effect.gen(function* () { + const config = yield* getTuiConfig(test.directory) + expect(config.theme).toBe("project") + expect(config.diff_style).toBe("auto") }), ) - }, - }) - - const config = await getTuiConfig(tmp.path) - expect(config.scroll_speed).toBe(3) - expect(config.diff_style).toBe("stacked") - // top-level keys take precedence over nested tui keys - expect(config.theme).toBe("outer") -}) - -test("top-level keys in tui.json take precedence over nested tui key", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "tui.json"), - JSON.stringify({ - diff_style: "auto", - tui: { diff_style: "stacked", scroll_speed: 2 }, - }), + }), + ), +) + +it.instance("merges keybind overrides across precedence layers", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeJson(path.join(Global.Path.config, "tui.json"), { keybinds: { app_exit: "ctrl+q" } }) + yield* fs.writeJson(path.join(test.directory, "tui.json"), { keybinds: { theme_list: "ctrl+k" } }) + + const config = yield* getTuiConfig(test.directory) + expect(config.keybinds.get("app.exit")?.[0]?.key).toBe("ctrl+q") + expect(config.keybinds.get("theme.switch")?.[0]?.key).toBe("ctrl+k") + }), + ), +) + +it.instance("ignores unknown keybind names without dropping valid overrides from the same file", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeJson(path.join(Global.Path.config, "tui.json"), { + keybinds: { + session_delete: "ctrl+d", + not_a_real_keybind: "ctrl+q", + }, + }) + + const config = yield* getTuiConfig(test.directory) + expect(config.keybinds.get("session.delete")?.[0]?.key).toBe("ctrl+d") + expect(config.keybinds.get("not_a_real_keybind")).toEqual([]) + }), + ), +) + +it.instance("resolves keybind lookup from canonical keybinds", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeJson(path.join(test.directory, "tui.json"), { + keybinds: { + leader: { key: { name: "g", ctrl: true } }, + command_list: "alt+p", + which_key_toggle: "alt+k", + editor_open: "ctrl+e", + "prompt.autocomplete.next": "ctrl+j", + "dialog.mcp.toggle": "ctrl+t", + model_favorite_toggle: "ctrl+f", + "dialog.plugins.install": "shift+i", + }, + leader_timeout: 1234, + }) + + const config = yield* getTuiConfig(test.directory) + expect(config.keybinds.get("leader")?.[0]?.key).toEqual({ name: "g", ctrl: true }) + expect(config.leader_timeout).toBe(1234) + expect(config.keybinds.get("command.palette.show")?.[0]?.key).toBe("alt+p") + expect(config.keybinds.get("session.new")?.[0]?.key).toBe("n") + expect(config.keybinds.get("which-key.toggle")?.[0]?.key).toBe("alt+k") + expect(config.keybinds.get("which-key.layout.toggle")?.[0]?.key).toBe("ctrl+alt+shift+k") + expect(config.keybinds.get("which-key.pending.toggle")?.[0]?.key).toBe("ctrl+alt+shift+p") + expect(config.keybinds.get("which-key.group.next")?.[0]?.key).toBe("ctrl+alt+right,ctrl+alt+]") + expect((config.keybinds.get("which-key.toggle")?.[0] as { desc?: unknown } | undefined)?.desc).toBe( + "Toggle which-key panel", ) - }, - }) - - const config = await getTuiConfig(tmp.path) - expect(config.diff_style).toBe("auto") - expect(config.scroll_speed).toBe(2) -}) - -test("project config takes precedence over KILO_TUI_CONFIG (matches KILO_CONFIG)", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "tui.json"), JSON.stringify({ theme: "project", diff_style: "auto" })) - const custom = path.join(dir, "custom-tui.json") - await Bun.write(custom, JSON.stringify({ theme: "custom", diff_style: "stacked" })) - process.env.KILO_TUI_CONFIG = custom - }, - }) - - const config = await getTuiConfig(tmp.path) - // project tui.json overrides the custom path, same as server config precedence - expect(config.theme).toBe("project") - // project also set diff_style, so that wins - expect(config.diff_style).toBe("auto") -}) - -test("merges keybind overrides across precedence layers", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(Global.Path.config, "tui.json"), JSON.stringify({ keybinds: { app_exit: "ctrl+q" } })) - await Bun.write(path.join(dir, "tui.json"), JSON.stringify({ keybinds: { theme_list: "ctrl+k" } })) - }, - }) - const config = await getTuiConfig(tmp.path) - expect(config.keybinds.get("app.exit")?.[0]?.key).toBe("ctrl+q") - expect(config.keybinds.get("theme.switch")?.[0]?.key).toBe("ctrl+k") -}) - -test("resolves keybind lookup from canonical keybinds", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "tui.json"), - JSON.stringify({ + expect(config.keybinds.get("prompt.editor")?.[0]?.key).toBe("ctrl+e") + expect(config.keybinds.get("prompt.autocomplete.next")?.[0]?.key).toBe("ctrl+j") + expect(config.keybinds.get("dialog.mcp.toggle")?.[0]?.key).toBe("ctrl+t") + expect(config.keybinds.get("model.dialog.favorite")?.[0]?.key).toBe("ctrl+f") + expect(config.keybinds.get("dialog.plugins.install")?.[0]?.key).toBe("shift+i") + expect( + config.keybinds.gather("plugins.dialog", ["dialog.plugins.install"]).map((binding) => binding.cmd), + ).toEqual(["dialog.plugins.install"]) + }), + ), +) + +it.instance("keybinds accept OpenTUI binding specs", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeJson(path.join(test.directory, "tui.json"), { + keybinds: { + command_list: [{ key: "alt+p", preventDefault: false }], + editor_open: { key: { name: "e", ctrl: true }, group: "Explicit" }, + "prompt.autocomplete.next": false, + plugin_manager: "ctrl+shift+p", + }, + }) + + const config = yield* getTuiConfig(test.directory) + expect(config.keybinds.get("command.palette.show")).toEqual([ + { key: "alt+p", cmd: "command.palette.show", preventDefault: false, desc: "List available commands" }, + ]) + expect(config.keybinds.get("prompt.editor")?.[0]).toMatchObject({ + key: { name: "e", ctrl: true }, + cmd: "prompt.editor", + group: "Explicit", + }) + expect(config.keybinds.get("prompt.autocomplete.next")).toEqual([]) + expect(config.keybinds.get("plugins.list")?.[0]?.key).toBe("ctrl+shift+p") + }), + ), +) + +winIt("defaults Ctrl+Z to input undo on Windows", () => + withCleanState( + Effect.gen(function* () { + const test = yield* TestInstance + const config = yield* getTuiConfig(test.directory) + expect(config.keybinds.get("terminal.suspend")).toEqual([]) + expect(config.keybinds.get("input.undo")?.[0]?.key).toBe("ctrl+z,ctrl+-,super+z") + }), + ), +) + +winIt("keeps explicit input undo overrides on Windows", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeJson(path.join(test.directory, "tui.json"), { keybinds: { input_undo: "ctrl+y" } }) + + const config = yield* getTuiConfig(test.directory) + expect(config.keybinds.get("terminal.suspend")).toEqual([]) + expect(config.keybinds.get("input.undo")?.[0]?.key).toBe("ctrl+y") + }), + ), +) + +winIt("ignores terminal suspend bindings on Windows", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeJson(path.join(test.directory, "tui.json"), { keybinds: { terminal_suspend: "alt+z" } }) + + const config = yield* getTuiConfig(test.directory) + expect(config.keybinds.get("terminal.suspend")).toEqual([]) + expect(config.keybinds.get("input.undo")?.[0]?.key).toBe("ctrl+z,ctrl+-,super+z") + }), + ), +) + +it.instance("applies Windows keybind defaults", () => + withCleanState( + withPlatform( + "win32", + Effect.gen(function* () { + const test = yield* TestInstance + const config = yield* getTuiConfig(test.directory) + expect(config.keybinds.get("terminal.suspend")).toEqual([]) + expect(config.keybinds.get("input.undo")?.[0]?.key).toBe("ctrl+z,ctrl+-,super+z") + }), + ), + ), +) + +it.instance("ignores explicit keybind terminal suspend binding on Windows", () => + withCleanState( + withPlatform( + "win32", + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeJson(path.join(test.directory, "tui.json"), { keybinds: { - leader: { key: { name: "g", ctrl: true } }, - command_list: "alt+p", - which_key_toggle: "alt+k", - editor_open: "ctrl+e", - "prompt.autocomplete.next": "ctrl+j", - "dialog.mcp.toggle": "ctrl+t", - model_favorite_toggle: "ctrl+f", - "dialog.plugins.install": "shift+i", + terminal_suspend: "alt+z", }, - leader_timeout: 1234, - }), - ) - }, - }) - - const config = await getTuiConfig(tmp.path) - expect(config.keybinds.get("leader")?.[0]?.key).toEqual({ name: "g", ctrl: true }) - expect(config.leader_timeout).toBe(1234) - expect(config.keybinds.get("command.palette.show")?.[0]?.key).toBe("alt+p") - expect(config.keybinds.get("session.new")?.[0]?.key).toBe("n") - expect(config.keybinds.get("which-key.toggle")?.[0]?.key).toBe("alt+k") - expect(config.keybinds.get("which-key.layout.toggle")?.[0]?.key).toBe("ctrl+alt+shift+k") - expect(config.keybinds.get("which-key.pending.toggle")?.[0]?.key).toBe("ctrl+alt+shift+p") - expect(config.keybinds.get("which-key.group.next")?.[0]?.key).toBe("ctrl+alt+right,ctrl+alt+]") - expect((config.keybinds.get("which-key.toggle")?.[0] as { desc?: unknown } | undefined)?.desc).toBe( - "Toggle which-key panel", - ) - expect(config.keybinds.get("prompt.editor")?.[0]?.key).toBe("ctrl+e") - expect(config.keybinds.get("prompt.autocomplete.next")?.[0]?.key).toBe("ctrl+j") - expect(config.keybinds.get("dialog.mcp.toggle")?.[0]?.key).toBe("ctrl+t") - expect(config.keybinds.get("model.dialog.favorite")?.[0]?.key).toBe("ctrl+f") - expect(config.keybinds.get("dialog.plugins.install")?.[0]?.key).toBe("shift+i") - expect(config.keybinds.gather("plugins.dialog", ["dialog.plugins.install"]).map((binding) => binding.cmd)).toEqual([ - "dialog.plugins.install", - ]) -}) + }) -test("keybinds accept OpenTUI binding specs", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "tui.json"), - JSON.stringify({ + const config = yield* getTuiConfig(test.directory) + expect(config.keybinds.get("terminal.suspend")).toEqual([]) + }), + ), + ), +) + +it.instance("keeps explicit configured keybind input undo on Windows", () => + withCleanState( + withPlatform( + "win32", + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeJson(path.join(test.directory, "tui.json"), { keybinds: { - command_list: [{ key: "alt+p", preventDefault: false }], - editor_open: { key: { name: "e", ctrl: true }, group: "Explicit" }, - "prompt.autocomplete.next": false, - plugin_manager: "ctrl+shift+p", + input_undo: "ctrl+y", }, + }) + + const config = yield* getTuiConfig(test.directory) + expect(config.keybinds.get("input.undo")?.[0]?.key).toBe("ctrl+y") + }), + ), + ), +) + +it.instance("KILO_TUI_CONFIG provides settings when no project config exists", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + const custom = path.join(test.directory, "custom-tui.json") + yield* fs.writeJson(custom, { theme: "from-env", diff_style: "stacked" }) + + yield* withEnv( + "KILO_TUI_CONFIG", + custom, + Effect.gen(function* () { + const config = yield* getTuiConfig(test.directory) + expect(config.theme).toBe("from-env") + expect(config.diff_style).toBe("stacked") }), ) - }, - }) - - const config = await getTuiConfig(tmp.path) - expect(config.keybinds.get("command.palette.show")).toEqual([ - { key: "alt+p", cmd: "command.palette.show", preventDefault: false, desc: "List available commands" }, - ]) - expect(config.keybinds.get("prompt.editor")?.[0]).toMatchObject({ - key: { name: "e", ctrl: true }, - cmd: "prompt.editor", - group: "Explicit", - }) - expect(config.keybinds.get("prompt.autocomplete.next")).toEqual([]) - expect(config.keybinds.get("plugins.list")?.[0]?.key).toBe("ctrl+shift+p") -}) - -wintest("defaults Ctrl+Z to input undo on Windows", async () => { - await using tmp = await tmpdir() - const config = await getTuiConfig(tmp.path) - expect(config.keybinds.get("terminal.suspend")).toEqual([]) - expect(config.keybinds.get("input.undo")?.[0]?.key).toBe("ctrl+z,ctrl+-,super+z") -}) - -wintest("keeps explicit input undo overrides on Windows", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "tui.json"), JSON.stringify({ keybinds: { input_undo: "ctrl+y" } })) - }, - }) - const config = await getTuiConfig(tmp.path) - expect(config.keybinds.get("terminal.suspend")).toEqual([]) - expect(config.keybinds.get("input.undo")?.[0]?.key).toBe("ctrl+y") -}) - -wintest("ignores terminal suspend bindings on Windows", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "tui.json"), JSON.stringify({ keybinds: { terminal_suspend: "alt+z" } })) - }, - }) - - const config = await getTuiConfig(tmp.path) - expect(config.keybinds.get("terminal.suspend")).toEqual([]) - expect(config.keybinds.get("input.undo")?.[0]?.key).toBe("ctrl+z,ctrl+-,super+z") -}) - -test("applies Windows keybind defaults", async () => { - await withPlatform("win32", async () => { - await using tmp = await tmpdir() - - const config = await getTuiConfig(tmp.path) - expect(config.keybinds.get("terminal.suspend")).toEqual([]) - expect(config.keybinds.get("input.undo")?.[0]?.key).toBe("ctrl+z,ctrl+-,super+z") - }) -}) - -test("ignores explicit keybind terminal suspend binding on Windows", async () => { - await withPlatform("win32", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "tui.json"), - JSON.stringify({ - keybinds: { - terminal_suspend: "alt+z", - }, - }), - ) - }, - }) - - const config = await getTuiConfig(tmp.path) - expect(config.keybinds.get("terminal.suspend")).toEqual([]) - }) -}) - -test("keeps explicit configured keybind input undo on Windows", async () => { - await withPlatform("win32", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "tui.json"), - JSON.stringify({ - keybinds: { - input_undo: "ctrl+y", - }, - }), - ) - }, - }) - - const config = await getTuiConfig(tmp.path) - expect(config.keybinds.get("input.undo")?.[0]?.key).toBe("ctrl+y") - }) -}) - -test("KILO_TUI_CONFIG provides settings when no project config exists", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - const custom = path.join(dir, "custom-tui.json") - await Bun.write(custom, JSON.stringify({ theme: "from-env", diff_style: "stacked" })) - process.env.KILO_TUI_CONFIG = custom - }, - }) - const config = await getTuiConfig(tmp.path) - expect(config.theme).toBe("from-env") - expect(config.diff_style).toBe("stacked") -}) - -test("does not derive tui path from KILO_CONFIG", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - const customDir = path.join(dir, "custom") - await fs.mkdir(customDir, { recursive: true }) - await Bun.write(path.join(customDir, "kilo.json"), JSON.stringify({ model: "test/model" })) - await Bun.write(path.join(customDir, "tui.json"), JSON.stringify({ theme: "should-not-load" })) - process.env.KILO_CONFIG = path.join(customDir, "kilo.json") // kilocode_change - }, - }) - const config = await getTuiConfig(tmp.path) - expect(config.theme).toBeUndefined() -}) - -test("applies env and file substitutions in tui.json", async () => { - const original = process.env.TUI_THEME_TEST - process.env.TUI_THEME_TEST = "env-theme" - try { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "keybind.txt"), "ctrl+q") - await Bun.write( - path.join(dir, "tui.json"), - JSON.stringify({ - theme: "{env:TUI_THEME_TEST}", - keybinds: { app_exit: "{file:keybind.txt}" }, - }), - ) - }, - }) - const config = await getTuiConfig(tmp.path) - expect(config.theme).toBe("env-theme") - expect(config.keybinds.get("app.exit")?.[0]?.key).toBe("ctrl+q") - } finally { - if (original === undefined) delete process.env.TUI_THEME_TEST - else process.env.TUI_THEME_TEST = original - } -}) - -test("applies file substitutions when first identical token is in a commented line", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "theme.txt"), "resolved-theme") - await Bun.write( - path.join(dir, "tui.jsonc"), + }), + ), +) + +it.instance("does not derive tui path from KILO_CONFIG", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + const customDir = path.join(test.directory, "custom") + yield* fs.makeDirectory(customDir, { recursive: true }) + yield* fs.writeJson(path.join(customDir, "kilo.json"), { model: "test/model" }) + yield* fs.writeJson(path.join(customDir, "tui.json"), { theme: "should-not-load" }) + + yield* withEnv( + "KILO_CONFIG", + path.join(customDir, "kilo.json"), + Effect.gen(function* () { + const config = yield* getTuiConfig(test.directory) + expect(config.theme).toBeUndefined() + }), + ) + }), + ), +) + +it.instance("applies env and file substitutions in tui.json", () => + withCleanState( + withEnv( + "TUI_THEME_TEST", + "env-theme", + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeFileString(path.join(test.directory, "keybind.txt"), "ctrl+q") + yield* fs.writeJson(path.join(test.directory, "tui.json"), { + theme: "{env:TUI_THEME_TEST}", + keybinds: { app_exit: "{file:keybind.txt}" }, + }) + + const config = yield* getTuiConfig(test.directory) + expect(config.theme).toBe("env-theme") + expect(config.keybinds.get("app.exit")?.[0]?.key).toBe("ctrl+q") + }), + ), + ), +) + +it.instance("applies file substitutions when first identical token is in a commented line", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeFileString(path.join(test.directory, "theme.txt"), "resolved-theme") + yield* fs.writeFileString( + path.join(test.directory, "tui.jsonc"), `{ // "theme": "{file:theme.txt}", "theme": "{file:theme.txt}" }`, ) - }, - }) - const config = await getTuiConfig(tmp.path) - expect(config.theme).toBe("resolved-theme") -}) - -// kilocode_change start -test("loads .kilo/tui.json", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await fs.mkdir(path.join(dir, ".kilo"), { recursive: true }) - await Bun.write(path.join(dir, ".kilo", "tui.json"), JSON.stringify({ diff_style: "stacked" }, null, 2)) - // kilocode_change end - }, - }) - const config = await getTuiConfig(tmp.path) - expect(config.diff_style).toBe("stacked") -}) - -test("supports tuple plugin specs with options in tui.json", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "tui.json"), - JSON.stringify({ - plugin: [["acme-plugin@1.2.3", { enabled: true, label: "demo" }]], - }), - ) - }, - }) - - const config = await getTuiConfig(tmp.path) - expect(config.plugin).toEqual([["acme-plugin@1.2.3", { enabled: true, label: "demo" }]]) - expect(config.plugin_origins).toEqual([ - { - spec: ["acme-plugin@1.2.3", { enabled: true, label: "demo" }], - scope: "local", - source: path.join(tmp.path, "tui.json"), - }, - ]) -}) - -test("deduplicates tuple plugin specs by name with higher precedence winning", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(Global.Path.config, "tui.json"), - JSON.stringify({ - plugin: [["acme-plugin@1.0.0", { source: "global" }]], - }), - ) - await Bun.write( - path.join(dir, "tui.json"), - JSON.stringify({ - plugin: [ - ["acme-plugin@2.0.0", { source: "project" }], - ["second-plugin@3.0.0", { source: "project" }], - ], - }), - ) - }, - }) - - const config = await getTuiConfig(tmp.path) - expect(config.plugin).toEqual([ - ["acme-plugin@2.0.0", { source: "project" }], - ["second-plugin@3.0.0", { source: "project" }], - ]) - expect(config.plugin_origins).toEqual([ - { - spec: ["acme-plugin@2.0.0", { source: "project" }], - scope: "local", - source: path.join(tmp.path, "tui.json"), - }, - { - spec: ["second-plugin@3.0.0", { source: "project" }], - scope: "local", - source: path.join(tmp.path, "tui.json"), - }, - ]) -}) - -test("tracks global and local plugin metadata in merged tui config", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(Global.Path.config, "tui.json"), - JSON.stringify({ - plugin: ["global-plugin@1.0.0"], - }), - ) - await Bun.write( - path.join(dir, "tui.json"), - JSON.stringify({ - plugin: ["local-plugin@2.0.0"], - }), - ) - }, - }) - const config = await getTuiConfig(tmp.path) - expect(config.plugin).toEqual(["global-plugin@1.0.0", "local-plugin@2.0.0"]) - expect(config.plugin_origins).toEqual([ - { - spec: "global-plugin@1.0.0", - scope: "global", - source: path.join(Global.Path.config, "tui.json"), - }, - { - spec: "local-plugin@2.0.0", - scope: "local", - source: path.join(tmp.path, "tui.json"), - }, - ]) -}) - -test("merges plugin_enabled flags across config layers", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(Global.Path.config, "tui.json"), - JSON.stringify({ - plugin_enabled: { - "internal:sidebar-context": false, - "demo.plugin": true, - }, - }), - ) - await Bun.write( - path.join(dir, "tui.json"), - JSON.stringify({ - plugin_enabled: { - "demo.plugin": false, - "local.plugin": true, - }, - }), + const config = yield* getTuiConfig(test.directory) + expect(config.theme).toBe("resolved-theme") + }), + ), +) + +it.instance("loads .kilo/tui.json", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeWithDirs( + path.join(test.directory, ".kilo", "tui.json"), + JSON.stringify({ diff_style: "stacked" }, null, 2), ) - }, - }) - - const config = await getTuiConfig(tmp.path) - expect(config.plugin_enabled).toEqual({ - "internal:sidebar-context": false, - "demo.plugin": false, - "local.plugin": true, - }) -}) - -test("silently skips malformed tui.json — load failures degrade to {}", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "tui.json"), '{ "theme": "broken",') - await Bun.write(path.join(dir, ".opencode", "tui.json"), JSON.stringify({ theme: "fallback" })) - }, - }) - - const config = await getTuiConfig(tmp.path) - // Project tui.json is malformed → silently skipped (logs a warning) - // .opencode/tui.json (lower precedence in this path) still loads - expect(config.theme).toBe("fallback") -}) -test("silently skips non-ENOENT read failures (e.g. tui.json is a directory) — fallback layer still loads", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - // tui.json exists as a DIRECTORY rather than a file → readFileString fails - // with EISDIR (PlatformError reason ≠ NotFound). The fix in this PR routes - // that through catchCause → log + skip, so a fallback layer should still load. - await fs.mkdir(path.join(dir, "tui.json"), { recursive: true }) - await Bun.write(path.join(dir, ".opencode", "tui.json"), JSON.stringify({ theme: "fallback" })) - }, - }) - - const config = await getTuiConfig(tmp.path) - // Did NOT crash; .opencode/tui.json (lower precedence) still loads. - expect(config.theme).toBe("fallback") -}) - -test("missing tui.json — silently treated as empty (ENOENT path)", async () => { - await using tmp = await tmpdir({}) - - // No tui.json anywhere. Should not throw. - const config = await getTuiConfig(tmp.path) - expect(config).toBeDefined() - // No theme set anywhere. - expect(config.theme).toBeUndefined() -}) + const config = yield* getTuiConfig(test.directory) + expect(config.diff_style).toBe("stacked") + }), + ), +) + +it.instance("supports tuple plugin specs with options in tui.json", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeJson(path.join(test.directory, "tui.json"), { + plugin: [["acme-plugin@1.2.3", { enabled: true, label: "demo" }]], + }) + + const config = yield* getTuiConfig(test.directory) + expect(config.plugin).toEqual([["acme-plugin@1.2.3", { enabled: true, label: "demo" }]]) + expect(config.plugin_origins).toEqual([ + { + spec: ["acme-plugin@1.2.3", { enabled: true, label: "demo" }], + scope: "local", + source: path.join(test.directory, "tui.json"), + }, + ]) + }), + ), +) + +it.instance("deduplicates tuple plugin specs by name with higher precedence winning", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeJson(path.join(Global.Path.config, "tui.json"), { + plugin: [["acme-plugin@1.0.0", { source: "global" }]], + }) + yield* fs.writeJson(path.join(test.directory, "tui.json"), { + plugin: [ + ["acme-plugin@2.0.0", { source: "project" }], + ["second-plugin@3.0.0", { source: "project" }], + ], + }) + + const config = yield* getTuiConfig(test.directory) + expect(config.plugin).toEqual([ + ["acme-plugin@2.0.0", { source: "project" }], + ["second-plugin@3.0.0", { source: "project" }], + ]) + expect(config.plugin_origins).toEqual([ + { + spec: ["acme-plugin@2.0.0", { source: "project" }], + scope: "local", + source: path.join(test.directory, "tui.json"), + }, + { + spec: ["second-plugin@3.0.0", { source: "project" }], + scope: "local", + source: path.join(test.directory, "tui.json"), + }, + ]) + }), + ), +) + +it.instance("tracks global and local plugin metadata in merged tui config", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeJson(path.join(Global.Path.config, "tui.json"), { plugin: ["global-plugin@1.0.0"] }) + yield* fs.writeJson(path.join(test.directory, "tui.json"), { plugin: ["local-plugin@2.0.0"] }) + + const config = yield* getTuiConfig(test.directory) + expect(config.plugin).toEqual(["global-plugin@1.0.0", "local-plugin@2.0.0"]) + expect(config.plugin_origins).toEqual([ + { + spec: "global-plugin@1.0.0", + scope: "global", + source: path.join(Global.Path.config, "tui.json"), + }, + { + spec: "local-plugin@2.0.0", + scope: "local", + source: path.join(test.directory, "tui.json"), + }, + ]) + }), + ), +) + +it.instance("merges plugin_enabled flags across config layers", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeJson(path.join(Global.Path.config, "tui.json"), { + plugin_enabled: { + "internal:sidebar-context": false, + "demo.plugin": true, + }, + }) + yield* fs.writeJson(path.join(test.directory, "tui.json"), { + plugin_enabled: { + "demo.plugin": false, + "local.plugin": true, + }, + }) + + const config = yield* getTuiConfig(test.directory) + expect(config.plugin_enabled).toEqual({ + "internal:sidebar-context": false, + "demo.plugin": false, + "local.plugin": true, + }) + }), + ), +) + +it.instance("silently skips malformed tui.json - load failures degrade to {}", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.writeFileString(path.join(test.directory, "tui.json"), '{ "theme": "broken",') + yield* fs.writeWithDirs(path.join(test.directory, ".opencode", "tui.json"), JSON.stringify({ theme: "fallback" })) + + const config = yield* getTuiConfig(test.directory) + expect(config.theme).toBe("fallback") + }), + ), +) + +it.instance("silently skips non-ENOENT read failures (e.g. tui.json is a directory) - fallback layer still loads", () => + withCleanState( + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fs.makeDirectory(path.join(test.directory, "tui.json"), { recursive: true }) + yield* fs.writeWithDirs(path.join(test.directory, ".opencode", "tui.json"), JSON.stringify({ theme: "fallback" })) + + const config = yield* getTuiConfig(test.directory) + expect(config.theme).toBe("fallback") + }), + ), +) + +it.instance("missing tui.json - silently treated as empty (ENOENT path)", () => + withCleanState( + Effect.gen(function* () { + const test = yield* TestInstance + const config = yield* getTuiConfig(test.directory) + expect(config).toBeDefined() + expect(config.theme).toBeUndefined() + }), + ), +) diff --git a/packages/opencode/test/control-plane/workspace.test.ts b/packages/opencode/test/control-plane/workspace.test.ts index 5e3178a5c4d..8b6f3ce8bc4 100644 --- a/packages/opencode/test/control-plane/workspace.test.ts +++ b/packages/opencode/test/control-plane/workspace.test.ts @@ -6,23 +6,24 @@ import path from "node:path" import { setTimeout as delay } from "node:timers/promises" import { NodeHttpServer } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpServer, HttpServerRequest, HttpServerResponse } from "effect/unstable/http" +import { FetchHttpClient, HttpServer, HttpServerRequest, HttpServerResponse } from "effect/unstable/http" import { eq } from "drizzle-orm" +import { AppFileSystem } from "@opencode-ai/core/filesystem" import * as Log from "@opencode-ai/core/util/log" -import { Flag } from "@opencode-ai/core/flag/flag" import { GlobalBus, type GlobalEvent } from "@/bus/global" import { Database } from "@/storage/db" import { ProjectID } from "@/project/schema" import { ProjectTable } from "@/project/project.sql" import { Instance } from "@/project/instance" import { WithInstance } from "../../src/project/with-instance" +import { InstanceRef } from "@/effect/instance-ref" import { Session as SessionNs } from "@/session/session" import { SessionID } from "@/session/schema" import { SessionTable } from "@/session/session.sql" import { SyncEvent } from "@/sync" import { EventSequenceTable } from "@/sync/event.sql" import { resetDatabase } from "../fixture/db" -import { disposeAllInstances, provideTmpdirInstance, tmpdir } from "../fixture/fixture" +import { disposeAllInstances, provideTmpdirInstance, TestInstance, tmpdir } from "../fixture/fixture" import { testEffect } from "../lib/effect" import { registerAdapter } from "../../src/control-plane/adapters" import { WorkspaceID } from "../../src/control-plane/schema" @@ -32,24 +33,43 @@ import * as Workspace from "../../src/control-plane/workspace" import { AppRuntime } from "@/effect/app-runtime" import { InstanceStore } from "@/project/instance-store" import { InstanceBootstrap } from "@/project/bootstrap" +import { Auth } from "@/auth" +import { SessionPrompt } from "@/session/prompt" +import { Project } from "@/project/project" +import { Vcs } from "@/project/vcs" +import { RuntimeFlags } from "@/effect/runtime-flags" void Log.init({ print: false }) -const testServerLayer = Layer.mergeAll( - NodeHttpServer.layer(Http.createServer, { host: "127.0.0.1", port: 0 }), - Workspace.defaultLayer.pipe(Layer.provide(InstanceStore.defaultLayer), Layer.provide(InstanceBootstrap.defaultLayer)), - SessionNs.defaultLayer, -) -const it = testEffect(testServerLayer) - -const originalWorkspacesFlag = Flag.KILO_EXPERIMENTAL_WORKSPACES const originalEnv = { KILO_AUTH_CONTENT: process.env.KILO_AUTH_CONTENT, + KILO_EXPERIMENTAL_WORKSPACES: process.env.KILO_EXPERIMENTAL_WORKSPACES, OTEL_EXPORTER_OTLP_HEADERS: process.env.OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_ENDPOINT: process.env.OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_RESOURCE_ATTRIBUTES: process.env.OTEL_RESOURCE_ATTRIBUTES, } +const workspaceLayer = (experimentalWorkspaces: boolean) => + Workspace.layer.pipe( + Layer.provide(Auth.defaultLayer), + Layer.provide(SessionNs.defaultLayer), + Layer.provide(SyncEvent.defaultLayer), + Layer.provide(SessionPrompt.defaultLayer), + Layer.provide(Project.defaultLayer), + Layer.provide(Vcs.defaultLayer), + Layer.provide(FetchHttpClient.layer), + Layer.provide(AppFileSystem.defaultLayer), + Layer.provide(RuntimeFlags.layer({ experimentalWorkspaces })), + Layer.provide(InstanceStore.defaultLayer.pipe(Layer.provide(InstanceBootstrap.defaultLayer))), + ) + +const testServerLayer = Layer.mergeAll( + NodeHttpServer.layer(Http.createServer, { host: "127.0.0.1", port: 0 }), + workspaceLayer(true), + SessionNs.defaultLayer, +) +const it = testEffect(testServerLayer) + type RecordedCreate = { info: WorkspaceInfo env: Record @@ -91,21 +111,20 @@ function restoreEnv() { beforeEach(() => { Database.close() - Flag.KILO_EXPERIMENTAL_WORKSPACES = true restoreEnv() + process.env.KILO_EXPERIMENTAL_WORKSPACES = "true" }) afterEach(async () => { mock.restore() await disposeAllInstances() - Flag.KILO_EXPERIMENTAL_WORKSPACES = originalWorkspacesFlag restoreEnv() await resetDatabase() }) async function withInstance(fn: (dir: string) => T | Promise) { await using tmp = await tmpdir({ git: true }) - return WithInstance.provide({ + return await WithInstance.provide({ directory: tmp.path, fn: () => fn(tmp.path), }) @@ -114,7 +133,6 @@ async function withInstance(fn: (dir: string) => T | Promise) { async function initGitRepo(dir: string) { await fs.mkdir(dir, { recursive: true }) await $`git init`.cwd(dir).quiet() - await $`git config core.autocrlf false`.cwd(dir).quiet() // kilocode_change - align test repos with Git service patch behavior await $`git config core.fsmonitor false`.cwd(dir).quiet() await $`git config commit.gpgsign false`.cwd(dir).quiet() await $`git config user.email "test@opencode.test"`.cwd(dir).quiet() @@ -136,12 +154,17 @@ const syncListWorkspaces = (project: Parameters const getWorkspace = (id: WorkspaceID) => runWorkspace(Workspace.Service.use((workspace) => workspace.get(id))) const removeWorkspace = (id: WorkspaceID) => runWorkspace(Workspace.Service.use((workspace) => workspace.remove(id))) const workspaceStatus = () => runWorkspace(Workspace.Service.use((workspace) => workspace.status())) -const winSkip = process.platform === "win32" ? test.skip : test // kilocode_change - git patch application is covered on Linux CI const isWorkspaceSyncing = (id: WorkspaceID) => runWorkspace(Workspace.Service.use((workspace) => workspace.isSyncing(id))) const startWorkspaceSyncing = (projectID: ProjectID) => { void runWorkspace(Workspace.Service.use((workspace) => workspace.startWorkspaceSyncing(projectID))) } +const startWorkspaceSyncingWithFlag = (projectID: ProjectID, experimentalWorkspaces: boolean) => + Effect.runPromise( + Workspace.Service.use((workspace) => workspace.startWorkspaceSyncing(projectID)).pipe( + Effect.provide(workspaceLayer(experimentalWorkspaces)), + ), + ) const waitForWorkspaceSync = (workspaceID: WorkspaceID, state: Record, signal?: AbortSignal) => runWorkspace(Workspace.Service.use((workspace) => workspace.waitForSync(workspaceID, state, signal))) @@ -724,30 +747,39 @@ describe("workspace CRUD", () => { }) }) - test("remove deletes the workspace, associated sessions, adapter resources, and status", async () => { - await withInstance(async (dir) => { - const type = unique("remove-local") - const recorded = localAdapter(path.join(dir, "remove-local")) - registerAdapter(Instance.project.id, type, recorded.adapter) - const info = await createWorkspace({ type, branch: null, projectID: Instance.project.id, extra: null }) - const one = await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({}))) - const two = await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({}))) - attachSessionToWorkspace(one.id, info.id) - attachSessionToWorkspace(two.id, info.id) - - const removed = await removeWorkspace(info.id) - - expect(removed).toEqual(info) - expect(await getWorkspace(info.id)).toBeUndefined() - expect(recorded.calls.remove).toEqual([info]) - expect((await workspaceStatus()).find((item) => item.workspaceID === info.id)?.status).toBeUndefined() - expect( - Database.use((db) => - db.select({ id: SessionTable.id }).from(SessionTable).where(eq(SessionTable.workspace_id, info.id)).all(), - ), - ).toEqual([]) - }) - }) + it.instance( + "remove deletes the workspace, associated sessions, adapter resources, and status", + () => { + return Effect.gen(function* () { + const { directory: dir } = yield* TestInstance + const instance = yield* InstanceRef + if (!instance) return yield* Effect.die(new Error("missing test instance")) + const workspace = yield* Workspace.Service + const sessionSvc = yield* SessionNs.Service + const type = unique("remove-local") + const recorded = localAdapter(path.join(dir, "remove-local")) + registerAdapter(instance.project.id, type, recorded.adapter) + const info = yield* workspace.create({ type, branch: null, projectID: instance.project.id, extra: null }) + const one = yield* sessionSvc.create({}) + const two = yield* sessionSvc.create({}) + attachSessionToWorkspace(one.id, info.id) + attachSessionToWorkspace(two.id, info.id) + + const removed = yield* workspace.remove(info.id) + + expect(removed).toEqual(info) + expect(yield* workspace.get(info.id)).toBeUndefined() + expect(recorded.calls.remove).toEqual([info]) + expect((yield* workspace.status()).find((item) => item.workspaceID === info.id)?.status).toBeUndefined() + expect( + Database.use((db) => + db.select({ id: SessionTable.id }).from(SessionTable).where(eq(SessionTable.workspace_id, info.id)).all(), + ), + ).toEqual([]) + }) + }, + { git: true }, + ) test("remove still deletes the row when the adapter cannot remove resources", async () => { await withInstance(async () => { @@ -772,86 +804,111 @@ describe("workspace CRUD", () => { }) }) - test("sessionWarp moves a session into a local workspace and claims ownership", async () => { - await withInstance(async (dir) => { - const previousType = unique("warp-prev-local") - const targetType = unique("warp-target-local") - const previous = workspaceInfo(Instance.project.id, previousType) - const target = workspaceInfo(Instance.project.id, targetType) - insertWorkspace(previous) - insertWorkspace(target) - registerAdapter(Instance.project.id, previousType, localAdapter(path.join(dir, "warp-prev-local")).adapter) - registerAdapter(Instance.project.id, targetType, localAdapter(path.join(dir, "warp-target-local")).adapter) - const session = await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({}))) - attachSessionToWorkspace(session.id, previous.id) - - await warpWorkspaceSession({ workspaceID: target.id, sessionID: session.id }) - - expect( - Database.use((db) => - db - .select({ workspaceID: SessionTable.workspace_id }) - .from(SessionTable) - .where(eq(SessionTable.id, session.id)) - .get(), - )?.workspaceID, - ).toBe(target.id) - expect(sessionSequenceOwner(session.id)).toBe(target.id) - }) - }) + it.instance( + "sessionWarp moves a session into a local workspace and claims ownership", + () => { + return Effect.gen(function* () { + const { directory: dir } = yield* TestInstance + const instance = yield* InstanceRef + if (!instance) return yield* Effect.die(new Error("missing test instance")) + const workspace = yield* Workspace.Service + const sessionSvc = yield* SessionNs.Service + const previousType = unique("warp-prev-local") + const targetType = unique("warp-target-local") + const previous = workspaceInfo(instance.project.id, previousType) + const target = workspaceInfo(instance.project.id, targetType) + insertWorkspace(previous) + insertWorkspace(target) + registerAdapter(instance.project.id, previousType, localAdapter(path.join(dir, "warp-prev-local")).adapter) + registerAdapter(instance.project.id, targetType, localAdapter(path.join(dir, "warp-target-local")).adapter) + const session = yield* sessionSvc.create({}) + attachSessionToWorkspace(session.id, previous.id) + + yield* workspace.sessionWarp({ workspaceID: target.id, sessionID: session.id }) - winSkip("sessionWarp applies source workspace patch to local target workspace", async () => { - // kilocode_change - await withInstance(async (dir) => { - const previousType = unique("warp-patch-prev-local") - const targetType = unique("warp-patch-target-local") - const previousDir = path.join(dir, "warp-patch-prev-local") - const targetDir = path.join(dir, "warp-patch-target-local") - await initGitRepo(previousDir) - await initGitRepo(targetDir) - await fs.writeFile(path.join(previousDir, "tracked.txt"), "changed\n") - await fs.writeFile(path.join(previousDir, "new.txt"), "new\n") - await $`git add new.txt`.cwd(previousDir).quiet() // kilocode_change - avoid unrelated untracked patch path - - const previous = workspaceInfo(Instance.project.id, previousType) - const target = workspaceInfo(Instance.project.id, targetType) - insertWorkspace(previous) - insertWorkspace(target) - registerAdapter(Instance.project.id, previousType, localAdapter(previousDir, { createDir: false }).adapter) - registerAdapter(Instance.project.id, targetType, localAdapter(targetDir, { createDir: false }).adapter) - const session = await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({}))) - attachSessionToWorkspace(session.id, previous.id) - - await warpWorkspaceSession({ workspaceID: target.id, sessionID: session.id, copyChanges: true }) - - expect(await fs.readFile(path.join(targetDir, "tracked.txt"), "utf8")).toBe("changed\n") - expect(await fs.readFile(path.join(targetDir, "new.txt"), "utf8")).toBe("new\n") - }) - }) + expect( + Database.use((db) => + db + .select({ workspaceID: SessionTable.workspace_id }) + .from(SessionTable) + .where(eq(SessionTable.id, session.id)) + .get(), + )?.workspaceID, + ).toBe(target.id) + expect(sessionSequenceOwner(session.id)).toBe(target.id) + }) + }, + { git: true }, + ) - test("sessionWarp detaches a session to the local project and claims project ownership", async () => { - await withInstance(async (dir) => { - const previousType = unique("warp-detach-local") - const previous = workspaceInfo(Instance.project.id, previousType) - insertWorkspace(previous) - registerAdapter(Instance.project.id, previousType, localAdapter(path.join(dir, "warp-detach-local")).adapter) - const session = await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({}))) - attachSessionToWorkspace(session.id, previous.id) + it.instance( + "sessionWarp applies source workspace patch to local target workspace", + () => { + return Effect.gen(function* () { + const { directory: dir } = yield* TestInstance + const instance = yield* InstanceRef + if (!instance) return yield* Effect.die(new Error("missing test instance")) + const workspace = yield* Workspace.Service + const sessionSvc = yield* SessionNs.Service + const previousType = unique("warp-patch-prev-local") + const targetType = unique("warp-patch-target-local") + const previousDir = path.join(dir, "warp-patch-prev-local") + const targetDir = path.join(dir, "warp-patch-target-local") + yield* Effect.promise(() => initGitRepo(previousDir)) + yield* Effect.promise(() => initGitRepo(targetDir)) + yield* Effect.promise(() => fs.writeFile(path.join(previousDir, "tracked.txt"), "changed\n")) + yield* Effect.promise(() => fs.writeFile(path.join(previousDir, "new.txt"), "new\n")) + + const previous = workspaceInfo(instance.project.id, previousType) + const target = workspaceInfo(instance.project.id, targetType) + insertWorkspace(previous) + insertWorkspace(target) + registerAdapter(instance.project.id, previousType, localAdapter(previousDir, { createDir: false }).adapter) + registerAdapter(instance.project.id, targetType, localAdapter(targetDir, { createDir: false }).adapter) + const session = yield* sessionSvc.create({}) + attachSessionToWorkspace(session.id, previous.id) + + yield* workspace.sessionWarp({ workspaceID: target.id, sessionID: session.id, copyChanges: true }) + + expect(yield* Effect.promise(() => fs.readFile(path.join(targetDir, "tracked.txt"), "utf8"))).toBe("changed\n") + expect(yield* Effect.promise(() => fs.readFile(path.join(targetDir, "new.txt"), "utf8"))).toBe("new\n") + }) + }, + { git: true }, + ) - await warpWorkspaceSession({ workspaceID: null, sessionID: session.id }) + it.instance( + "sessionWarp detaches a session to the local project and claims project ownership", + () => { + return Effect.gen(function* () { + const { directory: dir } = yield* TestInstance + const instance = yield* InstanceRef + if (!instance) return yield* Effect.die(new Error("missing test instance")) + const workspace = yield* Workspace.Service + const sessionSvc = yield* SessionNs.Service + const previousType = unique("warp-detach-local") + const previous = workspaceInfo(instance.project.id, previousType) + insertWorkspace(previous) + registerAdapter(instance.project.id, previousType, localAdapter(path.join(dir, "warp-detach-local")).adapter) + const session = yield* sessionSvc.create({}) + attachSessionToWorkspace(session.id, previous.id) + + yield* workspace.sessionWarp({ workspaceID: null, sessionID: session.id }) - expect( - Database.use((db) => - db - .select({ workspaceID: SessionTable.workspace_id }) - .from(SessionTable) - .where(eq(SessionTable.id, session.id)) - .get(), - )?.workspaceID, - ).toBeNull() - expect(sessionSequenceOwner(session.id)).toBe(Instance.project.id) - }) - }) + expect( + Database.use((db) => + db + .select({ workspaceID: SessionTable.workspace_id }) + .from(SessionTable) + .where(eq(SessionTable.id, session.id)) + .get(), + )?.workspaceID, + ).toBeNull() + expect(sessionSequenceOwner(session.id)).toBe(instance.project.id) + }) + }, + { git: true }, + ) test("sessionWarp detaches to the source project when invoked from a workspace instance", async () => { await withInstance(async () => { @@ -981,110 +1038,138 @@ describe("workspace CRUD", () => { }) describe("workspace sync state", () => { - test("startWorkspaceSyncing is disabled by the experimental workspace flag", async () => { - await withInstance(async (dir) => { - Flag.KILO_EXPERIMENTAL_WORKSPACES = false - const type = unique("flag-disabled") - const info = workspaceInfo(Instance.project.id, type) - const session = await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({}))) - attachSessionToWorkspace(session.id, info.id) - insertWorkspace(info) - registerAdapter(Instance.project.id, type, localAdapter(path.join(dir, "flag-disabled")).adapter) + it.instance( + "startWorkspaceSyncing is disabled by the experimental workspace flag", + () => + Effect.gen(function* () { + const { directory: dir } = yield* TestInstance + const instance = yield* InstanceRef + if (!instance) return yield* Effect.die(new Error("missing test instance")) + const workspace = yield* Workspace.Service + const sessionSvc = yield* SessionNs.Service + const type = unique("flag-disabled") + const info = workspaceInfo(instance.project.id, type) + const session = yield* sessionSvc.create({}) + attachSessionToWorkspace(session.id, info.id) + insertWorkspace(info) + registerAdapter(instance.project.id, type, localAdapter(path.join(dir, "flag-disabled")).adapter) - startWorkspaceSyncing(Instance.project.id) - await delay(25) + yield* Effect.promise(() => startWorkspaceSyncingWithFlag(instance.project.id, false)) + yield* Effect.sleep("25 millis") - expect((await workspaceStatus()).find((item) => item.workspaceID === info.id)?.status).toBeUndefined() - }) - }) + expect((yield* workspace.status()).find((item) => item.workspaceID === info.id)?.status).toBeUndefined() + }), + { git: true }, + ) - test("startWorkspaceSyncing starts all workspaces", async () => { - await withInstance(async (dir) => { - const firstType = unique("first") - const secondType = unique("second") - const first = workspaceInfo(Instance.project.id, firstType) - const second = workspaceInfo(Instance.project.id, secondType) - await fs.mkdir(path.join(dir, "first"), { recursive: true }) - await fs.mkdir(path.join(dir, "second"), { recursive: true }) - insertWorkspace(first) - insertWorkspace(second) - registerAdapter(Instance.project.id, firstType, localAdapter(path.join(dir, "first")).adapter) - registerAdapter(Instance.project.id, secondType, localAdapter(path.join(dir, "second")).adapter) - - startWorkspaceSyncing(Instance.project.id) - - await eventually(() => - workspaceStatus().then((status) => { - expect(status.find((item) => item.workspaceID === first.id)?.status).toBe("connected") - expect(status.find((item) => item.workspaceID === second.id)?.status).toBe("connected") - }), - ) - await removeWorkspace(first.id) - await removeWorkspace(second.id) - }) - }) + it.instance( + "startWorkspaceSyncing starts all workspaces", + () => + Effect.gen(function* () { + const { directory: dir } = yield* TestInstance + const instance = yield* InstanceRef + if (!instance) return yield* Effect.die(new Error("missing test instance")) + const workspace = yield* Workspace.Service + const projectID = instance.project.id + const firstType = unique("first") + const secondType = unique("second") + const first = workspaceInfo(projectID, firstType) + const second = workspaceInfo(projectID, secondType) + yield* Effect.promise(() => fs.mkdir(path.join(dir, "first"), { recursive: true })) + yield* Effect.promise(() => fs.mkdir(path.join(dir, "second"), { recursive: true })) + yield* Effect.sync(() => { + insertWorkspace(first) + insertWorkspace(second) + registerAdapter(projectID, firstType, localAdapter(path.join(dir, "first")).adapter) + registerAdapter(projectID, secondType, localAdapter(path.join(dir, "second")).adapter) + }) + yield* Effect.addFinalizer(() => + Effect.all([workspace.remove(first.id), workspace.remove(second.id)], { discard: true }).pipe(Effect.ignore), + ) - test("local start reports error when the target directory is missing", async () => { - await withInstance(async (dir) => { - const type = unique("missing-local") - const info = workspaceInfo(Instance.project.id, type) - insertWorkspace(info) - registerAdapter( - Instance.project.id, - type, - localAdapter(path.join(dir, "missing-target"), { createDir: false }).adapter, - ) - attachSessionToWorkspace( - (await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({})))).id, - info.id, - ) + yield* workspace.startWorkspaceSyncing(projectID) - startWorkspaceSyncing(Instance.project.id) + yield* eventuallyEffect( + Effect.gen(function* () { + const status = yield* workspace.status() + expect(status.find((item) => item.workspaceID === first.id)?.status).toBe("connected") + expect(status.find((item) => item.workspaceID === second.id)?.status).toBe("connected") + }), + ) + }), + { git: true }, + ) - await eventually(() => - workspaceStatus().then((status) => - expect(status.find((item) => item.workspaceID === info.id)?.status).toBe("error"), - ), - ) - expect(await isWorkspaceSyncing(info.id)).toBe(false) - await removeWorkspace(info.id) - }) - }) + it.instance( + "local start reports error when the target directory is missing", + () => + Effect.gen(function* () { + const { directory: dir } = yield* TestInstance + const instance = yield* InstanceRef + if (!instance) return yield* Effect.die(new Error("missing test instance")) + const workspace = yield* Workspace.Service + const sessionSvc = yield* SessionNs.Service + const type = unique("missing-local") + const info = workspaceInfo(instance.project.id, type) + insertWorkspace(info) + registerAdapter( + instance.project.id, + type, + localAdapter(path.join(dir, "missing-target"), { createDir: false }).adapter, + ) + attachSessionToWorkspace((yield* sessionSvc.create({})).id, info.id) - test("duplicate local status updates are suppressed", async () => { - await withInstance(async (dir) => { - const captured = captureGlobalEvents() - try { + yield* workspace.startWorkspaceSyncing(instance.project.id) + + yield* eventuallyEffect( + Effect.gen(function* () { + const status = yield* workspace.status() + expect(status.find((item) => item.workspaceID === info.id)?.status).toBe("error") + }), + ) + expect(yield* workspace.isSyncing(info.id)).toBe(false) + yield* workspace.remove(info.id) + }), + { git: true }, + ) + + it.instance( + "duplicate local status updates are suppressed", + () => + Effect.gen(function* () { + const { directory: dir } = yield* TestInstance + const instance = yield* InstanceRef + if (!instance) return yield* Effect.die(new Error("missing test instance")) + const workspace = yield* Workspace.Service + const sessionSvc = yield* SessionNs.Service + const captured = captureGlobalEvents() + yield* Effect.addFinalizer(() => Effect.sync(() => captured.dispose())) const type = unique("dedupe-local") - const info = workspaceInfo(Instance.project.id, type) + const info = workspaceInfo(instance.project.id, type) const target = path.join(dir, "dedupe-local") - await fs.mkdir(target, { recursive: true }) + yield* Effect.promise(() => fs.mkdir(target, { recursive: true })) insertWorkspace(info) - registerAdapter(Instance.project.id, type, localAdapter(target).adapter) - attachSessionToWorkspace( - (await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({})))).id, - info.id, - ) + registerAdapter(instance.project.id, type, localAdapter(target).adapter) + attachSessionToWorkspace((yield* sessionSvc.create({})).id, info.id) - startWorkspaceSyncing(Instance.project.id) - startWorkspaceSyncing(Instance.project.id) + yield* workspace.startWorkspaceSyncing(instance.project.id) + yield* workspace.startWorkspaceSyncing(instance.project.id) - await eventually(() => - workspaceStatus().then((status) => - expect(status.find((item) => item.workspaceID === info.id)?.status).toBe("connected"), - ), + yield* eventuallyEffect( + Effect.gen(function* () { + const status = yield* workspace.status() + expect(status.find((item) => item.workspaceID === info.id)?.status).toBe("connected") + }), ) expect( captured.events.filter( (event) => event.workspace === info.id && event.payload.type === Workspace.Event.Status.type, ), ).toHaveLength(1) - await removeWorkspace(info.id) - } finally { - captured.dispose() - } - }) - }) + yield* workspace.remove(info.id) + }), + { git: true }, + ) it.live("remote start emits disconnected, connecting, and connected then refuses duplicate listeners", () => { const calls: FetchCall[] = [] diff --git a/packages/opencode/test/effect/app-runtime-logger.test.ts b/packages/opencode/test/effect/app-runtime-logger.test.ts index fe9516ef99f..e4ce6511dc5 100644 --- a/packages/opencode/test/effect/app-runtime-logger.test.ts +++ b/packages/opencode/test/effect/app-runtime-logger.test.ts @@ -1,12 +1,13 @@ import { expect } from "bun:test" -import { Context, Effect, Layer, Logger } from "effect" +import { Context, Deferred, Effect, Fiber, Layer, Logger } from "effect" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" -import { AppRuntime } from "../../src/effect/app-runtime" +import { AppLayer } from "../../src/effect/app-runtime" import { EffectBridge } from "@/effect/bridge" import { InstanceRef } from "../../src/effect/instance-ref" import * as EffectLogger from "@opencode-ai/core/effect/logger" -import { makeRuntime } from "../../src/effect/run-service" -import { provideInstance, tmpdirScoped } from "../fixture/fixture" +import * as Observability from "@opencode-ai/core/effect/observability" +import { attach } from "../../src/effect/run-service" +import { TestInstance } from "../fixture/fixture" import { testEffect } from "../lib/effect" const it = testEffect(CrossSpawnSpawner.defaultLayer) @@ -35,64 +36,70 @@ it.live("makeRuntime installs EffectLogger through Observability.layer", () => }), ) - const current = yield* Effect.promise(() => makeRuntime(Dummy, layer).runPromise((svc) => svc.current())) + const current = yield* Dummy.use((svc) => svc.current()).pipe( + Effect.provide(Layer.provideMerge(layer, Observability.layer)), + ) expect(current.effectLogger).toBe(true) expect(current.defaultLogger).toBe(false) }), ) -it.live("AppRuntime also installs EffectLogger through Observability.layer", () => +it.live("AppLayer also installs EffectLogger through Observability.layer", () => Effect.gen(function* () { - const current = yield* Effect.promise(() => - AppRuntime.runPromise(Effect.map(Effect.service(Logger.CurrentLoggers), check)), - ) + const current = yield* Effect.map(Effect.service(Logger.CurrentLoggers), check).pipe(Effect.provide(AppLayer)) expect(current.effectLogger).toBe(true) expect(current.defaultLogger).toBe(false) }), ) -it.live("AppRuntime attaches InstanceRef from ALS", () => - Effect.gen(function* () { - const dir = yield* tmpdirScoped({ git: true }) - const current = yield* Effect.promise(() => - AppRuntime.runPromise( +it.instance( + "attach preserves InstanceRef from the current fiber context", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const current = yield* attach( Effect.gen(function* () { return (yield* InstanceRef)?.directory }), - ), - ).pipe(provideInstance(dir)) + ) - expect(current).toBe(dir) - }), + expect(current).toBe(test.directory) + }), + { git: true }, ) -it.live("EffectBridge preserves logger and instance context across async boundaries", () => - Effect.gen(function* () { - const dir = yield* tmpdirScoped({ git: true }) - const result = yield* Effect.promise(() => - AppRuntime.runPromise( - Effect.gen(function* () { - const bridge = yield* EffectBridge.make() - return yield* Effect.promise(() => - Promise.resolve().then(() => - bridge.promise( - Effect.gen(function* () { - return { - directory: (yield* InstanceRef)?.directory, - ...check(yield* Effect.service(Logger.CurrentLoggers)), - } - }), - ), +it.instance( + "EffectBridge preserves logger and instance context across async boundaries", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const bridge = yield* EffectBridge.make() + const started = yield* Deferred.make() + + const fiber = yield* Effect.gen(function* () { + yield* Deferred.succeed(started, undefined) + return yield* Effect.promise(() => + Promise.resolve().then(() => + bridge.promise( + Effect.gen(function* () { + return { + directory: (yield* InstanceRef)?.directory, + ...check(yield* Effect.service(Logger.CurrentLoggers)), + } + }), ), - ) - }), - ), - ).pipe(provideInstance(dir)) + ), + ) + }).pipe(Effect.forkScoped) - expect(result.directory).toBe(dir) - expect(result.effectLogger).toBe(true) - expect(result.defaultLogger).toBe(false) - }), + yield* Deferred.await(started) + const result = yield* Fiber.join(fiber) + + expect(result.directory).toBe(test.directory) + expect(result.effectLogger).toBe(true) + expect(result.defaultLogger).toBe(false) + }).pipe(Effect.provide(Observability.layer)), + { git: true }, ) diff --git a/packages/opencode/test/effect/instance-state.test.ts b/packages/opencode/test/effect/instance-state.test.ts index f5e69338832..b928f16bc6a 100644 --- a/packages/opencode/test/effect/instance-state.test.ts +++ b/packages/opencode/test/effect/instance-state.test.ts @@ -159,7 +159,7 @@ it.live("InstanceState preserves directory across async boundaries", () => return Test.of({ get: Effect.fn("Test.get")(function* () { - yield* Effect.promise(() => Bun.sleep(1)) + yield* Effect.sleep(Duration.millis(1)) yield* Effect.sleep(Duration.millis(1)) for (let i = 0; i < 100; i++) { yield* Effect.yieldNow @@ -168,7 +168,7 @@ it.live("InstanceState preserves directory across async boundaries", () => yield* Effect.promise(() => Promise.resolve()) } yield* Effect.sleep(Duration.millis(2)) - yield* Effect.promise(() => Bun.sleep(1)) + yield* Effect.sleep(Duration.millis(1)) return yield* InstanceState.get(state) }), }) @@ -212,7 +212,7 @@ it.live("InstanceState survives high-contention concurrent access", () => return Test.of({ get: Effect.fn("Test.get")(function* () { for (let i = 0; i < 10; i++) { - yield* Effect.promise(() => Bun.sleep(Math.random() * 3)) + yield* Effect.sleep(Duration.millis(Math.random() * 3)) yield* Effect.yieldNow yield* Effect.promise(() => Promise.resolve()) } @@ -248,8 +248,8 @@ it.live("InstanceState correct after interleaved init and dispose", () => Test, Effect.gen(function* () { const state = yield* InstanceState.make((ctx) => - Effect.promise(async () => { - await Bun.sleep(5) + Effect.gen(function* () { + yield* Effect.sleep(Duration.millis(5)) return ctx.directory }), ) @@ -305,9 +305,9 @@ it.live("InstanceState dedupes concurrent lookups", () => const dir = yield* tmpdirScoped() let n = 0 const state = yield* InstanceState.make(() => - Effect.promise(async () => { + Effect.gen(function* () { n += 1 - await Bun.sleep(10) + yield* Effect.sleep(Duration.millis(10)) return { n } }), ) diff --git a/packages/opencode/test/effect/runner.test.ts b/packages/opencode/test/effect/runner.test.ts index 0f5783bfc4a..27fe9e02542 100644 --- a/packages/opencode/test/effect/runner.test.ts +++ b/packages/opencode/test/effect/runner.test.ts @@ -1,8 +1,13 @@ import { describe, expect } from "bun:test" -import { Deferred, Effect, Exit, Fiber, Ref, Scope } from "effect" +import { Cause, Deferred, Effect, Exit, Fiber, Latch, Ref, Scope } from "effect" import { Runner } from "@/effect/runner" import { it } from "../lib/effect" +const waitForState = (runner: Runner.Runner, tag: Runner.State["_tag"]) => + Effect.gen(function* () { + while (runner.state._tag !== tag) yield* Effect.yieldNow + }).pipe(Effect.timeout("1 second")) + describe("Runner", () => { // --- ensureRunning semantics --- @@ -152,7 +157,7 @@ describe("Runner", () => { const s = yield* Scope.Scope const runner = Runner.make(s, { onInterrupt: Effect.succeed("fallback") }) const fiber = yield* runner.ensureRunning(Effect.never.pipe(Effect.as("never"))).pipe(Effect.forkChild) - yield* Effect.sleep("10 millis") + yield* waitForState(runner, "Running") yield* runner.cancel @@ -169,9 +174,9 @@ describe("Runner", () => { const runner = Runner.make(s, { onInterrupt: Effect.succeed("fallback") }) const a = yield* runner.ensureRunning(Effect.never.pipe(Effect.as("x"))).pipe(Effect.forkChild) - yield* Effect.sleep("10 millis") + yield* waitForState(runner, "Running") const b = yield* runner.ensureRunning(Effect.succeed("y")).pipe(Effect.forkChild) - yield* Effect.sleep("10 millis") + yield* Effect.yieldNow yield* runner.cancel @@ -189,7 +194,7 @@ describe("Runner", () => { const s = yield* Scope.Scope const runner = Runner.make(s) const fiber = yield* runner.ensureRunning(Effect.never.pipe(Effect.as("x"))).pipe(Effect.forkChild) - yield* Effect.sleep("10 millis") + yield* waitForState(runner, "Running") yield* runner.cancel yield* Fiber.await(fiber) @@ -215,7 +220,7 @@ describe("Runner", () => { ) const a = yield* runner.ensureRunning(first).pipe(Effect.exit, Effect.forkChild) - yield* Effect.sleep("10 millis") + yield* waitForState(runner, "Running") const stop = yield* runner.cancel.pipe(Effect.forkChild) yield* Deferred.await(hit).pipe(Effect.timeout("250 millis")) @@ -293,38 +298,17 @@ describe("Runner", () => { const gate = yield* Deferred.make() const sh = yield* runner.startShell(Deferred.await(gate).pipe(Effect.as("first"))).pipe(Effect.forkChild) - yield* Effect.sleep("10 millis") + yield* waitForState(runner, "Shell") const exit = yield* runner.startShell(Effect.succeed("second")).pipe(Effect.exit) expect(Exit.isFailure(exit)).toBe(true) + if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Runner.Busy) yield* Deferred.succeed(gate, undefined) yield* Fiber.await(sh) }), ) - it.live( - "shell rejects via busy callback and cancel still stops the first shell", - Effect.gen(function* () { - const s = yield* Scope.Scope - const runner = Runner.make(s, { - busy: () => { - throw new Error("busy") - }, - }) - - const sh = yield* runner.startShell(Effect.never.pipe(Effect.as("aborted"))).pipe(Effect.forkChild) - yield* Effect.sleep("10 millis") - - const exit = yield* runner.startShell(Effect.succeed("second")).pipe(Effect.exit) - expect(Exit.isFailure(exit)).toBe(true) - - yield* runner.cancel - const done = yield* Fiber.await(sh) - expect(Exit.isFailure(done)).toBe(true) - }), - ) - it.live( "cancel interrupts shell", Effect.gen(function* () { @@ -333,7 +317,7 @@ describe("Runner", () => { const gate = yield* Deferred.make() const sh = yield* runner.startShell(Deferred.await(gate).pipe(Effect.as("ignored"))).pipe(Effect.forkChild) - yield* Effect.sleep("10 millis") + yield* waitForState(runner, "Shell") const stop = yield* runner.cancel.pipe(Effect.forkChild) const stopExit = yield* Fiber.await(stop).pipe(Effect.timeout("250 millis")) @@ -352,11 +336,18 @@ describe("Runner", () => { Effect.gen(function* () { const s = yield* Scope.Scope const runner = Runner.make(s, { onInterrupt: Effect.succeed("interrupted") }) + const ready = yield* Latch.make() const sh = yield* runner - .startShell(Effect.never.pipe(Effect.ensuring(Effect.die("boom")), Effect.as("ignored"))) + .startShell( + Effect.gen(function* () { + yield* ready.open + return yield* Effect.never.pipe(Effect.as("ignored")) + }).pipe(Effect.ensuring(Effect.die("boom"))), + ready, + ) .pipe(Effect.forkChild) - yield* Effect.sleep("10 millis") + yield* ready.await.pipe(Effect.timeout("250 millis")) yield* runner.cancel expect(Exit.isFailure(yield* Fiber.await(sh))).toBe(true) @@ -373,11 +364,11 @@ describe("Runner", () => { const gate = yield* Deferred.make() const sh = yield* runner.startShell(Deferred.await(gate).pipe(Effect.as("shell-result"))).pipe(Effect.forkChild) - yield* Effect.sleep("10 millis") + yield* waitForState(runner, "Shell") expect(runner.state._tag).toBe("Shell") const run = yield* runner.ensureRunning(Effect.succeed("run-result")).pipe(Effect.forkChild) - yield* Effect.sleep("10 millis") + yield* waitForState(runner, "ShellThenRun") expect(runner.state._tag).toBe("ShellThenRun") yield* Deferred.succeed(gate, undefined) @@ -399,7 +390,7 @@ describe("Runner", () => { const gate = yield* Deferred.make() const sh = yield* runner.startShell(Deferred.await(gate).pipe(Effect.as("shell"))).pipe(Effect.forkChild) - yield* Effect.sleep("10 millis") + yield* waitForState(runner, "Shell") const work = Effect.gen(function* () { yield* Ref.update(calls, (n) => n + 1) @@ -407,7 +398,7 @@ describe("Runner", () => { }) const a = yield* runner.ensureRunning(work).pipe(Effect.forkChild) const b = yield* runner.ensureRunning(work).pipe(Effect.forkChild) - yield* Effect.sleep("10 millis") + yield* waitForState(runner, "ShellThenRun") yield* Deferred.succeed(gate, undefined) yield* Fiber.await(sh) @@ -426,10 +417,10 @@ describe("Runner", () => { const runner = Runner.make(s) const sh = yield* runner.startShell(Effect.never.pipe(Effect.as("aborted"))).pipe(Effect.forkChild) - yield* Effect.sleep("10 millis") + yield* waitForState(runner, "Shell") const run = yield* runner.ensureRunning(Effect.succeed("y")).pipe(Effect.forkChild) - yield* Effect.sleep("10 millis") + yield* waitForState(runner, "ShellThenRun") expect(runner.state._tag).toBe("ShellThenRun") yield* runner.cancel @@ -465,7 +456,7 @@ describe("Runner", () => { onIdle: Ref.update(count, (n) => n + 1), }) const fiber = yield* runner.ensureRunning(Effect.never.pipe(Effect.as("x"))).pipe(Effect.forkChild) - yield* Effect.sleep("10 millis") + yield* waitForState(runner, "Running") yield* runner.cancel yield* Fiber.await(fiber) expect(yield* Ref.get(count)).toBeGreaterThanOrEqual(1) @@ -495,7 +486,7 @@ describe("Runner", () => { const gate = yield* Deferred.make() const fiber = yield* runner.ensureRunning(Deferred.await(gate).pipe(Effect.as("ok"))).pipe(Effect.forkChild) - yield* Effect.sleep("10 millis") + yield* waitForState(runner, "Running") expect(runner.busy).toBe(true) yield* Deferred.succeed(gate, undefined) @@ -512,7 +503,7 @@ describe("Runner", () => { const gate = yield* Deferred.make() const fiber = yield* runner.startShell(Deferred.await(gate).pipe(Effect.as("ok"))).pipe(Effect.forkChild) - yield* Effect.sleep("10 millis") + yield* waitForState(runner, "Shell") expect(runner.busy).toBe(true) yield* Deferred.succeed(gate, undefined) diff --git a/packages/opencode/test/effect/runtime-flags.test.ts b/packages/opencode/test/effect/runtime-flags.test.ts new file mode 100644 index 00000000000..1744ffb3411 --- /dev/null +++ b/packages/opencode/test/effect/runtime-flags.test.ts @@ -0,0 +1,240 @@ +import { describe, expect } from "bun:test" +import { ConfigProvider, Effect, Layer } from "effect" +import { RuntimeFlags } from "../../src/effect/runtime-flags" +import { it } from "../lib/effect" + +const fromConfig = (input: Record) => + RuntimeFlags.defaultLayer.pipe(Layer.provide(ConfigProvider.layer(ConfigProvider.fromUnknown(input)))) + +const readFlags = RuntimeFlags.Service.useSync((flags) => flags) + +describe("RuntimeFlags", () => { + it.effect("defaultLayer defaults autoShare to false", () => + Effect.gen(function* () { + const flags = yield* readFlags.pipe(Effect.provide(fromConfig({}))) + + expect(flags.autoShare).toBe(false) + }), + ) + + it.effect("defaultLayer parses plugin flags from the active ConfigProvider", () => + Effect.gen(function* () { + const flags = yield* readFlags.pipe( + Effect.provide( + fromConfig({ + KILO_PURE: "true", + KILO_DISABLE_DEFAULT_PLUGINS: "true", + KILO_DISABLE_CHANNEL_DB: "true", + KILO_AUTO_SHARE: "true", + KILO_DISABLE_EMBEDDED_WEB_UI: "true", + KILO_EXPERIMENTAL: "true", + KILO_ENABLE_EXA: "true", + KILO_ENABLE_PARALLEL: "true", + KILO_ENABLE_EXPERIMENTAL_MODELS: "true", + KILO_ENABLE_QUESTION_TOOL: "true", + KILO_CLIENT: "desktop", + }), + ), + ) + + expect(flags.pure).toBe(true) + expect(flags.autoShare).toBe(true) + expect(flags.disableDefaultPlugins).toBe(true) + expect(flags.disableChannelDb).toBe(true) + expect(flags.disableEmbeddedWebUi).toBe(true) + expect(flags.enableExa).toBe(true) + expect(flags.enableParallel).toBe(true) + expect(flags.enableExperimentalModels).toBe(true) + expect(flags.enableQuestionTool).toBe(true) + expect(flags.experimentalScout).toBe(true) + expect(flags.experimentalBackgroundSubagents).toBe(true) + expect(flags.experimentalLspTy).toBe(false) + expect(flags.experimentalLspTool).toBe(true) + expect(flags.experimentalOxfmt).toBe(true) + expect(flags.experimentalPlanMode).toBe(true) + expect(flags.experimentalEventSystem).toBe(true) + expect(flags.experimentalWorkspaces).toBe(true) + expect(flags.experimentalIconDiscovery).toBe(true) + expect(flags.client).toBe("desktop") + }), + ) + + it.effect("defaultLayer parses KILO_EXPERIMENTAL_LSP_TY", () => + Effect.gen(function* () { + const flags = yield* readFlags.pipe( + Effect.provide( + fromConfig({ + KILO_EXPERIMENTAL_LSP_TY: "true", + }), + ), + ) + + expect(flags.experimentalLspTy).toBe(true) + }), + ) + + it.effect("layer accepts partial test overrides and fills defaults from Config definitions", () => + Effect.gen(function* () { + const flags = yield* readFlags.pipe( + Effect.provide(RuntimeFlags.layer({ disableDefaultPlugins: true, bashDefaultTimeoutMs: 1_000 })), + ) + + expect(flags.pure).toBe(false) + expect(flags.autoShare).toBe(false) + expect(flags.disableDefaultPlugins).toBe(true) + expect(flags.disableChannelDb).toBe(false) + expect(flags.disableEmbeddedWebUi).toBe(false) + expect(flags.disableClaudeCodeSkills).toBe(false) + expect(flags.enableExa).toBe(false) + expect(flags.experimentalIconDiscovery).toBe(false) + expect(flags.experimentalOxfmt).toBe(false) + expect(flags.bashDefaultTimeoutMs).toBe(1_000) + expect(flags.enableExperimentalModels).toBe(false) + expect(flags.client).toBe("cli") + }), + ) + + it.effect("experimentalIconDiscovery defaults to false", () => + Effect.gen(function* () { + const flags = yield* readFlags.pipe(Effect.provide(fromConfig({}))) + + expect(flags.experimentalIconDiscovery).toBe(false) + }), + ) + + it.effect("experimentalIconDiscovery reads KILO_EXPERIMENTAL_ICON_DISCOVERY", () => + Effect.gen(function* () { + const flags = yield* readFlags.pipe(Effect.provide(fromConfig({ KILO_EXPERIMENTAL_ICON_DISCOVERY: "true" }))) + + expect(flags.experimentalIconDiscovery).toBe(true) + }), + ) + + it.effect("experimentalIconDiscovery inherits KILO_EXPERIMENTAL", () => + Effect.gen(function* () { + const flags = yield* readFlags.pipe(Effect.provide(fromConfig({ KILO_EXPERIMENTAL: "true" }))) + + expect(flags.experimentalIconDiscovery).toBe(true) + }), + ) + + it.effect("experimentalOxfmt defaults to false", () => + Effect.gen(function* () { + const flags = yield* readFlags.pipe(Effect.provide(fromConfig({}))) + + expect(flags.experimentalOxfmt).toBe(false) + }), + ) + + it.effect("experimentalOxfmt is enabled by KILO_EXPERIMENTAL_OXFMT", () => + Effect.gen(function* () { + const flags = yield* readFlags.pipe( + Effect.provide( + fromConfig({ + KILO_EXPERIMENTAL_OXFMT: "true", + }), + ), + ) + + expect(flags.experimentalOxfmt).toBe(true) + }), + ) + + it.effect("experimentalOxfmt inherits KILO_EXPERIMENTAL", () => + Effect.gen(function* () { + const flags = yield* readFlags.pipe( + Effect.provide( + fromConfig({ + KILO_EXPERIMENTAL: "true", + }), + ), + ) + + expect(flags.experimentalOxfmt).toBe(true) + }), + ) + + for (const input of [ + { name: "absent", config: {}, expected: undefined }, + { + name: "valid positive integer", + config: { KILO_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS: "1234" }, + expected: 1234, + }, + { + name: "invalid string", + config: { KILO_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS: "nope" }, + expected: undefined, + }, + { name: "zero", config: { KILO_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS: "0" }, expected: undefined }, + { name: "negative", config: { KILO_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS: "-1" }, expected: undefined }, + { + name: "non-integer", + config: { KILO_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS: "1.5" }, + expected: undefined, + }, + ]) { + it.effect(`parses bashDefaultTimeoutMs from config: ${input.name}`, () => + Effect.gen(function* () { + const flags = yield* readFlags.pipe(Effect.provide(fromConfig(input.config))) + + expect(flags.bashDefaultTimeoutMs).toBe(input.expected) + }), + ) + } + + it.effect("layer ignores the active ConfigProvider for omitted test overrides", () => + Effect.gen(function* () { + const flags = yield* readFlags.pipe( + Effect.provide(RuntimeFlags.layer()), + Effect.provide( + ConfigProvider.layer( + ConfigProvider.fromUnknown({ + KILO_PURE: "true", + KILO_DISABLE_DEFAULT_PLUGINS: "true", + KILO_EXPERIMENTAL: "true", + KILO_ENABLE_EXA: "true", + KILO_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS: "1234", + KILO_CLIENT: "desktop", + }), + ), + ), + ) + + expect(flags.pure).toBe(false) + expect(flags.disableDefaultPlugins).toBe(false) + expect(flags.disableChannelDb).toBe(false) + expect(flags.disableEmbeddedWebUi).toBe(false) + expect(flags.disableClaudeCodeSkills).toBe(false) + expect(flags.enableExa).toBe(false) + expect(flags.experimentalIconDiscovery).toBe(false) + expect(flags.experimentalOxfmt).toBe(false) + expect(flags.bashDefaultTimeoutMs).toBeUndefined() + expect(flags.client).toBe("cli") + }), + ) + + it.effect("disableClaudeCodeSkills defaults to false", () => + Effect.gen(function* () { + const flags = yield* readFlags.pipe(Effect.provide(fromConfig({}))) + + expect(flags.disableClaudeCodeSkills).toBe(false) + }), + ) + + it.effect("disableClaudeCodeSkills reads KILO_DISABLE_CLAUDE_CODE_SKILLS", () => + Effect.gen(function* () { + const flags = yield* readFlags.pipe(Effect.provide(fromConfig({ KILO_DISABLE_CLAUDE_CODE_SKILLS: "true" }))) + + expect(flags.disableClaudeCodeSkills).toBe(true) + }), + ) + + it.effect("disableClaudeCodeSkills inherits KILO_DISABLE_CLAUDE_CODE", () => + Effect.gen(function* () { + const flags = yield* readFlags.pipe(Effect.provide(fromConfig({ KILO_DISABLE_CLAUDE_CODE: "true" }))) + + expect(flags.disableClaudeCodeSkills).toBe(true) + }), + ) +}) diff --git a/packages/opencode/test/fake/account.ts b/packages/opencode/test/fake/account.ts new file mode 100644 index 00000000000..aeaa0735bc2 --- /dev/null +++ b/packages/opencode/test/fake/account.ts @@ -0,0 +1,9 @@ +import { Effect, Layer, Option } from "effect" +import { Account } from "../../src/account/account" + +export const empty = Layer.mock(Account.Service)({ + active: () => Effect.succeed(Option.none()), + activeOrg: () => Effect.succeed(Option.none()), +}) + +export * as AccountTest from "./account" diff --git a/packages/opencode/test/fake/auth.ts b/packages/opencode/test/fake/auth.ts new file mode 100644 index 00000000000..c82babb6a20 --- /dev/null +++ b/packages/opencode/test/fake/auth.ts @@ -0,0 +1,8 @@ +import { Effect, Layer } from "effect" +import { Auth } from "../../src/auth" + +export const empty = Layer.mock(Auth.Service)({ + all: () => Effect.succeed({}), +}) + +export * as AuthTest from "./auth" diff --git a/packages/opencode/test/fake/npm.ts b/packages/opencode/test/fake/npm.ts new file mode 100644 index 00000000000..57efe83a0b8 --- /dev/null +++ b/packages/opencode/test/fake/npm.ts @@ -0,0 +1,8 @@ +import { Npm } from "@opencode-ai/core/npm" +import { Effect, Layer } from "effect" + +export const noop = Layer.mock(Npm.Service)({ + install: () => Effect.void, +}) + +export * as NpmTest from "./npm" diff --git a/packages/opencode/test/fake/skill.ts b/packages/opencode/test/fake/skill.ts new file mode 100644 index 00000000000..1a329ace36a --- /dev/null +++ b/packages/opencode/test/fake/skill.ts @@ -0,0 +1,8 @@ +import { Effect, Layer } from "effect" +import { Skill } from "../../src/skill" + +export const empty = Layer.mock(Skill.Service)({ + dirs: () => Effect.succeed([]), +}) + +export * as SkillTest from "./skill" diff --git a/packages/opencode/test/file/fsmonitor.test.ts b/packages/opencode/test/file/fsmonitor.test.ts index f345cd0850e..b8d3bd60552 100644 --- a/packages/opencode/test/file/fsmonitor.test.ts +++ b/packages/opencode/test/file/fsmonitor.test.ts @@ -3,67 +3,71 @@ import { describe, expect, test } from "bun:test" import { Effect } from "effect" import fs from "fs/promises" import path from "path" -import { File } from "../../src/file" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" -import { provideInstance, tmpdir } from "../fixture/fixture" -const run = (eff: Effect.Effect) => - Effect.runPromise(provideInstance(Instance.directory)(eff.pipe(Effect.provide(File.defaultLayer)))) -const status = () => run(File.Service.use((svc) => svc.status())) -const read = (file: string) => run(File.Service.use((svc) => svc.read(file))) - -const wintest = process.platform === "win32" ? test : test.skip +const it = + process.platform === "win32" + ? (await import("../lib/effect")).testEffect((await import("../../src/file")).File.defaultLayer) + : undefined describe("file fsmonitor", () => { - wintest("status does not start fsmonitor for readonly git checks", async () => { - await using tmp = await tmpdir({ git: true }) - const target = path.join(tmp.path, "tracked.txt") + if (!it) { + test.skip("status does not start fsmonitor for readonly git checks", () => {}) + test.skip("read does not start fsmonitor for git diffs", () => {}) + return + } + + it.instance( + "status does not start fsmonitor for readonly git checks", + () => + Effect.gen(function* () { + const { File } = yield* Effect.promise(() => import("../../src/file")) + const { TestInstance } = yield* Effect.promise(() => import("../fixture/fixture")) + const directory = (yield* TestInstance).directory + const target = path.join(directory, "tracked.txt") - await fs.writeFile(target, "base\n") - await $`git add tracked.txt`.cwd(tmp.path).quiet() - await $`git commit -m init`.cwd(tmp.path).quiet() - await $`git config core.fsmonitor true`.cwd(tmp.path).quiet() - await $`git fsmonitor--daemon stop`.cwd(tmp.path).quiet().nothrow() - await fs.writeFile(target, "next\n") - await fs.writeFile(path.join(tmp.path, "new.txt"), "new\n") + yield* Effect.promise(() => fs.writeFile(target, "base\n")) + yield* Effect.promise(() => $`git add tracked.txt`.cwd(directory).quiet()) + yield* Effect.promise(() => $`git commit -m init`.cwd(directory).quiet()) + yield* Effect.promise(() => $`git config core.fsmonitor true`.cwd(directory).quiet()) + yield* Effect.promise(() => $`git fsmonitor--daemon stop`.cwd(directory).quiet().nothrow()) + yield* Effect.promise(() => fs.writeFile(target, "next\n")) + yield* Effect.promise(() => fs.writeFile(path.join(directory, "new.txt"), "new\n")) - const before = await $`git fsmonitor--daemon status`.cwd(tmp.path).quiet().nothrow() - expect(before.exitCode).not.toBe(0) + const before = yield* Effect.promise(() => $`git fsmonitor--daemon status`.cwd(directory).quiet().nothrow()) + expect(before.exitCode).not.toBe(0) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await status() - }, - }) + yield* File.Service.use((svc) => svc.status()) - const after = await $`git fsmonitor--daemon status`.cwd(tmp.path).quiet().nothrow() - expect(after.exitCode).not.toBe(0) - }) + const after = yield* Effect.promise(() => $`git fsmonitor--daemon status`.cwd(directory).quiet().nothrow()) + expect(after.exitCode).not.toBe(0) + }), + { git: true }, + ) - wintest("read does not start fsmonitor for git diffs", async () => { - await using tmp = await tmpdir({ git: true }) - const target = path.join(tmp.path, "tracked.txt") + it.instance( + "read does not start fsmonitor for git diffs", + () => + Effect.gen(function* () { + const { File } = yield* Effect.promise(() => import("../../src/file")) + const { TestInstance } = yield* Effect.promise(() => import("../fixture/fixture")) + const directory = (yield* TestInstance).directory + const target = path.join(directory, "tracked.txt") - await fs.writeFile(target, "base\n") - await $`git add tracked.txt`.cwd(tmp.path).quiet() - await $`git commit -m init`.cwd(tmp.path).quiet() - await $`git config core.fsmonitor true`.cwd(tmp.path).quiet() - await $`git fsmonitor--daemon stop`.cwd(tmp.path).quiet().nothrow() - await fs.writeFile(target, "next\n") + yield* Effect.promise(() => fs.writeFile(target, "base\n")) + yield* Effect.promise(() => $`git add tracked.txt`.cwd(directory).quiet()) + yield* Effect.promise(() => $`git commit -m init`.cwd(directory).quiet()) + yield* Effect.promise(() => $`git config core.fsmonitor true`.cwd(directory).quiet()) + yield* Effect.promise(() => $`git fsmonitor--daemon stop`.cwd(directory).quiet().nothrow()) + yield* Effect.promise(() => fs.writeFile(target, "next\n")) - const before = await $`git fsmonitor--daemon status`.cwd(tmp.path).quiet().nothrow() - expect(before.exitCode).not.toBe(0) + const before = yield* Effect.promise(() => $`git fsmonitor--daemon status`.cwd(directory).quiet().nothrow()) + expect(before.exitCode).not.toBe(0) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await read("tracked.txt") - }, - }) + yield* File.Service.use((svc) => svc.read("tracked.txt")) - const after = await $`git fsmonitor--daemon status`.cwd(tmp.path).quiet().nothrow() - expect(after.exitCode).not.toBe(0) - }) + const after = yield* Effect.promise(() => $`git fsmonitor--daemon status`.cwd(directory).quiet().nothrow()) + expect(after.exitCode).not.toBe(0) + }), + { git: true }, + ) }) diff --git a/packages/opencode/test/file/index.test.ts b/packages/opencode/test/file/index.test.ts index 4ddee244e54..b7d531c63d2 100644 --- a/packages/opencode/test/file/index.test.ts +++ b/packages/opencode/test/file/index.test.ts @@ -1,557 +1,514 @@ -import { afterEach, describe, test, expect } from "bun:test" +import { afterEach, describe, expect } from "bun:test" +import { AppFileSystem } from "@opencode-ai/core/filesystem" import { $ } from "bun" -import { Effect } from "effect" +import { Cause, Effect, Exit, Layer } from "effect" import path from "path" import fs from "fs/promises" import { File } from "../../src/file" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" -import { Filesystem } from "@/util/filesystem" -import { disposeAllInstances, provideInstance, tmpdir } from "../fixture/fixture" +import { disposeAllInstances, TestInstance, withTmpdirInstance } from "../fixture/fixture" +import { testEffect } from "../lib/effect" afterEach(async () => { await disposeAllInstances() }) -const init = () => run(File.Service.use((svc) => svc.init())) -const run = (eff: Effect.Effect) => - Effect.runPromise(provideInstance(Instance.directory)(eff.pipe(Effect.provide(File.defaultLayer)))) -const status = () => run(File.Service.use((svc) => svc.status())) -const read = (file: string) => run(File.Service.use((svc) => svc.read(file))) -const list = (dir?: string) => run(File.Service.use((svc) => svc.list(dir))) -const search = (input: { query: string; limit?: number; dirs?: boolean; type?: "file" | "directory" }) => - run(File.Service.use((svc) => svc.search(input))) +const it = testEffect(Layer.mergeAll(File.defaultLayer, AppFileSystem.defaultLayer)) + +const init = Effect.fn("FileTest.init")(function* () { + const file = yield* File.Service + return yield* file.init() +}) + +const status = Effect.fn("FileTest.status")(function* () { + const file = yield* File.Service + return yield* file.status() +}) + +const read = Effect.fn("FileTest.read")(function* (input: string) { + const file = yield* File.Service + return yield* file.read(input) +}) + +const list = Effect.fn("FileTest.list")(function* (dir?: string) { + const file = yield* File.Service + return yield* file.list(dir) +}) + +const search = Effect.fn("FileTest.search")(function* (input: { + query: string + limit?: number + dirs?: boolean + type?: "file" | "directory" +}) { + const file = yield* File.Service + return yield* file.search(input) +}) + +const gitAddAll = (directory: string) => Effect.promise(() => $`git add .`.cwd(directory).quiet()) +const gitCommit = (directory: string, message: string) => + Effect.promise(() => $`git commit -m ${message}`.cwd(directory).quiet()) + +const failureMessage = (self: Effect.Effect) => + Effect.gen(function* () { + const exit = yield* self.pipe(Effect.exit) + if (Exit.isFailure(exit)) { + const error = Cause.squash(exit.cause) + return error instanceof Error ? error.message : String(error) + } + throw new Error("expected effect to fail") + }) + +const setupSearchableRepo = Effect.fn("FileTest.setupSearchableRepo")(function* (directory: string) { + const fsys = yield* AppFileSystem.Service + yield* fsys.writeWithDirs(path.join(directory, "index.ts"), "code") + yield* fsys.writeWithDirs(path.join(directory, "utils.ts"), "utils") + yield* fsys.writeWithDirs(path.join(directory, "readme.md"), "readme") + yield* fsys.writeWithDirs(path.join(directory, "src", "main.ts"), "main") + yield* fsys.writeWithDirs(path.join(directory, ".hidden", "secret.ts"), "secret") +}) describe("file/index Filesystem patterns", () => { describe("read() - text content", () => { - test("reads text file via Filesystem.readText()", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "test.txt") - await fs.writeFile(filepath, "Hello World", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await read("test.txt") - expect(result.type).toBe("text") - expect(result.content).toBe("Hello World") - }, - }) - }) - - test("reads with Filesystem.exists() check", async () => { - await using tmp = await tmpdir() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // Non-existent file should return empty content - const result = await read("nonexistent.txt") - expect(result.type).toBe("text") - expect(result.content).toBe("") - }, - }) - }) - - test("trims whitespace from text content", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "test.txt") - await fs.writeFile(filepath, " content with spaces \n\n", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await read("test.txt") - expect(result.content).toBe("content with spaces") - }, - }) - }) - - test("handles empty text file", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "empty.txt") - await fs.writeFile(filepath, "", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await read("empty.txt") - expect(result.type).toBe("text") - expect(result.content).toBe("") - }, - }) - }) - - test("handles multi-line text files", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "multiline.txt") - await fs.writeFile(filepath, "line1\nline2\nline3", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await read("multiline.txt") - expect(result.content).toBe("line1\nline2\nline3") - }, - }) - }) + it.instance("reads text file via Filesystem.readText()", () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "test.txt"), "Hello World", "utf-8")) + + const result = yield* read("test.txt") + expect(result.type).toBe("text") + expect(result.content).toBe("Hello World") + }), + ) + + it.instance("reads with Filesystem.exists() check", () => + Effect.gen(function* () { + const result = yield* read("nonexistent.txt") + expect(result.type).toBe("text") + expect(result.content).toBe("") + }), + ) + + it.instance("trims whitespace from text content", () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => + fs.writeFile(path.join(test.directory, "test.txt"), " content with spaces \n\n", "utf-8"), + ) + + const result = yield* read("test.txt") + expect(result.content).toBe("content with spaces") + }), + ) + + it.instance("handles empty text file", () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "empty.txt"), "", "utf-8")) + + const result = yield* read("empty.txt") + expect(result.type).toBe("text") + expect(result.content).toBe("") + }), + ) + + it.instance("handles multi-line text files", () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => + fs.writeFile(path.join(test.directory, "multiline.txt"), "line1\nline2\nline3", "utf-8"), + ) + + const result = yield* read("multiline.txt") + expect(result.content).toBe("line1\nline2\nline3") + }), + ) }) describe("read() - binary content", () => { - test("reads binary file via Filesystem.readArrayBuffer()", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "image.png") - const binaryContent = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]) - await fs.writeFile(filepath, binaryContent) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await read("image.png") - expect(result.type).toBe("text") // Images return as text with base64 encoding - expect(result.encoding).toBe("base64") - expect(result.mimeType).toBe("image/png") - expect(result.content).toBe(binaryContent.toString("base64")) - }, - }) - }) - - test("returns empty for binary non-image files", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "binary.so") - await fs.writeFile(filepath, Buffer.from([0x7f, 0x45, 0x4c, 0x46]), "binary") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await read("binary.so") - expect(result.type).toBe("binary") - expect(result.content).toBe("") - }, - }) - }) + it.instance("reads binary file via Filesystem.readArrayBuffer()", () => + Effect.gen(function* () { + const test = yield* TestInstance + const binaryContent = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]) + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "image.png"), binaryContent)) + + const result = yield* read("image.png") + expect(result.type).toBe("text") + expect(result.encoding).toBe("base64") + expect(result.mimeType).toBe("image/png") + expect(result.content).toBe(binaryContent.toString("base64")) + }), + ) + + it.instance("returns empty for binary non-image files", () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => + fs.writeFile(path.join(test.directory, "binary.so"), Buffer.from([0x7f, 0x45, 0x4c, 0x46])), + ) + + const result = yield* read("binary.so") + expect(result.type).toBe("binary") + expect(result.content).toBe("") + }), + ) }) describe("read() - Filesystem.mimeType()", () => { - test("detects MIME type via Filesystem.mimeType()", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "test.json") - await fs.writeFile(filepath, '{"key": "value"}', "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - expect(await Filesystem.mimeType(filepath)).toContain("application/json") - - const result = await read("test.json") - expect(result.type).toBe("text") - }, - }) - }) - - test("handles various image MIME types", async () => { - await using tmp = await tmpdir() - const testCases = [ - { ext: "jpg", mime: "image/jpeg" }, - { ext: "png", mime: "image/png" }, - { ext: "gif", mime: "image/gif" }, - { ext: "webp", mime: "image/webp" }, - ] - - for (const { ext, mime } of testCases) { - const filepath = path.join(tmp.path, `test.${ext}`) - await fs.writeFile(filepath, Buffer.from([0x00, 0x00, 0x00, 0x00]), "binary") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - expect(await Filesystem.mimeType(filepath)).toContain(mime) - }, - }) - } - }) + it.instance("detects MIME type via Filesystem.mimeType()", () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "test.json") + yield* Effect.promise(() => fs.writeFile(filepath, '{"key": "value"}', "utf-8")) + + expect(AppFileSystem.mimeType(filepath)).toContain("application/json") + + const result = yield* read("test.json") + expect(result.type).toBe("text") + }), + ) + + it.instance("handles various image MIME types", () => + Effect.gen(function* () { + const test = yield* TestInstance + const testCases = [ + { ext: "jpg", mime: "image/jpeg" }, + { ext: "png", mime: "image/png" }, + { ext: "gif", mime: "image/gif" }, + { ext: "webp", mime: "image/webp" }, + ] + + for (const testCase of testCases) { + const filepath = path.join(test.directory, `test.${testCase.ext}`) + yield* Effect.promise(() => fs.writeFile(filepath, Buffer.from([0x00, 0x00, 0x00, 0x00]))) + expect(AppFileSystem.mimeType(filepath)).toContain(testCase.mime) + } + }), + ) }) describe("list() - Filesystem.exists() and readText()", () => { - test("reads .gitignore via Filesystem.exists() and readText()", async () => { - await using tmp = await tmpdir({ git: true }) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const gitignorePath = path.join(tmp.path, ".gitignore") - await fs.writeFile(gitignorePath, "node_modules\ndist\n", "utf-8") - - // This is used internally in list() - expect(await Filesystem.exists(gitignorePath)).toBe(true) - - const content = await Filesystem.readText(gitignorePath) - expect(content).toContain("node_modules") - }, - }) - }) - - test("reads .ignore file similarly", async () => { - await using tmp = await tmpdir({ git: true }) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const ignorePath = path.join(tmp.path, ".ignore") - await fs.writeFile(ignorePath, "*.log\n.env\n", "utf-8") - - expect(await Filesystem.exists(ignorePath)).toBe(true) - expect(await Filesystem.readText(ignorePath)).toContain("*.log") - }, - }) - }) - - test("handles missing .gitignore gracefully", async () => { - await using tmp = await tmpdir({ git: true }) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const gitignorePath = path.join(tmp.path, ".gitignore") - expect(await Filesystem.exists(gitignorePath)).toBe(false) - - // list() should still work - const nodes = await list() + it.instance( + "reads .gitignore via AppFileSystem.existsSafe() and readFileString()", + () => + Effect.gen(function* () { + const fsys = yield* AppFileSystem.Service + const test = yield* TestInstance + const gitignorePath = path.join(test.directory, ".gitignore") + yield* fsys.writeFileString(gitignorePath, "node_modules\ndist\n") + + expect(yield* fsys.existsSafe(gitignorePath)).toBe(true) + expect(yield* fsys.readFileString(gitignorePath)).toContain("node_modules") + }), + { git: true }, + ) + + it.instance( + "reads .ignore file similarly", + () => + Effect.gen(function* () { + const fsys = yield* AppFileSystem.Service + const test = yield* TestInstance + const ignorePath = path.join(test.directory, ".ignore") + yield* fsys.writeFileString(ignorePath, "*.log\n.env\n") + + expect(yield* fsys.existsSafe(ignorePath)).toBe(true) + expect(yield* fsys.readFileString(ignorePath)).toContain("*.log") + }), + { git: true }, + ) + + it.instance( + "handles missing .gitignore gracefully", + () => + Effect.gen(function* () { + const fsys = yield* AppFileSystem.Service + const test = yield* TestInstance + const gitignorePath = path.join(test.directory, ".gitignore") + expect(yield* fsys.existsSafe(gitignorePath)).toBe(false) + + const nodes = yield* list() expect(Array.isArray(nodes)).toBe(true) - }, - }) - }) + }), + { git: true }, + ) }) - describe("File.changed() - Filesystem.readText() for untracked files", () => { - test("reads untracked files via Filesystem.readText()", async () => { - await using tmp = await tmpdir({ git: true }) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const untrackedPath = path.join(tmp.path, "untracked.txt") - await fs.writeFile(untrackedPath, "new content\nwith multiple lines", "utf-8") - - // This is how File.changed() reads untracked files - const content = await Filesystem.readText(untrackedPath) - const lines = content.split("\n").length - expect(lines).toBe(2) - }, - }) - }) + describe("File.changed() - AppFileSystem.readFileString() for untracked files", () => { + it.instance( + "reads untracked files via AppFileSystem.readFileString()", + () => + Effect.gen(function* () { + const fsys = yield* AppFileSystem.Service + const test = yield* TestInstance + const untrackedPath = path.join(test.directory, "untracked.txt") + yield* fsys.writeFileString(untrackedPath, "new content\nwith multiple lines") + + const content = yield* fsys.readFileString(untrackedPath) + expect(content.split("\n").length).toBe(2) + }), + { git: true }, + ) }) describe("Error handling", () => { - test("handles errors gracefully in Filesystem.readText()", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "readonly.txt") - await fs.writeFile(filepath, "content", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const nonExistentPath = path.join(tmp.path, "does-not-exist.txt") - // Filesystem.readText() on non-existent file throws - await expect(Filesystem.readText(nonExistentPath)).rejects.toThrow() - - // But read() handles this gracefully - const result = await read("does-not-exist.txt") - expect(result.content).toBe("") - }, - }) - }) - - test("handles errors in Filesystem.readArrayBuffer()", async () => { - await using tmp = await tmpdir() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const nonExistentPath = path.join(tmp.path, "does-not-exist.bin") - const buffer = await Filesystem.readArrayBuffer(nonExistentPath).catch(() => new ArrayBuffer(0)) - expect(buffer.byteLength).toBe(0) - }, - }) - }) - - test("returns empty array buffer on error for images", async () => { - await using tmp = await tmpdir() - const _filepath = path.join(tmp.path, "broken.png") - // Don't create the file - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // read() handles missing images gracefully - const result = await read("broken.png") - expect(result.type).toBe("text") - expect(result.content).toBe("") - }, - }) - }) + it.instance("handles errors gracefully in AppFileSystem.readFileString()", () => + Effect.gen(function* () { + const fsys = yield* AppFileSystem.Service + const test = yield* TestInstance + yield* fsys.writeFileString(path.join(test.directory, "readonly.txt"), "content") + + const nonExistentPath = path.join(test.directory, "does-not-exist.txt") + expect(Exit.isFailure(yield* fsys.readFileString(nonExistentPath).pipe(Effect.exit))).toBe(true) + + const result = yield* read("does-not-exist.txt") + expect(result.content).toBe("") + }), + ) + + it.instance("handles errors in AppFileSystem.readFile()", () => + Effect.gen(function* () { + const fsys = yield* AppFileSystem.Service + const test = yield* TestInstance + const nonExistentPath = path.join(test.directory, "does-not-exist.bin") + const buffer = yield* fsys.readFile(nonExistentPath).pipe(Effect.orElseSucceed(() => new Uint8Array(0))) + expect(buffer.byteLength).toBe(0) + }), + ) + + it.instance("returns empty array buffer on error for images", () => + Effect.gen(function* () { + const result = yield* read("broken.png") + expect(result.type).toBe("text") + expect(result.content).toBe("") + }), + ) }) describe("shouldEncode() logic", () => { - test("treats .ts files as text", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "test.ts") - await fs.writeFile(filepath, "export const value = 1", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await read("test.ts") - expect(result.type).toBe("text") - expect(result.content).toBe("export const value = 1") - }, - }) - }) - - test("treats .mts files as text", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "test.mts") - await fs.writeFile(filepath, "export const value = 1", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await read("test.mts") - expect(result.type).toBe("text") - expect(result.content).toBe("export const value = 1") - }, - }) - }) - - test("treats .sh files as text", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "test.sh") - await fs.writeFile(filepath, "#!/usr/bin/env bash\necho hello", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await read("test.sh") - expect(result.type).toBe("text") - expect(result.content).toBe("#!/usr/bin/env bash\necho hello") - }, - }) - }) - - test("treats Dockerfile as text", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "Dockerfile") - await fs.writeFile(filepath, "FROM alpine:3.20", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await read("Dockerfile") - expect(result.type).toBe("text") - expect(result.content).toBe("FROM alpine:3.20") - }, - }) - }) - - test("returns encoding info for text files", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "test.txt") - await fs.writeFile(filepath, "simple text", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await read("test.txt") - expect(result.encoding).toBeUndefined() - expect(result.type).toBe("text") - }, - }) - }) - - test("returns base64 encoding for images", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "test.jpg") - await fs.writeFile(filepath, Buffer.from([0xff, 0xd8, 0xff, 0xe0]), "binary") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await read("test.jpg") - expect(result.encoding).toBe("base64") - expect(result.mimeType).toBe("image/jpeg") - }, - }) - }) + it.instance("treats .ts files as text", () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => + fs.writeFile(path.join(test.directory, "test.ts"), "export const value = 1", "utf-8"), + ) + + const result = yield* read("test.ts") + expect(result.type).toBe("text") + expect(result.content).toBe("export const value = 1") + }), + ) + + it.instance("treats .mts files as text", () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => + fs.writeFile(path.join(test.directory, "test.mts"), "export const value = 1", "utf-8"), + ) + + const result = yield* read("test.mts") + expect(result.type).toBe("text") + expect(result.content).toBe("export const value = 1") + }), + ) + + it.instance("treats .sh files as text", () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => + fs.writeFile(path.join(test.directory, "test.sh"), "#!/usr/bin/env bash\necho hello", "utf-8"), + ) + + const result = yield* read("test.sh") + expect(result.type).toBe("text") + expect(result.content).toBe("#!/usr/bin/env bash\necho hello") + }), + ) + + it.instance("treats Dockerfile as text", () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "Dockerfile"), "FROM alpine:3.20", "utf-8")) + + const result = yield* read("Dockerfile") + expect(result.type).toBe("text") + expect(result.content).toBe("FROM alpine:3.20") + }), + ) + + it.instance("returns encoding info for text files", () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "test.txt"), "simple text", "utf-8")) + + const result = yield* read("test.txt") + expect(result.encoding).toBeUndefined() + expect(result.type).toBe("text") + }), + ) + + it.instance("returns base64 encoding for images", () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => + fs.writeFile(path.join(test.directory, "test.jpg"), Buffer.from([0xff, 0xd8, 0xff, 0xe0])), + ) + + const result = yield* read("test.jpg") + expect(result.encoding).toBe("base64") + expect(result.mimeType).toBe("image/jpeg") + }), + ) }) describe("Path security", () => { - test("throws for paths outside project directory", async () => { - await using tmp = await tmpdir() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await expect(read("../outside.txt")).rejects.toThrow("Access denied") - }, - }) - }) - - test("throws for paths outside project directory", async () => { - await using tmp = await tmpdir() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await expect(read("../outside.txt")).rejects.toThrow("Access denied") - }, - }) - }) + it.instance("throws for paths outside project directory", () => + Effect.gen(function* () { + expect(yield* failureMessage(read("../outside.txt"))).toContain("Access denied") + }), + ) + + it.instance("throws for paths outside project directory", () => + Effect.gen(function* () { + expect(yield* failureMessage(read("../outside.txt"))).toContain("Access denied") + }), + ) }) describe("status()", () => { - test("detects modified file", async () => { - await using tmp = await tmpdir({ git: true }) - const filepath = path.join(tmp.path, "file.txt") - await fs.writeFile(filepath, "original\n", "utf-8") - await $`git add .`.cwd(tmp.path).quiet() - await $`git commit -m "add file"`.cwd(tmp.path).quiet() - await fs.writeFile(filepath, "modified\nextra line\n", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await status() - const entry = result.find((f) => f.path === "file.txt") + it.instance( + "detects modified file", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "file.txt") + yield* Effect.promise(() => fs.writeFile(filepath, "original\n", "utf-8")) + yield* gitAddAll(test.directory) + yield* gitCommit(test.directory, "add file") + yield* Effect.promise(() => fs.writeFile(filepath, "modified\nextra line\n", "utf-8")) + + const result = yield* status() + const entry = result.find((file) => file.path === "file.txt") expect(entry).toBeDefined() expect(entry!.status).toBe("modified") expect(entry!.added).toBeGreaterThan(0) expect(entry!.removed).toBeGreaterThan(0) - }, - }) - }) - - test("detects untracked file as added", async () => { - await using tmp = await tmpdir({ git: true }) - await fs.writeFile(path.join(tmp.path, "new.txt"), "line1\nline2\nline3\n", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await status() - const entry = result.find((f) => f.path === "new.txt") + }), + { git: true }, + ) + + it.instance( + "detects untracked file as added", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => + fs.writeFile(path.join(test.directory, "new.txt"), "line1\nline2\nline3\n", "utf-8"), + ) + + const result = yield* status() + const entry = result.find((file) => file.path === "new.txt") expect(entry).toBeDefined() expect(entry!.status).toBe("added") - expect(entry!.added).toBe(4) // 3 lines + trailing newline splits to 4 + expect(entry!.added).toBe(4) expect(entry!.removed).toBe(0) - }, - }) - }) - - test("detects deleted file", async () => { - await using tmp = await tmpdir({ git: true }) - const filepath = path.join(tmp.path, "gone.txt") - await fs.writeFile(filepath, "content\n", "utf-8") - await $`git add .`.cwd(tmp.path).quiet() - await $`git commit -m "add file"`.cwd(tmp.path).quiet() - await fs.rm(filepath) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await status() - // Deleted files appear in both numstat (as "modified") and diff-filter=D (as "deleted") - const entries = result.filter((f) => f.path === "gone.txt") - expect(entries.some((e) => e.status === "deleted")).toBe(true) - }, - }) - }) - - test("detects mixed changes", async () => { - await using tmp = await tmpdir({ git: true }) - await fs.writeFile(path.join(tmp.path, "keep.txt"), "keep\n", "utf-8") - await fs.writeFile(path.join(tmp.path, "remove.txt"), "remove\n", "utf-8") - await $`git add .`.cwd(tmp.path).quiet() - await $`git commit -m "initial"`.cwd(tmp.path).quiet() - - // Modify one, delete one, add one - await fs.writeFile(path.join(tmp.path, "keep.txt"), "changed\n", "utf-8") - await fs.rm(path.join(tmp.path, "remove.txt")) - await fs.writeFile(path.join(tmp.path, "brand-new.txt"), "hello\n", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await status() - expect(result.some((f) => f.path === "keep.txt" && f.status === "modified")).toBe(true) - expect(result.some((f) => f.path === "remove.txt" && f.status === "deleted")).toBe(true) - expect(result.some((f) => f.path === "brand-new.txt" && f.status === "added")).toBe(true) - }, - }) - }) - - test("returns empty for non-git project", async () => { - await using tmp = await tmpdir() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await status() - expect(result).toEqual([]) - }, - }) - }) - - test("returns empty for clean repo", async () => { - await using tmp = await tmpdir({ git: true }) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await status() - expect(result).toEqual([]) - }, - }) - }) - - test("parses binary numstat as 0", async () => { - await using tmp = await tmpdir({ git: true }) - const filepath = path.join(tmp.path, "data.bin") - // Write content with null bytes so git treats it as binary - const binaryData = Buffer.alloc(256) - for (let i = 0; i < 256; i++) binaryData[i] = i - await fs.writeFile(filepath, binaryData) - await $`git add .`.cwd(tmp.path).quiet() - await $`git commit -m "add binary"`.cwd(tmp.path).quiet() - // Modify the binary - const modified = Buffer.alloc(512) - for (let i = 0; i < 512; i++) modified[i] = i % 256 - await fs.writeFile(filepath, modified) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await status() - const entry = result.find((f) => f.path === "data.bin") + }), + { git: true }, + ) + + it.instance( + "detects deleted file", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "gone.txt") + yield* Effect.promise(() => fs.writeFile(filepath, "content\n", "utf-8")) + yield* gitAddAll(test.directory) + yield* gitCommit(test.directory, "add file") + yield* Effect.promise(() => fs.rm(filepath)) + + const result = yield* status() + const entries = result.filter((file) => file.path === "gone.txt") + expect(entries.some((entry) => entry.status === "deleted")).toBe(true) + }), + { git: true }, + ) + + it.instance( + "detects mixed changes", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "keep.txt"), "keep\n", "utf-8")) + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "remove.txt"), "remove\n", "utf-8")) + yield* gitAddAll(test.directory) + yield* gitCommit(test.directory, "initial") + + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "keep.txt"), "changed\n", "utf-8")) + yield* Effect.promise(() => fs.rm(path.join(test.directory, "remove.txt"))) + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "brand-new.txt"), "hello\n", "utf-8")) + + const result = yield* status() + expect(result.some((file) => file.path === "keep.txt" && file.status === "modified")).toBe(true) + expect(result.some((file) => file.path === "remove.txt" && file.status === "deleted")).toBe(true) + expect(result.some((file) => file.path === "brand-new.txt" && file.status === "added")).toBe(true) + }), + { git: true }, + ) + + it.instance("returns empty for non-git project", () => + Effect.gen(function* () { + expect(yield* status()).toEqual([]) + }), + ) + + it.instance( + "returns empty for clean repo", + () => + Effect.gen(function* () { + expect(yield* status()).toEqual([]) + }), + { git: true }, + ) + + it.instance( + "parses binary numstat as 0", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "data.bin") + yield* Effect.promise(() => + fs.writeFile(filepath, Buffer.from(Array.from({ length: 256 }, (_, index) => index))), + ) + yield* gitAddAll(test.directory) + yield* gitCommit(test.directory, "add binary") + yield* Effect.promise(() => + fs.writeFile(filepath, Buffer.from(Array.from({ length: 512 }, (_, index) => index % 256))), + ) + + const result = yield* status() + const entry = result.find((file) => file.path === "data.bin") expect(entry).toBeDefined() expect(entry!.status).toBe("modified") expect(entry!.added).toBe(0) expect(entry!.removed).toBe(0) - }, - }) - }) + }), + { git: true }, + ) }) describe("list()", () => { - test("returns files and directories with correct shape", async () => { - await using tmp = await tmpdir({ git: true }) - await fs.mkdir(path.join(tmp.path, "subdir")) - await fs.writeFile(path.join(tmp.path, "file.txt"), "content", "utf-8") - await fs.writeFile(path.join(tmp.path, "subdir", "nested.txt"), "nested", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const nodes = await list() + it.instance( + "returns files and directories with correct shape", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => fs.mkdir(path.join(test.directory, "subdir"))) + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "file.txt"), "content", "utf-8")) + yield* Effect.promise(() => + fs.writeFile(path.join(test.directory, "subdir", "nested.txt"), "nested", "utf-8"), + ) + + const nodes = yield* list() expect(nodes.length).toBeGreaterThanOrEqual(2) for (const node of nodes) { expect(node).toHaveProperty("name") @@ -561,290 +518,264 @@ describe("file/index Filesystem patterns", () => { expect(node).toHaveProperty("ignored") expect(["file", "directory"]).toContain(node.type) } - }, - }) - }) - - test("sorts directories before files, alphabetical within each", async () => { - await using tmp = await tmpdir({ git: true }) - await fs.mkdir(path.join(tmp.path, "beta")) - await fs.mkdir(path.join(tmp.path, "alpha")) - await fs.writeFile(path.join(tmp.path, "zz.txt"), "", "utf-8") - await fs.writeFile(path.join(tmp.path, "aa.txt"), "", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const nodes = await list() - const dirs = nodes.filter((n) => n.type === "directory") - const files = nodes.filter((n) => n.type === "file") - // Dirs come first - const firstFile = nodes.findIndex((n) => n.type === "file") - const lastDir = nodes.findLastIndex((n) => n.type === "directory") + }), + { git: true }, + ) + + it.instance( + "sorts directories before files, alphabetical within each", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => fs.mkdir(path.join(test.directory, "beta"))) + yield* Effect.promise(() => fs.mkdir(path.join(test.directory, "alpha"))) + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "zz.txt"), "", "utf-8")) + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "aa.txt"), "", "utf-8")) + + const nodes = yield* list() + const dirs = nodes.filter((node) => node.type === "directory") + const files = nodes.filter((node) => node.type === "file") + const firstFile = nodes.findIndex((node) => node.type === "file") + const lastDir = nodes.findLastIndex((node) => node.type === "directory") if (lastDir >= 0 && firstFile >= 0) { expect(lastDir).toBeLessThan(firstFile) } - // Alphabetical within dirs - expect(dirs.map((d) => d.name)).toEqual(dirs.map((d) => d.name).toSorted()) - // Alphabetical within files - expect(files.map((f) => f.name)).toEqual(files.map((f) => f.name).toSorted()) - }, - }) - }) - - test("excludes .git and .DS_Store", async () => { - await using tmp = await tmpdir({ git: true }) - await fs.writeFile(path.join(tmp.path, ".DS_Store"), "", "utf-8") - await fs.writeFile(path.join(tmp.path, "visible.txt"), "", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const nodes = await list() - const names = nodes.map((n) => n.name) + expect(dirs.map((dir) => dir.name)).toEqual(dirs.map((dir) => dir.name).toSorted()) + expect(files.map((file) => file.name)).toEqual(files.map((file) => file.name).toSorted()) + }), + { git: true }, + ) + + it.instance( + "excludes .git and .DS_Store", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, ".DS_Store"), "", "utf-8")) + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "visible.txt"), "", "utf-8")) + + const names = (yield* list()).map((node) => node.name) expect(names).not.toContain(".git") expect(names).not.toContain(".DS_Store") expect(names).toContain("visible.txt") - }, - }) - }) - - test("marks gitignored files as ignored", async () => { - await using tmp = await tmpdir({ git: true }) - await fs.writeFile(path.join(tmp.path, ".gitignore"), "*.log\nbuild/\n", "utf-8") - await fs.writeFile(path.join(tmp.path, "app.log"), "log data", "utf-8") - await fs.writeFile(path.join(tmp.path, "main.ts"), "code", "utf-8") - await fs.mkdir(path.join(tmp.path, "build")) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const nodes = await list() - const logNode = nodes.find((n) => n.name === "app.log") - const tsNode = nodes.find((n) => n.name === "main.ts") - const buildNode = nodes.find((n) => n.name === "build") - expect(logNode?.ignored).toBe(true) - expect(tsNode?.ignored).toBe(false) - expect(buildNode?.ignored).toBe(true) - }, - }) - }) - - test("lists subdirectory contents", async () => { - await using tmp = await tmpdir({ git: true }) - await fs.mkdir(path.join(tmp.path, "sub")) - await fs.writeFile(path.join(tmp.path, "sub", "a.txt"), "", "utf-8") - await fs.writeFile(path.join(tmp.path, "sub", "b.txt"), "", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const nodes = await list("sub") + }), + { git: true }, + ) + + it.instance( + "marks gitignored files as ignored", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, ".gitignore"), "*.log\nbuild/\n", "utf-8")) + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "app.log"), "log data", "utf-8")) + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "main.ts"), "code", "utf-8")) + yield* Effect.promise(() => fs.mkdir(path.join(test.directory, "build"))) + + const nodes = yield* list() + expect(nodes.find((node) => node.name === "app.log")?.ignored).toBe(true) + expect(nodes.find((node) => node.name === "main.ts")?.ignored).toBe(false) + expect(nodes.find((node) => node.name === "build")?.ignored).toBe(true) + }), + { git: true }, + ) + + it.instance( + "lists subdirectory contents", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => fs.mkdir(path.join(test.directory, "sub"))) + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "sub", "a.txt"), "", "utf-8")) + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "sub", "b.txt"), "", "utf-8")) + + const nodes = yield* list("sub") expect(nodes.length).toBe(2) - expect(nodes.map((n) => n.name).sort()).toEqual(["a.txt", "b.txt"]) - // Paths should be relative to project root (normalize for Windows) + expect(nodes.map((node) => node.name).sort()).toEqual(["a.txt", "b.txt"]) expect(nodes[0].path.replaceAll("\\", "/").startsWith("sub/")).toBe(true) - }, - }) - }) - - test("throws for paths outside project directory", async () => { - await using tmp = await tmpdir({ git: true }) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await expect(list("../outside")).rejects.toThrow("Access denied") - }, - }) - }) - - test("works without git", async () => { - await using tmp = await tmpdir() - await fs.writeFile(path.join(tmp.path, "file.txt"), "hi", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const nodes = await list() - expect(nodes.length).toBeGreaterThanOrEqual(1) - // Without git, ignored should be false for all - for (const node of nodes) { - expect(node.ignored).toBe(false) - } - }, - }) - }) + }), + { git: true }, + ) + + it.instance( + "throws for paths outside project directory", + () => + Effect.gen(function* () { + expect(yield* failureMessage(list("../outside"))).toContain("Access denied") + }), + { git: true }, + ) + + it.instance("works without git", () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "file.txt"), "hi", "utf-8")) + + const nodes = yield* list() + expect(nodes.length).toBeGreaterThanOrEqual(1) + for (const node of nodes) { + expect(node.ignored).toBe(false) + } + }), + ) }) - // kilocode_change - skip on windows: address windows ci failures #9496 - describe.skipIf(process.platform === "win32")("search()", () => { - async function setupSearchableRepo() { - const tmp = await tmpdir({ git: true }) - await fs.writeFile(path.join(tmp.path, "index.ts"), "code", "utf-8") - await fs.writeFile(path.join(tmp.path, "utils.ts"), "utils", "utf-8") - await fs.writeFile(path.join(tmp.path, "readme.md"), "readme", "utf-8") - await fs.mkdir(path.join(tmp.path, "src")) - await fs.mkdir(path.join(tmp.path, ".hidden")) - await fs.writeFile(path.join(tmp.path, "src", "main.ts"), "main", "utf-8") - await fs.writeFile(path.join(tmp.path, ".hidden", "secret.ts"), "secret", "utf-8") - return tmp - } - - test("empty query returns files", async () => { - await using tmp = await setupSearchableRepo() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await init() + describe("search()", () => { + it.instance( + "empty query returns files", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* setupSearchableRepo(test.directory) + yield* init() - const result = await search({ query: "", type: "file" }) + const result = yield* search({ query: "", type: "file" }) expect(result.length).toBeGreaterThan(0) - }, - }) - }) - - test("search works before explicit init", async () => { - await using tmp = await setupSearchableRepo() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await search({ query: "main", type: "file" }) - expect(result.some((f) => f.includes("main"))).toBe(true) - }, - }) - }) - - test("empty query returns dirs sorted with hidden last", async () => { - await using tmp = await setupSearchableRepo() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await init() - - const result = await search({ query: "", type: "directory" }) + }), + { git: true }, + ) + + it.instance( + "search works before explicit init", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* setupSearchableRepo(test.directory) + + const result = yield* search({ query: "main", type: "file" }) + expect(result.some((file) => file.includes("main"))).toBe(true) + }), + { git: true }, + ) + + it.instance( + "empty query returns dirs sorted with hidden last", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* setupSearchableRepo(test.directory) + yield* init() + + const result = yield* search({ query: "", type: "directory" }) expect(result.length).toBeGreaterThan(0) - // Find first hidden dir index - const firstHidden = result.findIndex((d) => d.split("/").some((p) => p.startsWith(".") && p.length > 1)) - const lastVisible = result.findLastIndex((d) => !d.split("/").some((p) => p.startsWith(".") && p.length > 1)) + const firstHidden = result.findIndex((dir) => + dir.split("/").some((part) => part.startsWith(".") && part.length > 1), + ) + const lastVisible = result.findLastIndex( + (dir) => !dir.split("/").some((part) => part.startsWith(".") && part.length > 1), + ) if (firstHidden >= 0 && lastVisible >= 0) { expect(firstHidden).toBeGreaterThan(lastVisible) } - }, - }) - }) - - test("fuzzy matches file names", async () => { - await using tmp = await setupSearchableRepo() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await init() - - const result = await search({ query: "main", type: "file" }) - expect(result.some((f) => f.includes("main"))).toBe(true) - }, - }) - }) - - test("type filter returns only files", async () => { - await using tmp = await setupSearchableRepo() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await init() - - const result = await search({ query: "", type: "file" }) - // Files don't end with / - for (const f of result) { - expect(f.endsWith("/")).toBe(false) + }), + { git: true }, + ) + + it.instance( + "fuzzy matches file names", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* setupSearchableRepo(test.directory) + yield* init() + + const result = yield* search({ query: "main", type: "file" }) + expect(result.some((file) => file.includes("main"))).toBe(true) + }), + { git: true }, + ) + + it.instance( + "type filter returns only files", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* setupSearchableRepo(test.directory) + yield* init() + + const result = yield* search({ query: "", type: "file" }) + for (const file of result) { + expect(file.endsWith("/")).toBe(false) } - }, - }) - }) - - test("type filter returns only directories", async () => { - await using tmp = await setupSearchableRepo() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await init() - - const result = await search({ query: "", type: "directory" }) - // Directories end with / - for (const d of result) { - expect(d.endsWith("/")).toBe(true) + }), + { git: true }, + ) + + it.instance( + "type filter returns only directories", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* setupSearchableRepo(test.directory) + yield* init() + + const result = yield* search({ query: "", type: "directory" }) + for (const dir of result) { + expect(dir.endsWith("/")).toBe(true) } - }, - }) - }) - - test("respects limit", async () => { - await using tmp = await setupSearchableRepo() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await init() - - const result = await search({ query: "", type: "file", limit: 2 }) + }), + { git: true }, + ) + + it.instance( + "respects limit", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* setupSearchableRepo(test.directory) + yield* init() + + const result = yield* search({ query: "", type: "file", limit: 2 }) expect(result.length).toBeLessThanOrEqual(2) - }, - }) - }) - - test("query starting with dot prefers hidden files", async () => { - await using tmp = await setupSearchableRepo() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await init() - - const result = await search({ query: ".hidden", type: "directory" }) + }), + { git: true }, + ) + + it.instance( + "query starting with dot prefers hidden files", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* setupSearchableRepo(test.directory) + yield* init() + + const result = yield* search({ query: ".hidden", type: "directory" }) expect(result.length).toBeGreaterThan(0) expect(result[0]).toContain(".hidden") - }, - }) - }) - - test("search refreshes after init when files change", async () => { - await using tmp = await setupSearchableRepo() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await init() - expect(await search({ query: "fresh", type: "file" })).toEqual([]) - - await fs.writeFile(path.join(tmp.path, "fresh.ts"), "fresh", "utf-8") - - const result = await search({ query: "fresh", type: "file" }) - expect(result).toContain("fresh.ts") - }, - }) - }) + }), + { git: true }, + ) + + it.instance( + "search refreshes after init when files change", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* setupSearchableRepo(test.directory) + yield* init() + expect(yield* search({ query: "fresh", type: "file" })).toEqual([]) + + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "fresh.ts"), "fresh", "utf-8")) + + expect(yield* search({ query: "fresh", type: "file" })).toContain("fresh.ts") + }), + { git: true }, + ) }) describe("read() - diff/patch", () => { - test("returns diff and patch for modified tracked file", async () => { - await using tmp = await tmpdir({ git: true }) - const filepath = path.join(tmp.path, "file.txt") - await fs.writeFile(filepath, "original content\n", "utf-8") - await $`git add .`.cwd(tmp.path).quiet() - await $`git commit -m "add file"`.cwd(tmp.path).quiet() - await fs.writeFile(filepath, "modified content\n", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await read("file.txt") + it.instance( + "returns diff and patch for modified tracked file", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "file.txt") + yield* Effect.promise(() => fs.writeFile(filepath, "original content\n", "utf-8")) + yield* gitAddAll(test.directory) + yield* gitCommit(test.directory, "add file") + yield* Effect.promise(() => fs.writeFile(filepath, "modified content\n", "utf-8")) + + const result = yield* read("file.txt") expect(result.type).toBe("text") expect(result.content).toBe("modified content") expect(result.diff).toBeDefined() @@ -852,108 +783,90 @@ describe("file/index Filesystem patterns", () => { expect(result.diff).toContain("modified content") expect(result.patch).toBeDefined() expect(result.patch!.hunks.length).toBeGreaterThan(0) - }, - }) - }) - - test("returns diff for staged changes", async () => { - await using tmp = await tmpdir({ git: true }) - const filepath = path.join(tmp.path, "staged.txt") - await fs.writeFile(filepath, "before\n", "utf-8") - await $`git add .`.cwd(tmp.path).quiet() - await $`git commit -m "add file"`.cwd(tmp.path).quiet() - await fs.writeFile(filepath, "after\n", "utf-8") - await $`git add .`.cwd(tmp.path).quiet() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await read("staged.txt") + }), + { git: true }, + ) + + it.instance( + "returns diff for staged changes", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "staged.txt") + yield* Effect.promise(() => fs.writeFile(filepath, "before\n", "utf-8")) + yield* gitAddAll(test.directory) + yield* gitCommit(test.directory, "add file") + yield* Effect.promise(() => fs.writeFile(filepath, "after\n", "utf-8")) + yield* gitAddAll(test.directory) + + const result = yield* read("staged.txt") expect(result.diff).toBeDefined() expect(result.patch).toBeDefined() - }, - }) - }) - - test("returns no diff for unmodified file", async () => { - await using tmp = await tmpdir({ git: true }) - const filepath = path.join(tmp.path, "clean.txt") - await fs.writeFile(filepath, "unchanged\n", "utf-8") - await $`git add .`.cwd(tmp.path).quiet() - await $`git commit -m "add file"`.cwd(tmp.path).quiet() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await read("clean.txt") + }), + { git: true }, + ) + + it.instance( + "returns no diff for unmodified file", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "clean.txt") + yield* Effect.promise(() => fs.writeFile(filepath, "unchanged\n", "utf-8")) + yield* gitAddAll(test.directory) + yield* gitCommit(test.directory, "add file") + + const result = yield* read("clean.txt") expect(result.type).toBe("text") expect(result.content).toBe("unchanged") expect(result.diff).toBeUndefined() expect(result.patch).toBeUndefined() - }, - }) - }) + }), + { git: true }, + ) }) - // kilocode_change - skip on windows: address windows ci failures #9496 - describe.skipIf(process.platform === "win32")("InstanceState isolation", () => { - test("two directories get independent file caches", async () => { - await using one = await tmpdir({ git: true }) - await using two = await tmpdir({ git: true }) - await fs.writeFile(path.join(one.path, "a.ts"), "one", "utf-8") - await fs.writeFile(path.join(two.path, "b.ts"), "two", "utf-8") - - await WithInstance.provide({ - directory: one.path, - fn: async () => { - await init() - const results = await search({ query: "a.ts", type: "file" }) - expect(results).toContain("a.ts") - const results2 = await search({ query: "b.ts", type: "file" }) - expect(results2).not.toContain("b.ts") - }, - }) - - await WithInstance.provide({ - directory: two.path, - fn: async () => { - await init() - const results = await search({ query: "b.ts", type: "file" }) - expect(results).toContain("b.ts") - const results2 = await search({ query: "a.ts", type: "file" }) - expect(results2).not.toContain("a.ts") - }, - }) - }) - - test("disposal gives fresh state on next access", async () => { - await using tmp = await tmpdir({ git: true }) - await fs.writeFile(path.join(tmp.path, "before.ts"), "before", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await init() - const results = await search({ query: "before", type: "file" }) - expect(results).toContain("before.ts") - }, - }) - - await disposeAllInstances() - - await fs.writeFile(path.join(tmp.path, "after.ts"), "after", "utf-8") - await fs.rm(path.join(tmp.path, "before.ts")) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await init() - const results = await search({ query: "after", type: "file" }) - expect(results).toContain("after.ts") - const stale = await search({ query: "before", type: "file" }) - expect(stale).not.toContain("before.ts") - }, - }) - }) + describe("InstanceState isolation", () => { + it.instance( + "two directories get independent file caches", + () => + Effect.gen(function* () { + const one = yield* TestInstance + yield* Effect.promise(() => fs.writeFile(path.join(one.directory, "a.ts"), "one", "utf-8")) + yield* init() + expect(yield* search({ query: "a.ts", type: "file" })).toContain("a.ts") + expect(yield* search({ query: "b.ts", type: "file" })).not.toContain("b.ts") + + yield* Effect.gen(function* () { + const two = yield* TestInstance + yield* Effect.promise(() => fs.writeFile(path.join(two.directory, "b.ts"), "two", "utf-8")) + yield* init() + expect(yield* search({ query: "b.ts", type: "file" })).toContain("b.ts") + expect(yield* search({ query: "a.ts", type: "file" })).not.toContain("a.ts") + }).pipe(withTmpdirInstance({ git: true })) + }), + { git: true }, + ) + + it.instance( + "disposal gives fresh state on next access", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "before.ts"), "before", "utf-8")) + yield* init() + expect(yield* search({ query: "before", type: "file" })).toContain("before.ts") + + yield* Effect.promise(() => disposeAllInstances()) + + yield* Effect.promise(() => fs.writeFile(path.join(test.directory, "after.ts"), "after", "utf-8")) + yield* Effect.promise(() => fs.rm(path.join(test.directory, "before.ts"))) + + yield* init() + expect(yield* search({ query: "after", type: "file" })).toContain("after.ts") + expect(yield* search({ query: "before", type: "file" })).not.toContain("before.ts") + }), + { git: true }, + ) }) }) diff --git a/packages/opencode/test/file/path-traversal.test.ts b/packages/opencode/test/file/path-traversal.test.ts index 5b59929ea58..336f214d1ae 100644 --- a/packages/opencode/test/file/path-traversal.test.ts +++ b/packages/opencode/test/file/path-traversal.test.ts @@ -1,42 +1,55 @@ -import { test, expect, describe } from "bun:test" -import { Effect } from "effect" +import { expect, describe } from "bun:test" +import { Cause, Effect, Exit } from "effect" import path from "path" import fs from "fs/promises" import { Filesystem } from "@/util/filesystem" import { File } from "../../src/file" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" +import { InstanceState } from "../../src/effect/instance-state" import { containsPath } from "../../src/project/instance-context" -import { provideInstance, tmpdir } from "../fixture/fixture" - -const run = (eff: Effect.Effect) => - Effect.runPromise(provideInstance(Instance.directory)(eff.pipe(Effect.provide(File.defaultLayer)))) -const read = (file: string) => run(File.Service.use((svc) => svc.read(file))) -const list = (dir?: string) => run(File.Service.use((svc) => svc.list(dir))) - -describe("Filesystem.contains", () => { - test("allows paths within project", () => { - expect(Filesystem.contains("/project", "/project/src")).toBe(true) - expect(Filesystem.contains("/project", "/project/src/file.ts")).toBe(true) - expect(Filesystem.contains("/project", "/project")).toBe(true) - }) - - test("blocks ../ traversal", () => { - expect(Filesystem.contains("/project", "/project/../etc")).toBe(false) - expect(Filesystem.contains("/project", "/project/src/../../etc")).toBe(false) - expect(Filesystem.contains("/project", "/etc/passwd")).toBe(false) - }) - - test("blocks absolute paths outside project", () => { - expect(Filesystem.contains("/project", "/etc/passwd")).toBe(false) - expect(Filesystem.contains("/project", "/tmp/file")).toBe(false) - expect(Filesystem.contains("/home/user/project", "/home/user/other")).toBe(false) +import { TestInstance } from "../fixture/fixture" +import { testEffect } from "../lib/effect" + +const it = testEffect(File.defaultLayer) +const read = (file: string) => File.Service.use((svc) => svc.read(file)) +const list = (dir?: string) => File.Service.use((svc) => svc.list(dir)) +const expectAccessDenied = (effect: Effect.Effect) => + Effect.gen(function* () { + const exit = yield* effect.pipe(Effect.exit) + if (Exit.isSuccess(exit)) throw new Error("expected access denied") + expect(Cause.squash(exit.cause)).toHaveProperty("message", "Access denied: path escapes project directory") }) - test("handles prefix collision edge cases", () => { - expect(Filesystem.contains("/project", "/project-other/file")).toBe(false) - expect(Filesystem.contains("/project", "/projectfile")).toBe(false) - }) +describe("Filesystem.contains", () => { + it.effect("allows paths within project", () => + Effect.sync(() => { + expect(Filesystem.contains("/project", "/project/src")).toBe(true) + expect(Filesystem.contains("/project", "/project/src/file.ts")).toBe(true) + expect(Filesystem.contains("/project", "/project")).toBe(true) + }), + ) + + it.effect("blocks ../ traversal", () => + Effect.sync(() => { + expect(Filesystem.contains("/project", "/project/../etc")).toBe(false) + expect(Filesystem.contains("/project", "/project/src/../../etc")).toBe(false) + expect(Filesystem.contains("/project", "/etc/passwd")).toBe(false) + }), + ) + + it.effect("blocks absolute paths outside project", () => + Effect.sync(() => { + expect(Filesystem.contains("/project", "/etc/passwd")).toBe(false) + expect(Filesystem.contains("/project", "/tmp/file")).toBe(false) + expect(Filesystem.contains("/home/user/project", "/home/user/other")).toBe(false) + }), + ) + + it.effect("handles prefix collision edge cases", () => + Effect.sync(() => { + expect(Filesystem.contains("/project", "/project-other/file")).toBe(false) + expect(Filesystem.contains("/project", "/projectfile")).toBe(false) + }), + ) }) /* @@ -49,158 +62,124 @@ describe("Filesystem.contains", () => { * This is a SEPARATE code path from ReadTool, which has its own checks. */ describe("File.read path traversal protection", () => { - test("rejects ../ traversal attempting to read /etc/passwd", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "allowed.txt"), "allowed content") - }, - }) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await expect(read("../../../etc/passwd")).rejects.toThrow("Access denied: path escapes project directory") - }, - }) - }) - - test("rejects deeply nested traversal", async () => { - await using tmp = await tmpdir() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await expect(read("src/nested/../../../../../../../etc/passwd")).rejects.toThrow( - "Access denied: path escapes project directory", - ) - }, - }) - }) - - test("allows valid paths within project", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "valid.txt"), "valid content") - }, - }) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await read("valid.txt") - expect(result.content).toBe("valid content") - }, - }) - }) + it.instance("rejects ../ traversal attempting to read /etc/passwd", () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => Bun.write(path.join(test.directory, "allowed.txt"), "allowed content")) + yield* expectAccessDenied(read("../../../etc/passwd")) + }), + ) + + it.instance("rejects deeply nested traversal", () => + Effect.gen(function* () { + yield* expectAccessDenied(read("src/nested/../../../../../../../etc/passwd")) + }), + ) + + it.instance("allows valid paths within project", () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => Bun.write(path.join(test.directory, "valid.txt"), "valid content")) + + const result = yield* read("valid.txt") + expect(result.content).toBe("valid content") + }), + ) }) describe("File.list path traversal protection", () => { - test("rejects ../ traversal attempting to list /etc", async () => { - await using tmp = await tmpdir() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await expect(list("../../../etc")).rejects.toThrow("Access denied: path escapes project directory") - }, - }) - }) - - test("allows valid subdirectory listing", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "subdir", "file.txt"), "content") - }, - }) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await list("subdir") - expect(Array.isArray(result)).toBe(true) - }, - }) - }) + it.instance("rejects ../ traversal attempting to list /etc", () => + Effect.gen(function* () { + yield* expectAccessDenied(list("../../../etc")) + }), + ) + + it.instance("allows valid subdirectory listing", () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => Bun.write(path.join(test.directory, "subdir", "file.txt"), "content")) + + const result = yield* list("subdir") + expect(Array.isArray(result)).toBe(true) + }), + ) }) describe("containsPath", () => { - test("returns true for path inside directory", async () => { - await using tmp = await tmpdir({ git: true }) - - await WithInstance.provide({ - directory: tmp.path, - fn: () => { - expect(containsPath(path.join(tmp.path, "foo.txt"), Instance.current)).toBe(true) - expect(containsPath(path.join(tmp.path, "src", "file.ts"), Instance.current)).toBe(true) - }, - }) - }) - - test("returns true for path inside worktree but outside directory (monorepo subdirectory scenario)", async () => { - await using tmp = await tmpdir({ git: true }) - const subdir = path.join(tmp.path, "packages", "lib") - await fs.mkdir(subdir, { recursive: true }) + it.instance( + "returns true for path inside directory", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const ctx = yield* InstanceState.context + expect(containsPath(path.join(test.directory, "foo.txt"), ctx)).toBe(true) + expect(containsPath(path.join(test.directory, "src", "file.ts"), ctx)).toBe(true) + }), + { git: true }, + ) + + it.instance( + "returns true for path inside worktree but outside directory (monorepo subdirectory scenario)", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const subdir = path.join(test.directory, "packages", "lib") + yield* Effect.promise(() => fs.mkdir(subdir, { recursive: true })) + const ctx = { ...(yield* InstanceState.context), directory: subdir } - await WithInstance.provide({ - directory: subdir, - fn: () => { // .opencode at worktree root, but we're running from packages/lib - expect(containsPath(path.join(tmp.path, ".opencode", "state"), Instance.current)).toBe(true) + expect(containsPath(path.join(test.directory, ".opencode", "state"), ctx)).toBe(true) // sibling package should also be accessible - expect(containsPath(path.join(tmp.path, "packages", "other", "file.ts"), Instance.current)).toBe(true) + expect(containsPath(path.join(test.directory, "packages", "other", "file.ts"), ctx)).toBe(true) // worktree root itself - expect(containsPath(tmp.path, Instance.current)).toBe(true) - }, - }) - }) - - test("returns false for path outside both directory and worktree", async () => { - await using tmp = await tmpdir({ git: true }) - - await WithInstance.provide({ - directory: tmp.path, - fn: () => { - expect(containsPath("/etc/passwd", Instance.current)).toBe(false) - expect(containsPath("/tmp/other-project", Instance.current)).toBe(false) - }, - }) - }) - - test("returns false for path with .. escaping worktree", async () => { - await using tmp = await tmpdir({ git: true }) - - await WithInstance.provide({ - directory: tmp.path, - fn: () => { - expect(containsPath(path.join(tmp.path, "..", "escape.txt"), Instance.current)).toBe(false) - }, - }) - }) - - test("handles directory === worktree (running from repo root)", async () => { - await using tmp = await tmpdir({ git: true }) - - await WithInstance.provide({ - directory: tmp.path, - fn: () => { - expect(Instance.directory).toBe(Instance.worktree) - expect(containsPath(path.join(tmp.path, "file.txt"), Instance.current)).toBe(true) - expect(containsPath("/etc/passwd", Instance.current)).toBe(false) - }, - }) - }) - - test("non-git project does not allow arbitrary paths via worktree='/'", async () => { - await using tmp = await tmpdir() // no git: true - - await WithInstance.provide({ - directory: tmp.path, - fn: () => { - // worktree is "/" for non-git projects, but containsPath should NOT allow all paths - expect(containsPath(path.join(tmp.path, "file.txt"), Instance.current)).toBe(true) - expect(containsPath("/etc/passwd", Instance.current)).toBe(false) - expect(containsPath("/tmp/other", Instance.current)).toBe(false) - }, - }) - }) + expect(containsPath(test.directory, ctx)).toBe(true) + }), + { git: true }, + ) + + it.instance( + "returns false for path outside both directory and worktree", + () => + Effect.gen(function* () { + const ctx = yield* InstanceState.context + expect(containsPath("/etc/passwd", ctx)).toBe(false) + expect(containsPath("/tmp/other-project", ctx)).toBe(false) + }), + { git: true }, + ) + + it.instance( + "returns false for path with .. escaping worktree", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const ctx = yield* InstanceState.context + expect(containsPath(path.join(test.directory, "..", "escape.txt"), ctx)).toBe(false) + }), + { git: true }, + ) + + it.instance( + "handles directory === worktree (running from repo root)", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const ctx = yield* InstanceState.context + expect(ctx.directory).toBe(ctx.worktree) + expect(containsPath(path.join(test.directory, "file.txt"), ctx)).toBe(true) + expect(containsPath("/etc/passwd", ctx)).toBe(false) + }), + { git: true }, + ) + + it.instance("non-git project does not allow arbitrary paths via worktree='/'", () => + Effect.gen(function* () { + const test = yield* TestInstance + const ctx = yield* InstanceState.context + // worktree is "/" for non-git projects, but containsPath should NOT allow all paths + expect(containsPath(path.join(test.directory, "file.txt"), ctx)).toBe(true) + expect(containsPath("/etc/passwd", ctx)).toBe(false) + expect(containsPath("/tmp/other", ctx)).toBe(false) + }), + ) }) diff --git a/packages/opencode/test/file/ripgrep.test.ts b/packages/opencode/test/file/ripgrep.test.ts index 0c5788c7362..d71ce205ea7 100644 --- a/packages/opencode/test/file/ripgrep.test.ts +++ b/packages/opencode/test/file/ripgrep.test.ts @@ -1,231 +1,220 @@ -import { describe, expect, test } from "bun:test" +import { describe, expect } from "bun:test" import { Effect } from "effect" import * as Stream from "effect/Stream" import fs from "fs/promises" +import os from "os" import path from "path" -import { tmpdir } from "../fixture/fixture" import { Ripgrep } from "../../src/file/ripgrep" - -const run = (effect: Effect.Effect) => - effect.pipe(Effect.provide(Ripgrep.defaultLayer), Effect.runPromise) - -// kilocode_change - skip on windows: address windows ci failures #9496 -describe.skipIf(process.platform === "win32")("file.ripgrep", () => { - test("defaults to include hidden", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "visible.txt"), "hello") - await fs.mkdir(path.join(dir, ".opencode"), { recursive: true }) - await Bun.write(path.join(dir, ".opencode", "thing.json"), "{}") - }, - }) - - const files = await run( - Ripgrep.Service.use((rg) => - rg.files({ cwd: tmp.path }).pipe( - Stream.runCollect, - Effect.map((c) => [...c]), - ), - ), - ) - expect(files.includes("visible.txt")).toBe(true) - expect(files.includes(path.join(".opencode", "thing.json"))).toBe(true) - }) - - test("hidden false excludes hidden", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "visible.txt"), "hello") - await fs.mkdir(path.join(dir, ".opencode"), { recursive: true }) - await Bun.write(path.join(dir, ".opencode", "thing.json"), "{}") - }, - }) - - const files = await run( - Ripgrep.Service.use((rg) => - rg.files({ cwd: tmp.path, hidden: false }).pipe( - Stream.runCollect, - Effect.map((c) => [...c]), - ), - ), - ) - expect(files.includes("visible.txt")).toBe(true) - expect(files.includes(path.join(".opencode", "thing.json"))).toBe(false) - }) - - // kilocode_change start - .kilo directory should also be skipped in tree() - test("tree skips .kilo directory files", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "src", "main.ts"), "export {}") - await fs.mkdir(path.join(dir, ".kilo"), { recursive: true }) - await Bun.write(path.join(dir, ".kilo", "config.json"), "{}") - }, - }) - - const result = await run(Ripgrep.Service.use((rg) => rg.tree({ cwd: tmp.path }))) - expect(result).not.toContain(".kilo") - expect(result).toContain("src") - }) - // kilocode_change end - - test("search returns empty when nothing matches", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "match.ts"), "const value = 'other'\n") - }, - }) - - const result = await run(Ripgrep.Service.use((rg) => rg.search({ cwd: tmp.path, pattern: "needle" }))) - expect(result.partial).toBe(false) - expect(result.items).toEqual([]) - }) - - test("search returns match metadata with normalized path", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await fs.mkdir(path.join(dir, "src"), { recursive: true }) - await Bun.write(path.join(dir, "src", "match.ts"), "const needle = 1\n") - }, - }) - - const result = await run(Ripgrep.Service.use((rg) => rg.search({ cwd: tmp.path, pattern: "needle" }))) - expect(result.partial).toBe(false) - expect(result.items).toHaveLength(1) - expect(result.items[0]?.path.text).toBe(path.join("src", "match.ts")) - expect(result.items[0]?.line_number).toBe(1) - expect(result.items[0]?.lines.text).toContain("needle") - }) - - test("search returns matched rows with glob filter", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "match.ts"), "const value = 'needle'\n") - await Bun.write(path.join(dir, "skip.txt"), "const value = 'other'\n") - }, - }) - - const result = await run( - Ripgrep.Service.use((rg) => rg.search({ cwd: tmp.path, pattern: "needle", glob: ["*.ts"] })), - ) - expect(result.partial).toBe(false) - expect(result.items).toHaveLength(1) - expect(result.items[0]?.path.text).toContain("match.ts") - expect(result.items[0]?.lines.text).toContain("needle") - }) - - test("search supports explicit file targets", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "match.ts"), "const value = 'needle'\n") - await Bun.write(path.join(dir, "skip.ts"), "const value = 'needle'\n") - }, - }) - - const file = path.join(tmp.path, "match.ts") - const result = await run(Ripgrep.Service.use((rg) => rg.search({ cwd: tmp.path, pattern: "needle", file: [file] }))) - expect(result.partial).toBe(false) - expect(result.items).toHaveLength(1) - expect(result.items[0]?.path.text).toBe(file) - }) - - test("files returns empty when glob matches no files", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await fs.mkdir(path.join(dir, "packages", "console"), { recursive: true }) - await Bun.write(path.join(dir, "packages", "console", "package.json"), "{}") - }, - }) - - const files = await run( - Ripgrep.Service.use((rg) => - rg.files({ cwd: tmp.path, glob: ["packages/*"] }).pipe( - Stream.runCollect, - Effect.map((c) => [...c]), - ), - ), - ) - expect(files).toEqual([]) - }) - - test("files returns stream of filenames", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "a.txt"), "hello") - await Bun.write(path.join(dir, "b.txt"), "world") - }, - }) - - const files = await run( - Ripgrep.Service.use((rg) => - rg.files({ cwd: tmp.path }).pipe( - Stream.runCollect, - Effect.map((c) => [...c].sort()), - ), - ), - ) - expect(files).toEqual(["a.txt", "b.txt"]) - }) - - test("files respects glob filter", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "keep.ts"), "yes") - await Bun.write(path.join(dir, "skip.txt"), "no") - }, - }) - - const files = await run( - Ripgrep.Service.use((rg) => - rg.files({ cwd: tmp.path, glob: ["*.ts"] }).pipe( - Stream.runCollect, - Effect.map((c) => [...c]), - ), - ), - ) - expect(files).toEqual(["keep.ts"]) - }) - - test("files dies on nonexistent directory", async () => { - const exit = await Ripgrep.Service.use((rg) => - rg.files({ cwd: "/tmp/nonexistent-dir-12345" }).pipe(Stream.runCollect), - ).pipe(Effect.provide(Ripgrep.defaultLayer), Effect.runPromiseExit) - expect(exit._tag).toBe("Failure") - }) - - test("ignores RIPGREP_CONFIG_PATH in direct mode", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "match.ts"), "const needle = 1\n") - }, - }) - - const prev = process.env["RIPGREP_CONFIG_PATH"] - process.env["RIPGREP_CONFIG_PATH"] = path.join(tmp.path, "missing-ripgreprc") - try { - const result = await run(Ripgrep.Service.use((rg) => rg.search({ cwd: tmp.path, pattern: "needle" }))) +import { testEffect } from "../lib/effect" + +const it = testEffect(Ripgrep.defaultLayer) + +const tmpdir = (init?: (dir: string) => Effect.Effect) => + Effect.acquireRelease( + Effect.promise(async () => fs.realpath(await fs.mkdtemp(path.join(os.tmpdir(), "opencode-test-")))), + (dir) => + Effect.promise(() => + fs.rm(dir, { + recursive: true, + force: true, + maxRetries: 5, + retryDelay: 100, + }), + ).pipe(Effect.ignore), + ).pipe(Effect.tap((dir) => init?.(dir) ?? Effect.void)) + +const write = (file: string, data: string) => Effect.promise(() => Bun.write(file, data)) +const mkdir = (dir: string) => Effect.promise(() => fs.mkdir(dir, { recursive: true })) +const collectFiles = (input: Ripgrep.FilesInput) => + Ripgrep.Service.use((rg) => + rg.files(input).pipe( + Stream.runCollect, + Effect.map((c) => [...c]), + ), + ) + +const withRipgrepConfig = (value: string, effect: Effect.Effect) => + Effect.acquireUseRelease( + Effect.sync(() => { + const prev = process.env["RIPGREP_CONFIG_PATH"] + process.env["RIPGREP_CONFIG_PATH"] = value + return prev + }), + () => effect, + (prev) => + Effect.sync(() => { + if (prev === undefined) delete process.env["RIPGREP_CONFIG_PATH"] + else process.env["RIPGREP_CONFIG_PATH"] = prev + }), + ) + +describe("file.ripgrep", () => { + it.live("defaults to include hidden", () => + Effect.gen(function* () { + const dir = yield* tmpdir((dir) => + Effect.gen(function* () { + yield* write(path.join(dir, "visible.txt"), "hello") + yield* mkdir(path.join(dir, ".opencode")) + yield* write(path.join(dir, ".opencode", "thing.json"), "{}") + }), + ) + + const files = yield* collectFiles({ cwd: dir }) + expect(files.includes("visible.txt")).toBe(true) + expect(files.includes(path.join(".opencode", "thing.json"))).toBe(true) + }), + ) + + it.live("hidden false excludes hidden", () => + Effect.gen(function* () { + const dir = yield* tmpdir((dir) => + Effect.gen(function* () { + yield* write(path.join(dir, "visible.txt"), "hello") + yield* mkdir(path.join(dir, ".opencode")) + yield* write(path.join(dir, ".opencode", "thing.json"), "{}") + }), + ) + + const files = yield* collectFiles({ cwd: dir, hidden: false }) + expect(files.includes("visible.txt")).toBe(true) + expect(files.includes(path.join(".opencode", "thing.json"))).toBe(false) + }), + ) + + it.live("search returns empty when nothing matches", () => + Effect.gen(function* () { + const dir = yield* tmpdir((dir) => write(path.join(dir, "match.ts"), "const value = 'other'\n")) + + const result = yield* Ripgrep.Service.use((rg) => rg.search({ cwd: dir, pattern: "needle" })) + expect(result.partial).toBe(false) + expect(result.items).toEqual([]) + }), + ) + + it.live("search returns match metadata with normalized path", () => + Effect.gen(function* () { + const dir = yield* tmpdir((dir) => + Effect.gen(function* () { + yield* mkdir(path.join(dir, "src")) + yield* write(path.join(dir, "src", "match.ts"), "const needle = 1\n") + }), + ) + + const result = yield* Ripgrep.Service.use((rg) => rg.search({ cwd: dir, pattern: "needle" })) + expect(result.partial).toBe(false) + expect(result.items).toHaveLength(1) + expect(result.items[0]?.path.text).toBe(path.join("src", "match.ts")) + expect(result.items[0]?.line_number).toBe(1) + expect(result.items[0]?.lines.text).toContain("needle") + }), + ) + + it.live("search returns matched rows with glob filter", () => + Effect.gen(function* () { + const dir = yield* tmpdir((dir) => + Effect.gen(function* () { + yield* write(path.join(dir, "match.ts"), "const value = 'needle'\n") + yield* write(path.join(dir, "skip.txt"), "const value = 'other'\n") + }), + ) + + const result = yield* Ripgrep.Service.use((rg) => rg.search({ cwd: dir, pattern: "needle", glob: ["*.ts"] })) + expect(result.partial).toBe(false) + expect(result.items).toHaveLength(1) + expect(result.items[0]?.path.text).toContain("match.ts") + expect(result.items[0]?.lines.text).toContain("needle") + }), + ) + + it.live("search supports explicit file targets", () => + Effect.gen(function* () { + const dir = yield* tmpdir((dir) => + Effect.gen(function* () { + yield* write(path.join(dir, "match.ts"), "const value = 'needle'\n") + yield* write(path.join(dir, "skip.ts"), "const value = 'needle'\n") + }), + ) + + const file = path.join(dir, "match.ts") + const result = yield* Ripgrep.Service.use((rg) => rg.search({ cwd: dir, pattern: "needle", file: [file] })) + expect(result.partial).toBe(false) expect(result.items).toHaveLength(1) - } finally { - if (prev === undefined) delete process.env["RIPGREP_CONFIG_PATH"] - else process.env["RIPGREP_CONFIG_PATH"] = prev - } - }) - - test("ignores RIPGREP_CONFIG_PATH in worker mode", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "match.ts"), "const needle = 1\n") - }, - }) - - const prev = process.env["RIPGREP_CONFIG_PATH"] - process.env["RIPGREP_CONFIG_PATH"] = path.join(tmp.path, "missing-ripgreprc") - try { - const result = await run(Ripgrep.Service.use((rg) => rg.search({ cwd: tmp.path, pattern: "needle" }))) + expect(result.items[0]?.path.text).toBe(file) + }), + ) + + it.live("files returns empty when glob matches no files", () => + Effect.gen(function* () { + const dir = yield* tmpdir((dir) => + Effect.gen(function* () { + yield* mkdir(path.join(dir, "packages", "console")) + yield* write(path.join(dir, "packages", "console", "package.json"), "{}") + }), + ) + + const files = yield* collectFiles({ cwd: dir, glob: ["packages/*"] }) + expect(files).toEqual([]) + }), + ) + + it.live("files returns stream of filenames", () => + Effect.gen(function* () { + const dir = yield* tmpdir((dir) => + Effect.gen(function* () { + yield* write(path.join(dir, "a.txt"), "hello") + yield* write(path.join(dir, "b.txt"), "world") + }), + ) + + const files = yield* collectFiles({ cwd: dir }).pipe(Effect.map((files) => files.sort())) + expect(files).toEqual(["a.txt", "b.txt"]) + }), + ) + + it.live("files respects glob filter", () => + Effect.gen(function* () { + const dir = yield* tmpdir((dir) => + Effect.gen(function* () { + yield* write(path.join(dir, "keep.ts"), "yes") + yield* write(path.join(dir, "skip.txt"), "no") + }), + ) + + const files = yield* collectFiles({ cwd: dir, glob: ["*.ts"] }) + expect(files).toEqual(["keep.ts"]) + }), + ) + + it.live("files dies on nonexistent directory", () => + Effect.gen(function* () { + const exit = yield* Ripgrep.Service.use((rg) => + rg.files({ cwd: "/tmp/nonexistent-dir-12345" }).pipe(Stream.runCollect), + ).pipe(Effect.exit) + expect(exit._tag).toBe("Failure") + }), + ) + + it.live("ignores RIPGREP_CONFIG_PATH in direct mode", () => + Effect.gen(function* () { + const dir = yield* tmpdir((dir) => write(path.join(dir, "match.ts"), "const needle = 1\n")) + + const result = yield* withRipgrepConfig( + path.join(dir, "missing-ripgreprc"), + Ripgrep.Service.use((rg) => rg.search({ cwd: dir, pattern: "needle" })), + ) + expect(result.items).toHaveLength(1) + }), + ) + + it.live("ignores RIPGREP_CONFIG_PATH in worker mode", () => + Effect.gen(function* () { + const dir = yield* tmpdir((dir) => write(path.join(dir, "match.ts"), "const needle = 1\n")) + + const result = yield* withRipgrepConfig( + path.join(dir, "missing-ripgreprc"), + Ripgrep.Service.use((rg) => rg.search({ cwd: dir, pattern: "needle" })), + ) expect(result.items).toHaveLength(1) - } finally { - if (prev === undefined) delete process.env["RIPGREP_CONFIG_PATH"] - else process.env["RIPGREP_CONFIG_PATH"] = prev - } - }) + }), + ) }) diff --git a/packages/opencode/test/file/watcher.test.ts b/packages/opencode/test/file/watcher.test.ts index 95fac0d1be5..f5495218b43 100644 --- a/packages/opencode/test/file/watcher.test.ts +++ b/packages/opencode/test/file/watcher.test.ts @@ -1,15 +1,13 @@ -import { $ } from "bun" -import { afterEach, describe, expect, test } from "bun:test" -import fs from "fs/promises" +import { describe, expect } from "bun:test" import path from "path" -import { ConfigProvider, Deferred, Effect, Layer, ManagedRuntime, Option } from "effect" -import { disposeAllInstances, tmpdir } from "../fixture/fixture" -import { Bus } from "../../src/bus" +import { AppFileSystem } from "@opencode-ai/core/filesystem" +import { ConfigProvider, Deferred, Effect, Layer, Option } from "effect" +import { TestInstance, provideInstance } from "../fixture/fixture" +import { testEffect } from "../lib/effect" +import { GlobalBus, type GlobalEvent } from "../../src/bus/global" import { Config } from "@/config/config" import { FileWatcher } from "../../src/file/watcher" import { Git } from "../../src/git" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" // Native @parcel/watcher bindings aren't reliably available in CI (missing on Linux, flaky on Windows) const describeWatcher = FileWatcher.hasNativeBinding() && !process.env.CI ? describe : describe.skip @@ -25,43 +23,43 @@ const watcherConfigLayer = ConfigProvider.layer( }), ) +const watcherLayer = FileWatcher.layer.pipe( + Layer.provide(Config.defaultLayer), + Layer.provide(Git.defaultLayer), + Layer.provide(watcherConfigLayer), +) + +const it = testEffect(Layer.mergeAll(AppFileSystem.defaultLayer, Git.defaultLayer)) + type WatcherEvent = { file: string; event: "add" | "change" | "unlink" } /** Run `body` with a live FileWatcher service. */ -function withWatcher(directory: string, body: Effect.Effect) { - return WithInstance.provide({ - directory, - fn: async () => { - const layer: Layer.Layer = FileWatcher.layer.pipe( - Layer.provide(Config.defaultLayer), - Layer.provide(Git.defaultLayer), - Layer.provide(watcherConfigLayer), - ) - const rt = ManagedRuntime.make(layer) - try { - await rt.runPromise(FileWatcher.Service.use((s) => s.init())) - await Effect.runPromise(ready(directory)) - await Effect.runPromise(body) - } finally { - await rt.dispose() - } - }, - }) +function withWatcher(directory: string, body: Effect.Effect) { + return Effect.gen(function* () { + const watcher = yield* FileWatcher.Service + yield* watcher.init() + yield* ready(directory) + return yield* body + }).pipe(Effect.provide(watcherLayer), provideInstance(directory), Effect.scoped) } function listen(directory: string, check: (evt: WatcherEvent) => boolean, hit: (evt: WatcherEvent) => void) { let done = false - const unsub = Bus.subscribe(FileWatcher.Event.Updated, (evt) => { + const on = (evt: GlobalEvent) => { if (done) return - if (!check(evt.properties)) return - hit(evt.properties) - }) + if (evt.directory !== directory) return + if (evt.payload.type !== FileWatcher.Event.Updated.type) return + if (!check(evt.payload.properties)) return + hit(evt.payload.properties) + } + + GlobalBus.on("event", on) return () => { if (done) return done = true - unsub() + GlobalBus.off("event", on) } } @@ -72,7 +70,7 @@ function wait(directory: string, check: (evt: WatcherEvent) => boolean) { let off = () => {} off = listen(directory, check, (evt) => { off() - Deferred.doneUnsafe(deferred, Effect.succeed(evt)) + Effect.runFork(Deferred.succeed(deferred, evt)) }) return off }) @@ -86,7 +84,12 @@ function nextUpdate(directory: string, check: (evt: WatcherEvent) => boolean, ({ deferred }) => Effect.gen(function* () { yield* trigger - return yield* Deferred.await(deferred).pipe(Effect.timeout("5 seconds")) + return yield* Deferred.await(deferred).pipe( + Effect.timeoutOrElse({ + duration: "5 seconds", + orElse: () => Effect.fail(new Error("timed out waiting for file watcher update")), + }), + ) }), ({ cleanup }) => Effect.sync(cleanup), ) @@ -104,7 +107,11 @@ function noUpdate( ({ deferred }) => Effect.gen(function* () { yield* trigger - expect(yield* Deferred.await(deferred).pipe(Effect.timeoutOption(`${ms} millis`))).toEqual(Option.none()) + const result = yield* Deferred.await(deferred).pipe( + Effect.map((evt) => Option.some(evt)), + Effect.timeoutOrElse({ duration: `${ms} millis`, orElse: () => Effect.succeed(Option.none()) }), + ) + expect(result).toEqual(Option.none()) }), ({ cleanup }) => Effect.sync(cleanup), ) @@ -115,29 +122,25 @@ function ready(directory: string) { const head = path.join(directory, ".git", "HEAD") return Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const git = yield* Git.Service + yield* nextUpdate( directory, (evt) => evt.file === file && evt.event === "add", - Effect.promise(() => fs.writeFile(file, "ready")), - ).pipe(Effect.ensuring(Effect.promise(() => fs.rm(file, { force: true }).catch(() => undefined))), Effect.asVoid) + fs.writeFileString(file, "ready"), + ).pipe(Effect.ensuring(fs.remove(file, { force: true }).pipe(Effect.ignore)), Effect.asVoid) - const git = yield* Effect.promise(() => - fs - .stat(head) - .then(() => true) - .catch(() => false), - ) - if (!git) return + if (!(yield* fs.existsSafe(head))) return const branch = `watch-${Math.random().toString(36).slice(2)}` - const hash = yield* Effect.promise(() => $`git rev-parse HEAD`.cwd(directory).quiet().text()) + const hash = (yield* git.run(["rev-parse", "HEAD"], { cwd: directory })).text() yield* nextUpdate( directory, (evt) => evt.file === head && evt.event !== "unlink", - Effect.promise(async () => { - await fs.writeFile(path.join(directory, ".git", "refs", "heads", branch), hash.trim() + "\n") - await fs.writeFile(head, `ref: refs/heads/${branch}\n`) - }), + fs + .writeFileString(path.join(directory, ".git", "refs", "heads", branch), hash.trim() + "\n") + .pipe(Effect.andThen(fs.writeFileString(head, `ref: refs/heads/${branch}\n`))), ).pipe(Effect.asVoid) }) } @@ -147,104 +150,114 @@ function ready(directory: string) { // --------------------------------------------------------------------------- describeWatcher("FileWatcher", () => { - afterEach(async () => { - await disposeAllInstances() - }) + it.instance( + "publishes root create, update, and delete events", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const fs = yield* AppFileSystem.Service + const file = path.join(test.directory, "watch.txt") + const cases = [ + { event: "add" as const, trigger: fs.writeFileString(file, "a") }, + { event: "change" as const, trigger: fs.writeFileString(file, "b") }, + { event: "unlink" as const, trigger: fs.remove(file) }, + ] - test("publishes root create, update, and delete events", async () => { - await using tmp = await tmpdir({ git: true }) - const file = path.join(tmp.path, "watch.txt") - const dir = tmp.path - const cases = [ - { event: "add" as const, trigger: Effect.promise(() => fs.writeFile(file, "a")) }, - { event: "change" as const, trigger: Effect.promise(() => fs.writeFile(file, "b")) }, - { event: "unlink" as const, trigger: Effect.promise(() => fs.unlink(file)) }, - ] - - await withWatcher( - dir, - Effect.forEach(cases, ({ event, trigger }) => - nextUpdate(dir, (evt) => evt.file === file && evt.event === event, trigger).pipe( - Effect.tap((evt) => Effect.sync(() => expect(evt).toEqual({ file, event }))), + yield* withWatcher( + test.directory, + Effect.forEach(cases, ({ event, trigger }) => + nextUpdate(test.directory, (evt) => evt.file === file && evt.event === event, trigger).pipe( + Effect.tap((evt) => Effect.sync(() => expect(evt).toEqual({ file, event }))), + ), + ), + ) + }), + { git: true }, + ) + + it.instance("watches non-git roots", () => + Effect.gen(function* () { + const test = yield* TestInstance + const fs = yield* AppFileSystem.Service + const file = path.join(test.directory, "plain.txt") + + yield* withWatcher( + test.directory, + nextUpdate(test.directory, (e) => e.file === file && e.event === "add", fs.writeFileString(file, "plain")).pipe( + Effect.tap((evt) => Effect.sync(() => expect(evt).toEqual({ file, event: "add" }))), ), - ), - ) - }) + ) + }), + ) - test("watches non-git roots", async () => { - await using tmp = await tmpdir() - const file = path.join(tmp.path, "plain.txt") - const dir = tmp.path - - await withWatcher( - dir, - nextUpdate( - dir, - (e) => e.file === file && e.event === "add", - Effect.promise(() => fs.writeFile(file, "plain")), - ).pipe(Effect.tap((evt) => Effect.sync(() => expect(evt).toEqual({ file, event: "add" })))), - ) - }) + it.instance( + "cleanup stops publishing events", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const fs = yield* AppFileSystem.Service + const file = path.join(test.directory, "after-dispose.txt") - test("cleanup stops publishing events", async () => { - await using tmp = await tmpdir({ git: true }) - const file = path.join(tmp.path, "after-dispose.txt") + // Start and immediately stop the watcher (withWatcher disposes on exit). + yield* withWatcher(test.directory, Effect.void) - // Start and immediately stop the watcher (withWatcher disposes on exit) - await withWatcher(tmp.path, Effect.void) + // Now write a file - no watcher should be listening. + yield* noUpdate(test.directory, (e) => e.file === file, fs.writeFileString(file, "gone")).pipe( + provideInstance(test.directory), + ) + }), + { git: true }, + ) - // Now write a file — no watcher should be listening - await WithInstance.provide({ - directory: tmp.path, - fn: () => - Effect.runPromise( + it.instance( + "ignores .git/index changes", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const fs = yield* AppFileSystem.Service + const git = yield* Git.Service + const gitIndex = path.join(test.directory, ".git", "index") + const edit = path.join(test.directory, "tracked.txt") + + yield* withWatcher( + test.directory, noUpdate( - tmp.path, - (e) => e.file === file, - Effect.promise(() => fs.writeFile(file, "gone")), + test.directory, + (e) => e.file === gitIndex, + fs.writeFileString(edit, "a").pipe(Effect.andThen(git.run(["add", "."], { cwd: test.directory }))), ), - ), - }) - }) + ) + }), + { git: true }, + ) - test("ignores .git/index changes", async () => { - await using tmp = await tmpdir({ git: true }) - const gitIndex = path.join(tmp.path, ".git", "index") - const edit = path.join(tmp.path, "tracked.txt") - - await withWatcher( - tmp.path, - noUpdate( - tmp.path, - (e) => e.file === gitIndex, - Effect.promise(async () => { - await fs.writeFile(edit, "a") - await $`git add .`.cwd(tmp.path).quiet().nothrow() - }), - ), - ) - }) + it.instance( + "publishes .git/HEAD events", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const fs = yield* AppFileSystem.Service + const git = yield* Git.Service + const head = path.join(test.directory, ".git", "HEAD") + const branch = `watch-${Math.random().toString(36).slice(2)}` + yield* git.run(["branch", branch], { cwd: test.directory }) - test("publishes .git/HEAD events", async () => { - await using tmp = await tmpdir({ git: true }) - const head = path.join(tmp.path, ".git", "HEAD") - const branch = `watch-${Math.random().toString(36).slice(2)}` - await $`git branch ${branch}`.cwd(tmp.path).quiet() - - await withWatcher( - tmp.path, - nextUpdate( - tmp.path, - (evt) => evt.file === head && evt.event !== "unlink", - Effect.promise(() => fs.writeFile(head, `ref: refs/heads/${branch}\n`)), - ).pipe( - Effect.tap((evt) => - Effect.sync(() => { - expect(evt.file).toBe(head) - expect(["add", "change"]).toContain(evt.event) - }), - ), - ), - ) - }) + yield* withWatcher( + test.directory, + nextUpdate( + test.directory, + (evt) => evt.file === head && evt.event !== "unlink", + fs.writeFileString(head, `ref: refs/heads/${branch}\n`), + ).pipe( + Effect.tap((evt) => + Effect.sync(() => { + expect(evt.file).toBe(head) + expect(["add", "change"]).toContain(evt.event) + }), + ), + ), + ) + }), + { git: true }, + ) }) diff --git a/packages/opencode/test/fixture/db.ts b/packages/opencode/test/fixture/db.ts index 07b42d99462..db4a5df20c4 100644 --- a/packages/opencode/test/fixture/db.ts +++ b/packages/opencode/test/fixture/db.ts @@ -5,7 +5,8 @@ import { disposeAllInstances } from "./fixture" export async function resetDatabase() { await disposeAllInstances().catch(() => undefined) Database.close() - await rm(Database.Path, { force: true }).catch(() => undefined) - await rm(`${Database.Path}-wal`, { force: true }).catch(() => undefined) - await rm(`${Database.Path}-shm`, { force: true }).catch(() => undefined) + const dbPath = Database.getPath() + await rm(dbPath, { force: true }).catch(() => undefined) + await rm(`${dbPath}-wal`, { force: true }).catch(() => undefined) + await rm(`${dbPath}-shm`, { force: true }).catch(() => undefined) } diff --git a/packages/opencode/test/fixture/fixture.ts b/packages/opencode/test/fixture/fixture.ts index 9fdbe57493d..1075061df42 100644 --- a/packages/opencode/test/fixture/fixture.ts +++ b/packages/opencode/test/fixture/fixture.ts @@ -131,7 +131,7 @@ export function tmpdirScoped(options?: { git?: boolean; config?: Partial> & { + soundboard?: Partial +} + function themeCurrent(): HostPluginApi["theme"]["current"] { const a = RGBA.fromInts(0, 120, 240) const b = RGBA.fromInts(120, 120, 120) @@ -83,6 +87,8 @@ function themeCurrent(): HostPluginApi["theme"]["current"] { type Opts = { client?: HostPluginApi["client"] | (() => HostPluginApi["client"]) renderer?: HostPluginApi["renderer"] + attention?: AttentionOpts + event?: HostPluginApi["event"] count?: Count keymap?: HostPluginApi["keymap"] tuiConfig?: Partial @@ -183,6 +189,17 @@ export function createTuiPluginApi(opts: Opts = {}): HostPluginApi { return opts.app?.version ?? "0.0.0-test" }, }, + attention: { + async notify(input) { + return opts.attention?.notify?.(input) ?? { ok: false, notification: false, sound: false } + }, + soundboard: { + registerPack: (pack) => opts.attention?.soundboard?.registerPack?.(pack) ?? (() => {}), + activate: (id, options) => opts.attention?.soundboard?.activate?.(id, options) ?? false, + current: () => opts.attention?.soundboard?.current?.() ?? "opencode.default", + list: () => opts.attention?.soundboard?.list?.() ?? [], + }, + }, keys: { formatSequence: () => "", formatBindings: () => undefined, @@ -190,7 +207,7 @@ export function createTuiPluginApi(opts: Opts = {}): HostPluginApi { get client() { return client() }, - event: { + event: opts.event ?? { on: () => { if (count) count.event_add += 1 return () => { @@ -292,6 +309,7 @@ export function createTuiPluginApi(opts: Opts = {}): HostPluginApi { }, session: { count: opts.state?.session?.count ?? (() => 0), + get: opts.state?.session?.get ?? (() => undefined), diff: opts.state?.session?.diff ?? (() => []), todo: opts.state?.session?.todo ?? (() => []), processes: opts.state?.session?.processes ?? (() => []), // kilocode_change diff --git a/packages/opencode/test/fixture/tui-runtime.ts b/packages/opencode/test/fixture/tui-runtime.ts index 0a5c4b80f36..e3869aea756 100644 --- a/packages/opencode/test/fixture/tui-runtime.ts +++ b/packages/opencode/test/fixture/tui-runtime.ts @@ -5,7 +5,8 @@ import { TuiConfig } from "../../src/cli/cmd/tui/config/tui" import { TuiKeybind } from "../../src/cli/cmd/tui/config/keybind" type PluginSpec = string | [string, Record] -type ResolvedInput = Omit & { +type ResolvedInput = Omit & { + attention?: Partial keybinds?: Partial leader_timeout?: number } @@ -22,6 +23,15 @@ export function createTuiResolvedConfig(input: ResolvedInput = {}): TuiConfig.Re const keybinds = TuiKeybind.Keybinds.parse(input.keybinds ?? {}) return { ...input, + attention: { + enabled: false, + notifications: true, + sound: true, + volume: 0.4, + sound_pack: "opencode.default", + sounds: {}, + ...input.attention, + }, keybinds: createTuiResolvedKeybinds(keybinds), leader_timeout: input.leader_timeout ?? 2000, } diff --git a/packages/opencode/test/fixture/workspace.ts b/packages/opencode/test/fixture/workspace.ts new file mode 100644 index 00000000000..9c201d39824 --- /dev/null +++ b/packages/opencode/test/fixture/workspace.ts @@ -0,0 +1,28 @@ +import { FetchHttpClient } from "effect/unstable/http" +import { Layer } from "effect" +import { AppFileSystem } from "@opencode-ai/core/filesystem" +import { Auth } from "../../src/auth" +import { Workspace } from "../../src/control-plane/workspace" +import { RuntimeFlags } from "../../src/effect/runtime-flags" +import { InstanceBootstrap } from "../../src/project/bootstrap" +import { InstanceStore } from "../../src/project/instance-store" +import { Project } from "../../src/project/project" +import { Vcs } from "../../src/project/vcs" +import { Session } from "../../src/session/session" +import { SessionPrompt } from "../../src/session/prompt" +import { SyncEvent } from "../../src/sync" + +export const workspaceLayerWithRuntimeFlags = (overrides: Partial) => + Workspace.layer.pipe( + Layer.provide(Auth.defaultLayer), + Layer.provide(Session.defaultLayer), + Layer.provide(SyncEvent.defaultLayer), + Layer.provide(SessionPrompt.defaultLayer), + Layer.provide(Project.defaultLayer), + Layer.provide(Vcs.defaultLayer), + Layer.provide(FetchHttpClient.layer), + Layer.provide(AppFileSystem.defaultLayer), + Layer.provide(RuntimeFlags.layer(overrides)), + Layer.provide(InstanceStore.defaultLayer), + Layer.provide(InstanceBootstrap.defaultLayer), + ) diff --git a/packages/opencode/test/format/format.test.ts b/packages/opencode/test/format/format.test.ts index 674d2767cd0..c9e57d204e0 100644 --- a/packages/opencode/test/format/format.test.ts +++ b/packages/opencode/test/format/format.test.ts @@ -186,14 +186,14 @@ describe("Format", () => { Formatter.gofmt.enabled = async () => { active++ max = Math.max(max, active) - await Bun.sleep(20) + await Promise.resolve() active-- return ["sh", "-c", "true"] } Formatter.mix.enabled = async () => { active++ max = Math.max(max, active) - await Bun.sleep(20) + await Promise.resolve() active-- return ["sh", "-c", "true"] } diff --git a/packages/opencode/test/git/git.test.ts b/packages/opencode/test/git/git.test.ts index 1e56865d727..e80b8fa9065 100644 --- a/packages/opencode/test/git/git.test.ts +++ b/packages/opencode/test/git/git.test.ts @@ -1,71 +1,70 @@ import { $ } from "bun" -import { describe, expect, test } from "bun:test" +import { describe, expect } from "bun:test" import fs from "fs/promises" import path from "path" -import { ManagedRuntime } from "effect" +import { Effect } from "effect" import { Git } from "../../src/git" import { tmpdir } from "../fixture/fixture" +import { testEffect } from "../lib/effect" const weird = process.platform === "win32" ? "space file.txt" : "tab\tfile.txt" +const it = testEffect(Git.defaultLayer) -async function withGit(body: (rt: ManagedRuntime.ManagedRuntime) => Promise) { - const rt = ManagedRuntime.make(Git.defaultLayer) - try { - return await body(rt) - } finally { - await rt.dispose() - } -} +const scopedTmpdir = (options?: Parameters[0]) => + Effect.acquireRelease( + Effect.promise(() => tmpdir(options)), + (tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()), + ) describe("Git", () => { - test("branch() returns current branch name", async () => { - await using tmp = await tmpdir({ git: true }) - - await withGit(async (rt) => { - const branch = await rt.runPromise(Git.Service.use((git) => git.branch(tmp.path))) + it.live("branch() returns current branch name", () => + Effect.gen(function* () { + const tmp = yield* scopedTmpdir({ git: true }) + const git = yield* Git.Service + const branch = yield* git.branch(tmp.path) expect(branch).toBeDefined() expect(typeof branch).toBe("string") - }) - }) - - test("branch() returns undefined for non-git directories", async () => { - await using tmp = await tmpdir() - - await withGit(async (rt) => { - const branch = await rt.runPromise(Git.Service.use((git) => git.branch(tmp.path))) + }), + ) + + it.live("branch() returns undefined for non-git directories", () => + Effect.gen(function* () { + const tmp = yield* scopedTmpdir() + const git = yield* Git.Service + const branch = yield* git.branch(tmp.path) expect(branch).toBeUndefined() - }) - }) - - test("branch() returns undefined for detached HEAD", async () => { - await using tmp = await tmpdir({ git: true }) - const hash = (await $`git rev-parse HEAD`.cwd(tmp.path).quiet().text()).trim() - await $`git checkout --detach ${hash}`.cwd(tmp.path).quiet() - - await withGit(async (rt) => { - const branch = await rt.runPromise(Git.Service.use((git) => git.branch(tmp.path))) + }), + ) + + it.live("branch() returns undefined for detached HEAD", () => + Effect.gen(function* () { + const tmp = yield* scopedTmpdir({ git: true }) + const hash = (yield* Effect.promise(() => $`git rev-parse HEAD`.cwd(tmp.path).quiet().text())).trim() + yield* Effect.promise(() => $`git checkout --detach ${hash}`.cwd(tmp.path).quiet()) + const git = yield* Git.Service + const branch = yield* git.branch(tmp.path) expect(branch).toBeUndefined() - }) - }) - - test("defaultBranch() uses init.defaultBranch when available", async () => { - await using tmp = await tmpdir({ git: true }) - await $`git branch -M trunk`.cwd(tmp.path).quiet() - await $`git config init.defaultBranch trunk`.cwd(tmp.path).quiet() - - await withGit(async (rt) => { - const branch = await rt.runPromise(Git.Service.use((git) => git.defaultBranch(tmp.path))) + }), + ) + + it.live("defaultBranch() uses init.defaultBranch when available", () => + Effect.gen(function* () { + const tmp = yield* scopedTmpdir({ git: true }) + yield* Effect.promise(() => $`git branch -M trunk`.cwd(tmp.path).quiet()) + yield* Effect.promise(() => $`git config init.defaultBranch trunk`.cwd(tmp.path).quiet()) + const git = yield* Git.Service + const branch = yield* git.defaultBranch(tmp.path) expect(branch?.name).toBe("trunk") expect(branch?.ref).toBe("trunk") - }) - }) - - test("status() handles special filenames", async () => { - await using tmp = await tmpdir({ git: true }) - await fs.writeFile(path.join(tmp.path, weird), "hello\n", "utf-8") - - await withGit(async (rt) => { - const status = await rt.runPromise(Git.Service.use((git) => git.status(tmp.path))) + }), + ) + + it.live("status() handles special filenames", () => + Effect.gen(function* () { + const tmp = yield* scopedTmpdir({ git: true }) + yield* Effect.promise(() => fs.writeFile(path.join(tmp.path, weird), "hello\n", "utf-8")) + const git = yield* Git.Service + const status = yield* git.status(tmp.path) expect(status).toEqual( expect.arrayContaining([ expect.objectContaining({ @@ -74,23 +73,24 @@ describe("Git", () => { }), ]), ) - }) - }) - - test("diff(), stats(), and mergeBase() parse tracked changes", async () => { - await using tmp = await tmpdir({ git: true }) - await $`git branch -M main`.cwd(tmp.path).quiet() - await fs.writeFile(path.join(tmp.path, weird), "before\n", "utf-8") - await $`git add .`.cwd(tmp.path).quiet() - await $`git commit --no-gpg-sign -m "add file"`.cwd(tmp.path).quiet() - await $`git checkout -b feature/test`.cwd(tmp.path).quiet() - await fs.writeFile(path.join(tmp.path, weird), "after\n", "utf-8") - - await withGit(async (rt) => { - const [base, diff, stats] = await Promise.all([ - rt.runPromise(Git.Service.use((git) => git.mergeBase(tmp.path, "main"))), - rt.runPromise(Git.Service.use((git) => git.diff(tmp.path, "HEAD"))), - rt.runPromise(Git.Service.use((git) => git.stats(tmp.path, "HEAD"))), + }), + ) + + it.live("diff(), stats(), and mergeBase() parse tracked changes", () => + Effect.gen(function* () { + const tmp = yield* scopedTmpdir({ git: true }) + yield* Effect.promise(() => $`git branch -M main`.cwd(tmp.path).quiet()) + yield* Effect.promise(() => fs.writeFile(path.join(tmp.path, weird), "before\n", "utf-8")) + yield* Effect.promise(() => $`git add .`.cwd(tmp.path).quiet()) + yield* Effect.promise(() => $`git commit --no-gpg-sign -m "add file"`.cwd(tmp.path).quiet()) + yield* Effect.promise(() => $`git checkout -b feature/test`.cwd(tmp.path).quiet()) + yield* Effect.promise(() => fs.writeFile(path.join(tmp.path, weird), "after\n", "utf-8")) + + const git = yield* Git.Service + const [base, diff, stats] = yield* Effect.all([ + git.mergeBase(tmp.path, "main"), + git.diff(tmp.path, "HEAD"), + git.stats(tmp.path, "HEAD"), ]) expect(base).toBeTruthy() @@ -111,23 +111,24 @@ describe("Git", () => { }), ]), ) - }) - }) - - test("patch() returns capped native patch output", async () => { - await using tmp = await tmpdir({ git: true }) - await fs.writeFile(path.join(tmp.path, weird), "before\n", "utf-8") - await fs.writeFile(path.join(tmp.path, "other.txt"), "old\n", "utf-8") - await $`git add .`.cwd(tmp.path).quiet() - await $`git commit --no-gpg-sign -m "add file"`.cwd(tmp.path).quiet() - await fs.writeFile(path.join(tmp.path, weird), "after\n", "utf-8") - await fs.writeFile(path.join(tmp.path, "other.txt"), "new\n", "utf-8") - - await withGit(async (rt) => { - const [patch, all, capped] = await Promise.all([ - rt.runPromise(Git.Service.use((git) => git.patch(tmp.path, "HEAD", weird, { context: 2_147_483_647 }))), - rt.runPromise(Git.Service.use((git) => git.patchAll(tmp.path, "HEAD", { context: 2_147_483_647 }))), - rt.runPromise(Git.Service.use((git) => git.patch(tmp.path, "HEAD", weird, { maxOutputBytes: 1 }))), + }), + ) + + it.live("patch() returns capped native patch output", () => + Effect.gen(function* () { + const tmp = yield* scopedTmpdir({ git: true }) + yield* Effect.promise(() => fs.writeFile(path.join(tmp.path, weird), "before\n", "utf-8")) + yield* Effect.promise(() => fs.writeFile(path.join(tmp.path, "other.txt"), "old\n", "utf-8")) + yield* Effect.promise(() => $`git add .`.cwd(tmp.path).quiet()) + yield* Effect.promise(() => $`git commit --no-gpg-sign -m "add file"`.cwd(tmp.path).quiet()) + yield* Effect.promise(() => fs.writeFile(path.join(tmp.path, weird), "after\n", "utf-8")) + yield* Effect.promise(() => fs.writeFile(path.join(tmp.path, "other.txt"), "new\n", "utf-8")) + + const git = yield* Git.Service + const [patch, all, capped] = yield* Effect.all([ + git.patch(tmp.path, "HEAD", weird, { context: 2_147_483_647 }), + git.patchAll(tmp.path, "HEAD", { context: 2_147_483_647 }), + git.patch(tmp.path, "HEAD", weird, { maxOutputBytes: 1 }), ]) expect(patch.truncated).toBe(false) @@ -140,17 +141,18 @@ describe("Git", () => { expect(all.text).toContain("+new") expect(capped.truncated).toBe(true) expect(capped.text).toBe("") - }) - }) - - test("patchUntracked() and statUntracked() handle added files", async () => { - await using tmp = await tmpdir({ git: true }) - await fs.writeFile(path.join(tmp.path, weird), "one\ntwo\n", "utf-8") - - await withGit(async (rt) => { - const [patch, stat] = await Promise.all([ - rt.runPromise(Git.Service.use((git) => git.patchUntracked(tmp.path, weird, { context: 2_147_483_647 }))), - rt.runPromise(Git.Service.use((git) => git.statUntracked(tmp.path, weird))), + }), + ) + + it.live("patchUntracked() and statUntracked() handle added files", () => + Effect.gen(function* () { + const tmp = yield* scopedTmpdir({ git: true }) + yield* Effect.promise(() => fs.writeFile(path.join(tmp.path, weird), "one\ntwo\n", "utf-8")) + + const git = yield* Git.Service + const [patch, stat] = yield* Effect.all([ + git.patchUntracked(tmp.path, weird, { context: 2_147_483_647 }), + git.statUntracked(tmp.path, weird), ]) expect(patch.truncated).toBe(false) @@ -158,18 +160,19 @@ describe("Git", () => { expect(patch.text).toContain("+one") expect(patch.text).toContain("+two") expect(stat).toEqual(expect.objectContaining({ file: weird, additions: 2, deletions: 0 })) - }) - }) - - test("show() returns empty text for binary blobs", async () => { - await using tmp = await tmpdir({ git: true }) - await fs.writeFile(path.join(tmp.path, "bin.dat"), new Uint8Array([0, 1, 2, 3])) - await $`git add .`.cwd(tmp.path).quiet() - await $`git commit --no-gpg-sign -m "add binary"`.cwd(tmp.path).quiet() - - await withGit(async (rt) => { - const text = await rt.runPromise(Git.Service.use((git) => git.show(tmp.path, "HEAD", "bin.dat"))) + }), + ) + + it.live("show() returns empty text for binary blobs", () => + Effect.gen(function* () { + const tmp = yield* scopedTmpdir({ git: true }) + yield* Effect.promise(() => fs.writeFile(path.join(tmp.path, "bin.dat"), new Uint8Array([0, 1, 2, 3]))) + yield* Effect.promise(() => $`git add .`.cwd(tmp.path).quiet()) + yield* Effect.promise(() => $`git commit --no-gpg-sign -m "add binary"`.cwd(tmp.path).quiet()) + + const git = yield* Git.Service + const text = yield* git.show(tmp.path, "HEAD", "bin.dat") expect(text).toBe("") - }) - }) + }), + ) }) diff --git a/packages/opencode/test/image/fixtures/picture-5mb-base64.png b/packages/opencode/test/image/fixtures/picture-5mb-base64.png new file mode 100644 index 00000000000..e8923b9e71c Binary files /dev/null and b/packages/opencode/test/image/fixtures/picture-5mb-base64.png differ diff --git a/packages/opencode/test/installation/installation.test.ts b/packages/opencode/test/installation/installation.test.ts index cdc26c1b967..8193ab8d105 100644 --- a/packages/opencode/test/installation/installation.test.ts +++ b/packages/opencode/test/installation/installation.test.ts @@ -1,9 +1,11 @@ -import { describe, expect, test } from "bun:test" +import { describe, expect } from "bun:test" import { Effect, Layer, Stream } from "effect" import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http" import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process" import { Installation } from "../../src/installation" import { InstallationChannel } from "@opencode-ai/core/installation/version" +import { AppProcess } from "@opencode-ai/core/process" +import { testEffect } from "../lib/effect" const encoder = new TextEncoder() @@ -46,127 +48,122 @@ function testLayer( httpHandler: (request: HttpClientRequest.HttpClientRequest) => Response, spawnHandler?: (cmd: string, args: readonly string[]) => string, ) { - return Installation.layer.pipe(Layer.provide(mockHttpClient(httpHandler)), Layer.provide(mockSpawner(spawnHandler))) + const appProcess = AppProcess.layer.pipe(Layer.provide(mockSpawner(spawnHandler))) + return Installation.layer.pipe(Layer.provide(mockHttpClient(httpHandler)), Layer.provide(appProcess)) } describe("installation", () => { describe("latest", () => { - test("reads release version from GitHub releases", async () => { - const layer = testLayer(() => jsonResponse({ tag_name: "v1.2.3" })) - - const result = await Effect.runPromise( - Installation.Service.use((svc) => svc.latest("unknown")).pipe(Effect.provide(layer)), - ) - expect(result).toBe("1.2.3") - }) - - test("strips v prefix from GitHub release tag", async () => { - const layer = testLayer(() => jsonResponse({ tag_name: "v4.0.0-beta.1" })) + testEffect(testLayer(() => jsonResponse({ tag_name: "v1.2.3" }))).effect( + "reads release version from GitHub releases", + () => + Effect.gen(function* () { + const result = yield* Installation.Service.use((svc) => svc.latest("unknown")) + expect(result).toBe("1.2.3") + }), + ) - const result = await Effect.runPromise( - Installation.Service.use((svc) => svc.latest("curl")).pipe(Effect.provide(layer)), - ) - expect(result).toBe("4.0.0-beta.1") - }) + testEffect(testLayer(() => jsonResponse({ tag_name: "v4.0.0-beta.1" }))).effect( + "strips v prefix from GitHub release tag", + () => + Effect.gen(function* () { + const result = yield* Installation.Service.use((svc) => svc.latest("curl")) + expect(result).toBe("4.0.0-beta.1") + }), + ) - test("reads npm versions via registry", async () => { - const calls: string[] = [] - const layer = testLayer((request) => { - calls.push(request.url) + const npmCalls: string[] = [] + testEffect( + testLayer((request) => { + npmCalls.push(request.url) return jsonResponse({ version: "1.5.0" }) - }) - - const result = await Effect.runPromise( - Installation.Service.use((svc) => svc.latest("npm")).pipe(Effect.provide(layer)), - ) - expect(result).toBe("1.5.0") - expect(calls).toContain(`https://registry.npmjs.org/@kilocode%2Fcli/${InstallationChannel}`) // kilocode_change - }) + }), + ).effect("reads npm versions via registry", () => + Effect.gen(function* () { + const result = yield* Installation.Service.use((svc) => svc.latest("npm")) + expect(result).toBe("1.5.0") + expect(npmCalls).toContain(`https://registry.npmjs.org/opencode-ai/${InstallationChannel}`) + }), + ) - test("reads bun versions via registry", async () => { - const calls: string[] = [] - const layer = testLayer((request) => { - calls.push(request.url) + const bunCalls: string[] = [] + testEffect( + testLayer((request) => { + bunCalls.push(request.url) return jsonResponse({ version: "1.6.0" }) - }) - - const result = await Effect.runPromise( - Installation.Service.use((svc) => svc.latest("bun")).pipe(Effect.provide(layer)), - ) - expect(result).toBe("1.6.0") - expect(calls).toContain(`https://registry.npmjs.org/@kilocode%2Fcli/${InstallationChannel}`) // kilocode_change - }) + }), + ).effect("reads bun versions via registry", () => + Effect.gen(function* () { + const result = yield* Installation.Service.use((svc) => svc.latest("bun")) + expect(result).toBe("1.6.0") + expect(bunCalls).toContain(`https://registry.npmjs.org/opencode-ai/${InstallationChannel}`) + }), + ) - test("reads pnpm versions via registry", async () => { - const calls: string[] = [] - const layer = testLayer((request) => { - calls.push(request.url) + const pnpmCalls: string[] = [] + testEffect( + testLayer((request) => { + pnpmCalls.push(request.url) return jsonResponse({ version: "1.7.0" }) - }) - - const result = await Effect.runPromise( - Installation.Service.use((svc) => svc.latest("pnpm")).pipe(Effect.provide(layer)), - ) - expect(result).toBe("1.7.0") - expect(calls).toContain(`https://registry.npmjs.org/@kilocode%2Fcli/${InstallationChannel}`) // kilocode_change - }) - - test("reads scoop manifest versions", async () => { - const layer = testLayer(() => jsonResponse({ version: "2.3.4" })) - - const result = await Effect.runPromise( - Installation.Service.use((svc) => svc.latest("scoop")).pipe(Effect.provide(layer)), - ) - expect(result).toBe("2.3.4") - }) + }), + ).effect("reads pnpm versions via registry", () => + Effect.gen(function* () { + const result = yield* Installation.Service.use((svc) => svc.latest("pnpm")) + expect(result).toBe("1.7.0") + expect(pnpmCalls).toContain(`https://registry.npmjs.org/opencode-ai/${InstallationChannel}`) + }), + ) - test("reads chocolatey feed versions", async () => { - const layer = testLayer(() => jsonResponse({ d: { results: [{ Version: "3.4.5" }] } })) + testEffect(testLayer(() => jsonResponse({ version: "2.3.4" }))).effect("reads scoop manifest versions", () => + Effect.gen(function* () { + const result = yield* Installation.Service.use((svc) => svc.latest("scoop")) + expect(result).toBe("2.3.4") + }), + ) - const result = await Effect.runPromise( - Installation.Service.use((svc) => svc.latest("choco")).pipe(Effect.provide(layer)), - ) - expect(result).toBe("3.4.5") - }) + testEffect(testLayer(() => jsonResponse({ d: { results: [{ Version: "3.4.5" }] } }))).effect( + "reads chocolatey feed versions", + () => + Effect.gen(function* () { + const result = yield* Installation.Service.use((svc) => svc.latest("choco")) + expect(result).toBe("3.4.5") + }), + ) - test("reads brew formulae API versions", async () => { - const layer = testLayer( + testEffect( + testLayer( () => jsonResponse({ versions: { stable: "2.0.0" } }), (cmd, args) => { // getBrewFormula: return core formula (no tap) - // kilocode_change start - if (cmd === "brew" && args.includes("--formula") && args.includes("Kilo-Org/tap/kilo")) return "" - if (cmd === "brew" && args.includes("--formula") && args.includes("kilo")) return "kilo" - // kilocode_change end + if (cmd === "brew" && args.includes("--formula") && args.includes("anomalyco/tap/opencode")) return "" + if (cmd === "brew" && args.includes("--formula") && args.includes("opencode")) return "opencode" return "" }, - ) + ), + ).effect("reads brew formulae API versions", () => + Effect.gen(function* () { + const result = yield* Installation.Service.use((svc) => svc.latest("brew")) + expect(result).toBe("2.0.0") + }), + ) - const result = await Effect.runPromise( - Installation.Service.use((svc) => svc.latest("brew")).pipe(Effect.provide(layer)), - ) - expect(result).toBe("2.0.0") + const brewInfoJson = JSON.stringify({ + formulae: [{ versions: { stable: "2.1.0" } }], }) - - test("reads brew tap info JSON via CLI", async () => { - const brewInfoJson = JSON.stringify({ - formulae: [{ versions: { stable: "2.1.0" } }], - }) - const layer = testLayer( + testEffect( + testLayer( () => jsonResponse({}), // HTTP not used for tap formula (cmd, args) => { - // kilocode_change start - if (cmd === "brew" && args.includes("Kilo-Org/tap/kilo") && args.includes("--formula")) return "kilo" - // kilocode_change end + if (cmd === "brew" && args.includes("anomalyco/tap/opencode") && args.includes("--formula")) return "opencode" if (cmd === "brew" && args.includes("--json=v2")) return brewInfoJson return "" }, - ) - - const result = await Effect.runPromise( - Installation.Service.use((svc) => svc.latest("brew")).pipe(Effect.provide(layer)), - ) - expect(result).toBe("2.1.0") - }) + ), + ).effect("reads brew tap info JSON via CLI", () => + Effect.gen(function* () { + const result = yield* Installation.Service.use((svc) => svc.latest("brew")) + expect(result).toBe("2.1.0") + }), + ) }) }) diff --git a/packages/opencode/test/kilocode/bash-permission-metadata.test.ts b/packages/opencode/test/kilocode/bash-permission-metadata.test.ts index c5190d0ef79..dea0138049b 100644 --- a/packages/opencode/test/kilocode/bash-permission-metadata.test.ts +++ b/packages/opencode/test/kilocode/bash-permission-metadata.test.ts @@ -13,6 +13,7 @@ import * as CrossSpawnSpawner from "@opencode-ai/core/cross-spawn-spawner" import { AppFileSystem } from "@opencode-ai/core/filesystem" import { Plugin } from "../../src/plugin" import { Config } from "../../src/config/config" +import { RuntimeFlags } from "../../src/effect/runtime-flags" const runtime = ManagedRuntime.make( Layer.mergeAll( @@ -22,6 +23,7 @@ const runtime = ManagedRuntime.make( Truncate.defaultLayer, Agent.defaultLayer, Config.defaultLayer, + RuntimeFlags.layer(), ), ) diff --git a/packages/opencode/test/kilocode/cli/cmd/tui/context/tui-config.test.ts b/packages/opencode/test/kilocode/cli/cmd/tui/context/tui-config.test.ts index 9b0bae42c08..a2e7f96ae36 100644 --- a/packages/opencode/test/kilocode/cli/cmd/tui/context/tui-config.test.ts +++ b/packages/opencode/test/kilocode/cli/cmd/tui/context/tui-config.test.ts @@ -5,6 +5,8 @@ */ import { describe, expect, test } from "bun:test" import { createEffect, createRoot } from "solid-js" +import { createBindingLookup } from "@opentui/keymap/extras" +import { TuiKeybind } from "@/cli/cmd/tui/config/keybind" import { TuiConfig } from "@/cli/cmd/tui/config/tui" import { KiloTuiConfig } from "@/kilocode/cli/cmd/tui/context/tui-config" @@ -12,9 +14,29 @@ function cfg(input: Partial): TuiConfig.Info { return input as TuiConfig.Info } +function resolve(input: TuiConfig.Info): TuiConfig.Resolved { + const keybinds = TuiKeybind.parse(input.keybinds ?? {}) + return { + ...input, + attention: { + enabled: input.attention?.enabled ?? false, + notifications: input.attention?.notifications ?? true, + sound: input.attention?.sound ?? true, + volume: input.attention?.volume ?? 0.4, + sound_pack: input.attention?.sound_pack ?? "opencode.default", + sounds: input.attention?.sounds ?? {}, + }, + keybinds: createBindingLookup(TuiKeybind.toBindingConfig(keybinds), { + commandMap: TuiKeybind.CommandMap, + bindingDefaults: TuiKeybind.bindingDefaults(), + }), + leader_timeout: input.leader_timeout ?? 1_000, + } +} + describe("KiloTuiConfig.makeStore", () => { test("reactive reads update when set() reconciles a new config", () => { - const store = KiloTuiConfig.makeStore(TuiConfig.resolve(cfg({ keybinds: { app_exit: "ctrl+c" }, theme: "kilo" }))) + const store = KiloTuiConfig.makeStore(resolve(cfg({ keybinds: { app_exit: "ctrl+c" }, theme: "kilo" }))) const exits: Array = [] const themes: Array = [] @@ -44,7 +66,7 @@ describe("KiloTuiConfig.makeStore", () => { }) test("set() does not re-notify a tracked read when its value is unchanged", () => { - const store = KiloTuiConfig.makeStore(TuiConfig.resolve(cfg({ keybinds: { app_exit: "ctrl+c" }, theme: "kilo" }))) + const store = KiloTuiConfig.makeStore(resolve(cfg({ keybinds: { app_exit: "ctrl+c" }, theme: "kilo" }))) const exits: Array = [] let dispose!: () => void diff --git a/packages/opencode/test/kilocode/compaction-payload-recovery.test.ts b/packages/opencode/test/kilocode/compaction-payload-recovery.test.ts index 36df07576c4..dc8e46ee86b 100644 --- a/packages/opencode/test/kilocode/compaction-payload-recovery.test.ts +++ b/packages/opencode/test/kilocode/compaction-payload-recovery.test.ts @@ -5,6 +5,7 @@ import * as Stream from "effect/Stream" import { Agent } from "../../src/agent/agent" import { Bus } from "../../src/bus" import { Config } from "../../src/config/config" +import { RuntimeFlags } from "../../src/effect/runtime-flags" import { Image } from "../../src/image/image" import { KiloCompactionPayloadRecovery } from "../../src/kilocode/session/compaction-payload-recovery" import { KiloSessionCompaction } from "../../src/kilocode/session/compaction" @@ -195,6 +196,7 @@ function runtime(layer: Layer.Layer, config = Config.defaultLayer) Layer.provide(status), Layer.provide(bus), Layer.provide(config), + Layer.provide(RuntimeFlags.layer()), Layer.provide(SyncEvent.defaultLayer), ), ) diff --git a/packages/opencode/test/kilocode/config/config.test.ts b/packages/opencode/test/kilocode/config/config.test.ts index bcf3651f6aa..55c2039a6bc 100644 --- a/packages/opencode/test/kilocode/config/config.test.ts +++ b/packages/opencode/test/kilocode/config/config.test.ts @@ -1,6 +1,6 @@ // kilocode_change - new file import { afterEach, describe, expect, test } from "bun:test" -import { Effect, Layer, Option } from "effect" +import { Effect, Layer, Option, Schema } from "effect" import { NodeFileSystem, NodePath } from "@effect/platform-node" import path from "path" import { Global } from "@opencode-ai/core/global" @@ -54,6 +54,17 @@ async function writeConfig(dir: string, config: object, name = "kilo.json") { await Filesystem.write(path.join(dir, name), JSON.stringify(config)) } +function decode(input: unknown): Config.Info { + const config = Schema.decodeUnknownSync(Config.Info)(input) + return { + ...config, + skills: config.skills && { + paths: config.skills.paths && [...config.skills.paths], + urls: config.skills.urls && [...config.skills.urls], + }, + } +} + const cfg: Partial = { plugin: ["@kilocode/kilo-indexing"], indexing: { @@ -186,7 +197,7 @@ describe("kilocode indexing config", () => { }) test("accepts delete sentinels for indexing model overrides", () => { - const patch = Config.Info.zod.parse({ indexing: { model: null, dimension: null } }) + const patch = decode({ indexing: { model: null, dimension: null } }) const merged = KilocodeConfig.mergeConfig( { indexing: { @@ -207,7 +218,7 @@ describe("kilocode indexing config", () => { describe("agent config", () => { test("accepts delete sentinels for agent model and variant overrides", () => { - const patch = Config.Info.zod.parse({ agent: { explore: { model: null, variant: null } } }) + const patch = decode({ agent: { explore: { model: null, variant: null } } }) const merged = KilocodeConfig.mergeConfig( { agent: { @@ -226,7 +237,7 @@ describe("agent config", () => { }) test("removes an agent variant override without removing its model", () => { - const patch = Config.Info.zod.parse({ agent: { explore: { variant: null } } }) + const patch = decode({ agent: { explore: { variant: null } } }) const merged = KilocodeConfig.mergeConfig( { agent: { @@ -267,7 +278,7 @@ describe("agent config", () => { "}", ].join("\n"), ) - const patch = Config.Info.zod.parse({ agent: { explore: { model: null, variant: null } } }) + const patch = decode({ agent: { explore: { model: null, variant: null } } }) await saveGlobal(patch) diff --git a/packages/opencode/test/kilocode/config/indexing-default-plugin.test.ts b/packages/opencode/test/kilocode/config/indexing-default-plugin.test.ts index 7975c9cc842..7bfdf95a232 100644 --- a/packages/opencode/test/kilocode/config/indexing-default-plugin.test.ts +++ b/packages/opencode/test/kilocode/config/indexing-default-plugin.test.ts @@ -2,6 +2,7 @@ import { afterEach, describe, expect, test } from "bun:test" import { Effect, Layer, Option } from "effect" import { NodeFileSystem, NodePath } from "@effect/platform-node" import path from "path" +import { Flag } from "@opencode-ai/core/flag/flag" import { hasIndexingPlugin } from "@kilocode/kilo-indexing/detect" import { Account } from "../../../src/account/account" import { Auth } from "../../../src/auth" @@ -44,12 +45,8 @@ const layer = Config.layer.pipe( ) const load = () => Effect.runPromise(Config.Service.use((svc) => svc.get()).pipe(Effect.scoped, Effect.provide(layer))) -const clear = () => - Effect.runPromise(Config.Service.use((svc) => svc.invalidate()).pipe(Effect.scoped, Effect.provide(layer))) - describe("kilocode default indexing plugin", () => { afterEach(async () => { - await clear() await disposeAllInstances() }) @@ -76,32 +73,31 @@ describe("kilocode default indexing plugin", () => { }) test("does not hard-enable indexing plugin when default plugins are disabled", async () => { - const prev = process.env["KILO_DISABLE_DEFAULT_PLUGINS"] - process.env["KILO_DISABLE_DEFAULT_PLUGINS"] = "true" + const original = Flag.KILO_DISABLE_DEFAULT_PLUGINS + Flag.KILO_DISABLE_DEFAULT_PLUGINS = true try { await using tmp = await tmpdir({ - init: async (dir) => { - await Filesystem.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", - plugin: ["global-plugin-1"], - }), - ) - }, - }) + init: async (dir) => { + await Filesystem.write( + path.join(dir, "opencode.json"), + JSON.stringify({ + $schema: "https://app.kilo.ai/config.json", + plugin: ["global-plugin-1"], + }), + ) + }, + }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { + await WithInstance.provide({ + directory: tmp.path, + fn: async () => { const config = await load() expect(hasIndexingPlugin(config.plugin ?? [])).toBe(false) }, }) } finally { - if (prev === undefined) delete process.env["KILO_DISABLE_DEFAULT_PLUGINS"] - else process.env["KILO_DISABLE_DEFAULT_PLUGINS"] = prev + Flag.KILO_DISABLE_DEFAULT_PLUGINS = original } }) }) diff --git a/packages/opencode/test/kilocode/config/opentelemetry-default.test.ts b/packages/opencode/test/kilocode/config/opentelemetry-default.test.ts index e3711c417d4..53ab3b1147b 100644 --- a/packages/opencode/test/kilocode/config/opentelemetry-default.test.ts +++ b/packages/opencode/test/kilocode/config/opentelemetry-default.test.ts @@ -1,30 +1,31 @@ // kilocode_change - new file import { describe, expect, test } from "bun:test" import { Config } from "../../../src/config/config" +import { Schema } from "effect" describe("Config.Info experimental.openTelemetry default", () => { test("defaults to true when experimental is set without openTelemetry", () => { - const parsed = Config.Info.zod.parse({ experimental: {} }) + const parsed = Schema.decodeUnknownSync(Config.Info)({ experimental: {} }) expect(parsed.experimental?.openTelemetry).toBe(true) }) test("defaults to true when openTelemetry is explicitly undefined", () => { - const parsed = Config.Info.zod.parse({ experimental: { openTelemetry: undefined } }) + const parsed = Schema.decodeUnknownSync(Config.Info)({ experimental: { openTelemetry: undefined } }) expect(parsed.experimental?.openTelemetry).toBe(true) }) test("respects explicit false", () => { - const parsed = Config.Info.zod.parse({ experimental: { openTelemetry: false } }) + const parsed = Schema.decodeUnknownSync(Config.Info)({ experimental: { openTelemetry: false } }) expect(parsed.experimental?.openTelemetry).toBe(false) }) test("respects explicit true", () => { - const parsed = Config.Info.zod.parse({ experimental: { openTelemetry: true } }) + const parsed = Schema.decodeUnknownSync(Config.Info)({ experimental: { openTelemetry: true } }) expect(parsed.experimental?.openTelemetry).toBe(true) }) test("experimental stays undefined when not set at all", () => { - const parsed = Config.Info.zod.parse({}) + const parsed = Schema.decodeUnknownSync(Config.Info)({}) expect(parsed.experimental).toBeUndefined() }) }) diff --git a/packages/opencode/test/kilocode/config/speech-to-text-config.test.ts b/packages/opencode/test/kilocode/config/speech-to-text-config.test.ts index c4cf66fac8a..32b46a4b1ff 100644 --- a/packages/opencode/test/kilocode/config/speech-to-text-config.test.ts +++ b/packages/opencode/test/kilocode/config/speech-to-text-config.test.ts @@ -1,9 +1,10 @@ import { describe, expect, test } from "bun:test" import { Config } from "../../../src/config/config" +import { Schema } from "effect" describe("Config.Info experimental speech-to-text model", () => { test("parses the selected speech-to-text model", () => { - const parsed = Config.Info.zod.parse({ + const parsed = Schema.decodeUnknownSync(Config.Info)({ experimental: { speech_to_text_model: "openai/gpt-4o-mini-transcribe", }, @@ -13,7 +14,7 @@ describe("Config.Info experimental speech-to-text model", () => { }) test("keeps existing experimental defaults", () => { - const parsed = Config.Info.zod.parse({ experimental: { speech_to_text_model: "google/chirp-3" } }) + const parsed = Schema.decodeUnknownSync(Config.Info)({ experimental: { speech_to_text_model: "google/chirp-3" } }) expect(parsed.experimental?.openTelemetry).toBe(true) }) }) diff --git a/packages/opencode/test/kilocode/cost-propagation.test.ts b/packages/opencode/test/kilocode/cost-propagation.test.ts index 4f2d85dd5aa..ad7d0c87821 100644 --- a/packages/opencode/test/kilocode/cost-propagation.test.ts +++ b/packages/opencode/test/kilocode/cost-propagation.test.ts @@ -69,7 +69,7 @@ describe("KiloCostPropagation.propagate", () => { deltas.map((d) => KiloCostPropagation.propagate(sessions, chat.id, assistant.id, d)), { concurrency: "unbounded" }, ) - const parent = yield* Effect.sync(() => MessageV2.get({ sessionID: chat.id, messageID: assistant.id })) + const parent = yield* MessageV2.get({ sessionID: chat.id, messageID: assistant.id }) expect(parent.info.role).toBe("assistant") if (parent.info.role !== "assistant") return const total = deltas.reduce((a, b) => a + b, 0) @@ -85,7 +85,7 @@ describe("KiloCostPropagation.propagate", () => { const { chat, assistant } = yield* seed() yield* KiloCostPropagation.propagate(sessions, chat.id, assistant.id, 0) yield* KiloCostPropagation.propagate(sessions, chat.id, assistant.id, -1.5) - const parent = yield* Effect.sync(() => MessageV2.get({ sessionID: chat.id, messageID: assistant.id })) + const parent = yield* MessageV2.get({ sessionID: chat.id, messageID: assistant.id }) if (parent.info.role !== "assistant") return expect(parent.info.cost).toBe(0) }), diff --git a/packages/opencode/test/kilocode/custom-provider-delete.test.ts b/packages/opencode/test/kilocode/custom-provider-delete.test.ts index b721485b65c..ae371891d54 100644 --- a/packages/opencode/test/kilocode/custom-provider-delete.test.ts +++ b/packages/opencode/test/kilocode/custom-provider-delete.test.ts @@ -10,11 +10,12 @@ import { describe, expect, it } from "bun:test" import * as Config from "../../src/config/config" +import { Schema } from "effect" import { KilocodeConfig } from "../../src/kilocode/config/config" describe("Config.Info — null sentinels for custom provider deletes", () => { it("accepts a null model value inside a provider", () => { - const parsed = Config.Info.zod.safeParse({ + const parsed = Schema.decodeUnknownResult(Config.Info)({ provider: { myprovider: { name: "My Provider", @@ -24,20 +25,20 @@ describe("Config.Info — null sentinels for custom provider deletes", () => { }, }, }) - expect(parsed.success).toBe(true) + expect(parsed._tag).toBe("Success") }) it("accepts a null provider value", () => { - const parsed = Config.Info.zod.safeParse({ + const parsed = Schema.decodeUnknownResult(Config.Info)({ provider: { myprovider: null, }, }) - expect(parsed.success).toBe(true) + expect(parsed._tag).toBe("Success") }) it("accepts a null variant value inside a model", () => { - const parsed = Config.Info.zod.safeParse({ + const parsed = Schema.decodeUnknownResult(Config.Info)({ provider: { myprovider: { name: "My Provider", @@ -51,7 +52,7 @@ describe("Config.Info — null sentinels for custom provider deletes", () => { }, }, }) - expect(parsed.success).toBe(true) + expect(parsed._tag).toBe("Success") }) }) diff --git a/packages/opencode/test/kilocode/indexing-label.test.ts b/packages/opencode/test/kilocode/indexing-label.test.ts index b07efb4cc98..38114c4ccfd 100644 --- a/packages/opencode/test/kilocode/indexing-label.test.ts +++ b/packages/opencode/test/kilocode/indexing-label.test.ts @@ -11,7 +11,7 @@ describe("indexing label", () => { totalFiles: 50, percent: 42, }), - ).toBe("IDX 42% 21/50") + ).toBe("42% (21/50 files)") }) test("formats indeterminate in-progress status without 0/0 counts", () => { @@ -23,7 +23,19 @@ describe("indexing label", () => { totalFiles: 0, percent: 0, }), - ).toBe("IDX In Progress") + ).toBe("In progress") + }) + + test("formats progress percentage without counts", () => { + expect( + formatIndexingLabel({ + state: "In Progress", + message: "", + processedFiles: 0, + totalFiles: 0, + percent: 42, + }), + ).toBe("42%") }) test("formats error status with backend message", () => { @@ -35,7 +47,7 @@ describe("indexing label", () => { totalFiles: 0, percent: 0, }), - ).toBe("IDX Indexing failed.") + ).toBe("Indexing failed.") }) test("formats stable states", () => { @@ -47,7 +59,7 @@ describe("indexing label", () => { totalFiles: 1, percent: 100, }), - ).toBe("IDX Complete") + ).toBe("Complete") expect( formatIndexingLabel({ state: "Disabled", @@ -56,7 +68,7 @@ describe("indexing label", () => { totalFiles: 0, percent: 0, }), - ).toBe("IDX Disabled") + ).toBe("Disabled") expect( formatIndexingLabel({ state: "Standby", @@ -65,6 +77,6 @@ describe("indexing label", () => { totalFiles: 0, percent: 0, }), - ).toBe("IDX Standby") + ).toBe("Standby") }) }) diff --git a/packages/opencode/test/kilocode/kilo-loader-auth.test.ts b/packages/opencode/test/kilocode/kilo-loader-auth.test.ts index b14ba506781..a53dbd1431f 100644 --- a/packages/opencode/test/kilocode/kilo-loader-auth.test.ts +++ b/packages/opencode/test/kilocode/kilo-loader-auth.test.ts @@ -3,15 +3,17 @@ import { expect } from "bun:test" import { AppFileSystem } from "@opencode-ai/core/filesystem" +import { ModelsDev } from "../../src/provider/models" +import * as CoreModels from "@opencode-ai/core/models" import { Effect, Layer } from "effect" import { FetchHttpClient } from "effect/unstable/http" import { kiloCustomLoaders } from "../../src/kilocode/provider/provider" import { Auth } from "../../src/auth" import { ModelCache } from "../../src/provider/model-cache" -import { ModelsDev } from "../../src/provider/models" import { Provider } from "../../src/provider/provider" import { TestConfig } from "../fixture/config" import { testEffect } from "../lib/effect" +import { provideInstance } from "../fixture/fixture" const input = { id: "kilo", @@ -96,7 +98,15 @@ function layer() { Layer.provide(auth), Layer.provide(models), ) + const core = Layer.succeed( + CoreModels.Service, + CoreModels.Service.of({ + get: () => Effect.succeed(seed), + refresh: () => Effect.void, + }), + ) return Layer.fresh(ModelsDev.layer).pipe( + Layer.provide(core), Layer.provide(FetchHttpClient.layer), Layer.provide(files), Layer.provide(cfg), @@ -109,7 +119,10 @@ const it = testEffect(Layer.empty) it.live("assembles paid Kilo models without auth", () => Effect.gen(function* () { - const providers = yield* ModelsDev.Service.use((models) => models.get()).pipe(Effect.provide(layer())) + const providers = yield* ModelsDev.Service.use((models) => models.get()).pipe( + Effect.provide(layer()), + provideInstance(process.cwd()), + ) const kilo = Provider.fromModelsDevProvider(providers.kilo) expect(kilo.models["paid-model"]).toMatchObject({ @@ -122,7 +135,10 @@ it.live("assembles paid Kilo models without auth", () => it.live("marks zero-cost Kilo models as free when the catalog omits isFree", () => Effect.gen(function* () { - const providers = yield* ModelsDev.Service.use((models) => models.get()).pipe(Effect.provide(layer())) + const providers = yield* ModelsDev.Service.use((models) => models.get()).pipe( + Effect.provide(layer()), + provideInstance(process.cwd()), + ) const kilo = Provider.fromModelsDevProvider(providers.kilo) expect(kilo.models["free-model"].isFree).toBe(true) diff --git a/packages/opencode/test/kilocode/local-model.test.ts b/packages/opencode/test/kilocode/local-model.test.ts index c15c90feb4a..95bca4d0ca3 100644 --- a/packages/opencode/test/kilocode/local-model.test.ts +++ b/packages/opencode/test/kilocode/local-model.test.ts @@ -93,6 +93,7 @@ mock.module("@tui/context/sync", () => ({ provider_default: { anthropic: "claude-sonnet" }, agent: mockAgents, config: mockConfig, + session: [], mcp: {}, }, }), @@ -127,6 +128,9 @@ mock.module("@tui/context/sdk", () => ({ connect: async () => {}, }, }, + event: { + on: () => () => {}, + }, }), })) diff --git a/packages/opencode/test/kilocode/lsp-typescript-lightweight.test.ts b/packages/opencode/test/kilocode/lsp-typescript-lightweight.test.ts index caad026aa17..6baee85611b 100644 --- a/packages/opencode/test/kilocode/lsp-typescript-lightweight.test.ts +++ b/packages/opencode/test/kilocode/lsp-typescript-lightweight.test.ts @@ -10,6 +10,7 @@ import { TsCheck } from "../../src/kilocode/ts-check" import { Flag } from "@opencode-ai/core/flag/flag" import { Instance, type InstanceContext } from "../../src/project/instance" import { disposeAllInstances } from "../fixture/fixture" +import type { RuntimeFlags } from "../../src/effect/runtime-flags" afterEach(async () => { await disposeAllInstances() @@ -17,6 +18,7 @@ afterEach(async () => { // Typescript.spawn doesn't use ctx, so a cast-through is fine for these tests. const fakeCtx = {} as InstanceContext +const fakeFlags = {} as RuntimeFlags.Info describe("typescript lightweight mode", () => { describe("spawn gate", () => { @@ -24,7 +26,7 @@ describe("typescript lightweight mode", () => { const saved = Flag.KILO_EXPERIMENTAL_LSP_TOOL Flag.KILO_EXPERIMENTAL_LSP_TOOL = false try { - const result = await LSPServer.Typescript.spawn("/tmp/any", fakeCtx) + const result = await LSPServer.Typescript.spawn("/tmp/any", fakeCtx, fakeFlags) expect(result).toBeUndefined() } finally { Flag.KILO_EXPERIMENTAL_LSP_TOOL = saved @@ -37,7 +39,7 @@ describe("typescript lightweight mode", () => { const spy = spyOn(TsCheck, "native_tsgo").mockResolvedValue(undefined) try { - const result = await LSPServer.Typescript.spawn("/tmp/any", fakeCtx) + const result = await LSPServer.Typescript.spawn("/tmp/any", fakeCtx, fakeFlags) expect(spy).toHaveBeenCalled() expect(result).toBeUndefined() // undefined because mock returns no binary } finally { @@ -83,7 +85,7 @@ describe("typescript lightweight mode", () => { test("lsp/lsp.ts uses TsClient for lightweight diagnostics", async () => { const src = await Bun.file(path.resolve(import.meta.dir, "../../src/lsp/lsp.ts")).text() expect(src).toContain("TsClient.create") - expect(src).toContain("KILO_EXPERIMENTAL_LSP_TOOL") + expect(src).toContain("flags.experimentalLspTool") }) }) }) diff --git a/packages/opencode/test/kilocode/patch.test.ts b/packages/opencode/test/kilocode/patch.test.ts index 86ef2195467..c44ce53a144 100644 --- a/packages/opencode/test/kilocode/patch.test.ts +++ b/packages/opencode/test/kilocode/patch.test.ts @@ -3,15 +3,55 @@ // legacy single-byte, CJK). // - Plain UTF-8 files do not gain a spurious BOM. // - Moved files keep the original encoding at the new path. -// These round-trip through Patch.applyPatch directly so we exercise the -// encoding + BOM integration in patch/index.ts without the tool stack. +// These round-trip through the apply_patch tool so the Kilo encoding layer is +// exercised with the upstream patch parser. import { afterEach, beforeEach, describe, expect, test } from "bun:test" import fs from "fs/promises" import path from "path" import { tmpdir } from "os" import iconv from "iconv-lite" -import { Patch } from "../../src/patch" +import { Effect, Layer } from "effect" +import { Agent } from "../../src/agent/agent" +import { Bus } from "../../src/bus" +import { Format } from "../../src/format" +import { LSP } from "../../src/lsp/lsp" +import { MessageID, SessionID } from "../../src/session/schema" +import { ApplyPatchTool } from "../../src/tool/apply_patch" +import { Tool } from "../../src/tool/tool" +import { Truncate } from "../../src/tool/truncate" +import { provideInstance } from "../fixture/fixture" +import { AppFileSystem } from "@opencode-ai/core/filesystem" + +const layer = Layer.mergeAll( + Agent.defaultLayer, + AppFileSystem.defaultLayer, + Bus.layer, + Format.defaultLayer, + LSP.defaultLayer, + Truncate.defaultLayer, +) + +const apply = (dir: string, patchText: string) => + Effect.runPromise( + Effect.gen(function* () { + const info = yield* ApplyPatchTool + const tool = yield* Tool.init(info) + yield* tool.execute( + { patchText }, + { + sessionID: SessionID.make("ses_patch"), + messageID: MessageID.make("msg_patch"), + callID: "call_patch", + agent: "code", + abort: AbortSignal.any([]), + messages: [], + metadata: () => Effect.void, + ask: () => Effect.void, + }, + ) + }).pipe(provideInstance(dir), Effect.scoped, Effect.provide(layer)), + ) const UTF8_BOM = Buffer.from([0xef, 0xbb, 0xbf]) const UTF16_LE_BOM = Buffer.from([0xff, 0xfe]) @@ -39,7 +79,7 @@ describe("Patch encoding preservation", () => { +line 2 updated *** End Patch` - await Patch.applyPatch(patch) + await apply(dir, patch) const bytes = await fs.readFile(file) expect(bytes.subarray(0, 3).equals(UTF8_BOM)).toBe(true) @@ -58,7 +98,7 @@ describe("Patch encoding preservation", () => { +line 2 updated *** End Patch` - await Patch.applyPatch(patch) + await apply(dir, patch) const bytes = await fs.readFile(file) expect(bytes[0]).not.toBe(0xef) @@ -77,7 +117,7 @@ describe("Patch encoding preservation", () => { +line 2 updated *** End Patch` - await Patch.applyPatch(patch) + await apply(dir, patch) const bytes = await fs.readFile(file) expect(bytes.subarray(0, 2).equals(UTF16_LE_BOM)).toBe(true) @@ -96,7 +136,7 @@ describe("Patch encoding preservation", () => { +águila *** End Patch` - await Patch.applyPatch(patch) + await apply(dir, patch) const bytes = await fs.readFile(file) expect(iconv.decode(bytes, "iso-8859-1")).toBe("café\náguila\n") @@ -122,7 +162,7 @@ describe("Patch encoding preservation", () => { line3 *** End Patch` - await Patch.applyPatch(patch) + await apply(dir, patch) const bytes = await fs.readFile(file) expect(iconv.decode(bytes, "Shift_JIS")).toBe("line1\nさようなら、世界!\nline3\n") @@ -143,7 +183,7 @@ describe("Patch encoding preservation", () => { +updated *** End Patch` - await Patch.applyPatch(patch) + await apply(dir, patch) const moved = await fs.readFile(to) expect(moved.subarray(0, 3).equals(UTF8_BOM)).toBe(true) @@ -163,10 +203,10 @@ describe("Patch encoding preservation", () => { +hello world *** End Patch` - await Patch.applyPatch(patch) + await apply(dir, patch) const bytes = await fs.readFile(file) expect(bytes[0]).not.toBe(0xef) - expect(bytes.toString("utf-8")).toBe("hello world") + expect(bytes.toString("utf-8")).toBe("hello world\n") }) }) diff --git a/packages/opencode/test/kilocode/permission/external-directory-allow.test.ts b/packages/opencode/test/kilocode/permission/external-directory-allow.test.ts index 3b4c4e6e9a3..f1af05cb648 100644 --- a/packages/opencode/test/kilocode/permission/external-directory-allow.test.ts +++ b/packages/opencode/test/kilocode/permission/external-directory-allow.test.ts @@ -7,6 +7,7 @@ import { Global } from "@opencode-ai/core/global" import { Agent } from "../../../src/agent/agent" import { Bus } from "../../../src/bus" import { Config } from "../../../src/config/config" +import { RuntimeFlags } from "../../../src/effect/runtime-flags" import { Permission } from "../../../src/permission" import { PermissionID } from "../../../src/permission/schema" import { WithInstance } from "../../../src/project/with-instance" @@ -24,6 +25,7 @@ const runtime = ManagedRuntime.make( CrossSpawnSpawner.defaultLayer, AppFileSystem.defaultLayer, Config.defaultLayer, + RuntimeFlags.layer(), Plugin.defaultLayer, Truncate.defaultLayer, Agent.defaultLayer, diff --git a/packages/opencode/test/kilocode/provider-list-failed-state.test.ts b/packages/opencode/test/kilocode/provider-list-failed-state.test.ts index 26b37353d97..9c9acc9611d 100644 --- a/packages/opencode/test/kilocode/provider-list-failed-state.test.ts +++ b/packages/opencode/test/kilocode/provider-list-failed-state.test.ts @@ -7,18 +7,18 @@ import { beforeEach, expect } from "bun:test" import { Effect, Layer } from "effect" import { FetchHttpClient } from "effect/unstable/http" +import type { ModelsDev } from "@opencode-ai/core/models" import * as Log from "@opencode-ai/core/util/log" Log.init({ print: false }) import { Auth } from "../../src/auth" import { ModelCache } from "../../src/provider/model-cache" -import type { Provider } from "../../src/provider/models" import { TestConfig } from "../fixture/config" import { testEffect } from "../lib/effect" type Failure = { kind: "unauthorized" | "network" | "schema" | "http"; status?: number } -type Result = { models: Provider["models"]; error?: Failure } +type Result = { models: ModelsDev.Provider["models"]; error?: Failure } let result: Result = { models: {} } let error: Error | undefined diff --git a/packages/opencode/test/kilocode/scout-codesearch.test.ts b/packages/opencode/test/kilocode/scout-codesearch.test.ts new file mode 100644 index 00000000000..8d0c0a03d5b --- /dev/null +++ b/packages/opencode/test/kilocode/scout-codesearch.test.ts @@ -0,0 +1,34 @@ +import { afterEach, expect } from "bun:test" +import { Effect, Layer } from "effect" +import { Agent } from "../../src/agent/agent" +import { Auth } from "../../src/auth" +import { Config } from "../../src/config/config" +import { RuntimeFlags } from "../../src/effect/runtime-flags" +import { Permission } from "../../src/permission" +import { Plugin } from "../../src/plugin" +import { Provider } from "../../src/provider/provider" +import { Skill } from "../../src/skill" +import { disposeAllInstances } from "../fixture/fixture" +import { testEffect } from "../lib/effect" + +const layer = Agent.layer.pipe( + Layer.provide(Plugin.defaultLayer), + Layer.provide(Provider.defaultLayer), + Layer.provide(Auth.defaultLayer), + Layer.provide(Config.defaultLayer), + Layer.provide(Skill.defaultLayer), + Layer.provide(RuntimeFlags.layer({ experimentalScout: true })), +) +const it = testEffect(layer) + +afterEach(async () => { + await disposeAllInstances() +}) + +it.instance("keeps codesearch available to Scout", () => + Effect.gen(function* () { + const scout = yield* Agent.Service.use((service) => service.get("scout")) + expect(scout).toBeDefined() + expect(Permission.evaluate("codesearch", "*", scout!.permission).action).toBe("allow") + }), +) diff --git a/packages/opencode/test/kilocode/server/httpapi-kilo-edit.test.ts b/packages/opencode/test/kilocode/server/httpapi-kilo-edit.test.ts index 2223330b29b..0b63dc05130 100644 --- a/packages/opencode/test/kilocode/server/httpapi-kilo-edit.test.ts +++ b/packages/opencode/test/kilocode/server/httpapi-kilo-edit.test.ts @@ -4,7 +4,7 @@ import { HttpRouter } from "effect/unstable/http" import { HEADER_FEATURE, HEADER_ORGANIZATIONID } from "@kilocode/kilo-gateway" import * as Log from "@opencode-ai/core/util/log" import { KiloGatewayPaths } from "../../../src/kilocode/server/httpapi/groups/kilo-gateway" -import { ExperimentalHttpApiServer } from "../../../src/server/routes/instance/httpapi/server" +import * as HttpApiServer from "../../../src/server/routes/instance/httpapi/server" import { resetDatabase } from "../../fixture/db" import { disposeAllInstances, tmpdir } from "../../fixture/fixture" @@ -30,7 +30,7 @@ const edit = { function app() { const handler = HttpRouter.toWebHandler( - ExperimentalHttpApiServer.routes.pipe(Layer.provide(ConfigProvider.layer(ConfigProvider.fromUnknown({})))), + HttpApiServer.routes.pipe(Layer.provide(ConfigProvider.layer(ConfigProvider.fromUnknown({})))), { disableLogger: true }, ).handler @@ -38,7 +38,7 @@ function app() { request(input: string | URL | Request, init?: RequestInit) { return handler( input instanceof Request ? input : new Request(new URL(input, "http://localhost"), init), - ExperimentalHttpApiServer.context, + HttpApiServer.context, ) }, } diff --git a/packages/opencode/test/kilocode/session-compaction-cap.test.ts b/packages/opencode/test/kilocode/session-compaction-cap.test.ts index abe58dbcef4..f38dd97e393 100644 --- a/packages/opencode/test/kilocode/session-compaction-cap.test.ts +++ b/packages/opencode/test/kilocode/session-compaction-cap.test.ts @@ -8,9 +8,11 @@ import { describe, expect } from "bun:test" import { Deferred, Effect, Layer } from "effect" import { FetchHttpClient } from "effect/unstable/http" import { Agent as AgentSvc } from "../../src/agent/agent" +import { BackgroundJob } from "../../src/background/job" import { Bus } from "../../src/bus" import { Command } from "../../src/command" import { Config } from "../../src/config/config" +import { RuntimeFlags } from "../../src/effect/runtime-flags" import * as CrossSpawnSpawner from "@opencode-ai/core/cross-spawn-spawner" import { Env } from "../../src/env" import { Ripgrep } from "../../src/file/ripgrep" @@ -128,6 +130,7 @@ const infra = Layer.mergeAll(NodeFileSystem.layer, CrossSpawnSpawner.defaultLaye function makeHttp() { const deps = Layer.mergeAll( Session.defaultLayer, + BackgroundJob.defaultLayer, Snapshot.defaultLayer, LLM.defaultLayer, Env.defaultLayer, @@ -136,6 +139,7 @@ function makeHttp() { Permission.defaultLayer, plugin, Config.defaultLayer, + RuntimeFlags.layer(), ProviderSvc.defaultLayer, lsp, mcp, diff --git a/packages/opencode/test/kilocode/session-compaction-chunks.test.ts b/packages/opencode/test/kilocode/session-compaction-chunks.test.ts index 3abe10d4b5a..8c5ecf12054 100644 --- a/packages/opencode/test/kilocode/session-compaction-chunks.test.ts +++ b/packages/opencode/test/kilocode/session-compaction-chunks.test.ts @@ -4,6 +4,7 @@ import * as Stream from "effect/Stream" import { Agent } from "../../src/agent/agent" import { Bus } from "../../src/bus" import { Config } from "../../src/config/config" +import { RuntimeFlags } from "../../src/effect/runtime-flags" import { Image } from "../../src/image/image" import { Permission } from "../../src/permission" import { Plugin } from "../../src/plugin" @@ -219,11 +220,12 @@ function runtime(layer: Layer.Layer, context = 7_000) { Layer.provide(Agent.defaultLayer), Layer.provide(Plugin.defaultLayer), Layer.provide(SyncEvent.defaultLayer), + Layer.provide(RuntimeFlags.layer()), Layer.provide(status), Layer.provide(bus), Layer.provide( Layer.mock(Config.Service)({ - get: () => Effect.succeed({ ...Config.Info.zod.parse({}), compaction: { reserved: 1_000 } }), + get: () => Effect.succeed({ ...{}, compaction: { reserved: 1_000 } }), }), ), ), @@ -284,10 +286,11 @@ function fakeRuntime() { Layer.provide(Agent.defaultLayer), Layer.provide(Plugin.defaultLayer), Layer.provide(SyncEvent.defaultLayer), + Layer.provide(RuntimeFlags.layer()), Layer.provide(bus), Layer.provide( Layer.mock(Config.Service)({ - get: () => Effect.succeed({ ...Config.Info.zod.parse({}), compaction: { reserved: 1_000 } }), + get: () => Effect.succeed({ ...{}, compaction: { reserved: 1_000 } }), }), ), ), @@ -314,11 +317,12 @@ function liveRuntime(layer: Layer.Layer, context = 10_000) { Layer.provide(Agent.defaultLayer), Layer.provide(Plugin.defaultLayer), Layer.provide(SyncEvent.defaultLayer), + Layer.provide(RuntimeFlags.layer()), Layer.provide(status), Layer.provide(bus), Layer.provide( Layer.mock(Config.Service)({ - get: () => Effect.succeed({ ...Config.Info.zod.parse({}), compaction: { reserved: 1_000 } }), + get: () => Effect.succeed({ ...{}, compaction: { reserved: 1_000 } }), }), ), ), @@ -372,7 +376,13 @@ describe("KiloCompactionChunks", () => { const second = await user(session.id, "second " + "c".repeat(10_000)) await assistant(session.id, second.id, tmp.path, "reply " + "d".repeat(10_000)) await Effect.runPromise( - KiloSessionCompaction.create({ session: store, sessionID: session.id, agent: "build", model: ref, auto: false }), + KiloSessionCompaction.create({ + session: store, + sessionID: session.id, + agent: "build", + model: ref, + auto: false, + }), ) const { rt, calls } = fakeRuntime() @@ -420,7 +430,13 @@ describe("KiloCompactionChunks", () => { const second = await user(session.id, "second " + "c".repeat(10_000)) await assistant(session.id, second.id, tmp.path, "reply " + "d".repeat(10_000)) await Effect.runPromise( - KiloSessionCompaction.create({ session: store, sessionID: session.id, agent: "build", model: ref, auto: false }), + KiloSessionCompaction.create({ + session: store, + sessionID: session.id, + agent: "build", + model: ref, + auto: false, + }), ) const { rt, calls } = fakeRuntime() @@ -458,7 +474,13 @@ describe("KiloCompactionChunks", () => { const first = await user(session.id, "first " + "a".repeat(20_000)) await assistant(session.id, first.id, tmp.path, "reply " + "b".repeat(20_000)) await Effect.runPromise( - KiloSessionCompaction.create({ session: store, sessionID: session.id, agent: "build", model: ref, auto: false }), + KiloSessionCompaction.create({ + session: store, + sessionID: session.id, + agent: "build", + model: ref, + auto: false, + }), ) const { rt, calls } = fakeRuntime() @@ -503,7 +525,13 @@ describe("KiloCompactionChunks", () => { const session = await svc.create({}) const first = await user(session.id, "single huge request " + "a".repeat(80_000)) await Effect.runPromise( - KiloSessionCompaction.create({ session: store, sessionID: session.id, agent: "build", model: ref, auto: false }), + KiloSessionCompaction.create({ + session: store, + sessionID: session.id, + agent: "build", + model: ref, + auto: false, + }), ) const { rt, calls } = fakeRuntime() @@ -543,7 +571,13 @@ describe("KiloCompactionChunks", () => { const first = await user(session.id, "first " + "a".repeat(1_000)) await assistant(session.id, first.id, tmp.path, "reply " + "b".repeat(1_000)) await Effect.runPromise( - KiloSessionCompaction.create({ session: store, sessionID: session.id, agent: "build", model: ref, auto: false }), + KiloSessionCompaction.create({ + session: store, + sessionID: session.id, + agent: "build", + model: ref, + auto: false, + }), ) try { diff --git a/packages/opencode/test/kilocode/session-message-metadata.test.ts b/packages/opencode/test/kilocode/session-message-metadata.test.ts index 785ae99dc6f..4a8423b2305 100644 --- a/packages/opencode/test/kilocode/session-message-metadata.test.ts +++ b/packages/opencode/test/kilocode/session-message-metadata.test.ts @@ -1,4 +1,3 @@ - import { describe, expect, test } from "bun:test" import { MessageV2 } from "../../src/session/message-v2" import { SessionID, MessageID, PartID } from "../../src/session/schema" diff --git a/packages/opencode/test/kilocode/session-overflow.test.ts b/packages/opencode/test/kilocode/session-overflow.test.ts index 201ab85454c..e5655a85c18 100644 --- a/packages/opencode/test/kilocode/session-overflow.test.ts +++ b/packages/opencode/test/kilocode/session-overflow.test.ts @@ -1,4 +1,5 @@ import { describe, expect, test } from "bun:test" +import { Schema } from "effect" import type { ModelMessage } from "ai" import { Config } from "@/config/config" import type { Provider } from "@/provider/provider" @@ -7,8 +8,15 @@ import { KiloSessionOverflow } from "@/kilocode/session/overflow" import type { MessageV2 } from "@/session/message-v2" import { isOverflow, usable } from "@/session/overflow" -function cfg(compaction?: Config.Info["compaction"]) { - return Config.Info.zod.parse({ compaction }) +function cfg(compaction?: Config.Info["compaction"]): Config.Info { + const config = Schema.decodeUnknownSync(Config.Info)({ compaction }) + return { + ...config, + skills: config.skills && { + paths: config.skills.paths && [...config.skills.paths], + urls: config.skills.urls && [...config.skills.urls], + }, + } } function model(opts: { context: number; output: number; input?: number }): Provider.Model { diff --git a/packages/opencode/test/kilocode/session-processor-empty-tool-calls.test.ts b/packages/opencode/test/kilocode/session-processor-empty-tool-calls.test.ts index 75949c0c383..b318976bcaf 100644 --- a/packages/opencode/test/kilocode/session-processor-empty-tool-calls.test.ts +++ b/packages/opencode/test/kilocode/session-processor-empty-tool-calls.test.ts @@ -6,6 +6,7 @@ import path from "path" import { Agent as AgentSvc } from "../../src/agent/agent" import { Bus } from "../../src/bus" import { Config } from "../../src/config/config" +import { RuntimeFlags } from "../../src/effect/runtime-flags" import { Image } from "../../src/image/image" import { Permission } from "../../src/permission" import { Plugin } from "../../src/plugin" @@ -102,6 +103,7 @@ const deps = Layer.mergeAll( Permission.defaultLayer, Plugin.defaultLayer, Config.defaultLayer, + RuntimeFlags.layer(), SessionSummary.defaultLayer, Image.defaultLayer, SyncEvent.defaultLayer, diff --git a/packages/opencode/test/kilocode/session-processor-network-offline.test.ts b/packages/opencode/test/kilocode/session-processor-network-offline.test.ts index 2dd18ff5dca..abdb5fc55d8 100644 --- a/packages/opencode/test/kilocode/session-processor-network-offline.test.ts +++ b/packages/opencode/test/kilocode/session-processor-network-offline.test.ts @@ -6,6 +6,7 @@ import path from "path" import { Agent as AgentSvc } from "../../src/agent/agent" import { Bus } from "../../src/bus" import { Config } from "../../src/config/config" +import { RuntimeFlags } from "../../src/effect/runtime-flags" import { Image } from "../../src/image/image" import { Permission } from "../../src/permission" import { Plugin } from "../../src/plugin" @@ -101,6 +102,7 @@ const deps = Layer.mergeAll( Permission.defaultLayer, Plugin.defaultLayer, Config.defaultLayer, + RuntimeFlags.layer(), SessionSummary.defaultLayer, Image.defaultLayer, SyncEvent.defaultLayer, diff --git a/packages/opencode/test/kilocode/session-processor-retry-limit.test.ts b/packages/opencode/test/kilocode/session-processor-retry-limit.test.ts index 91944a86bc7..827e9c0cf38 100644 --- a/packages/opencode/test/kilocode/session-processor-retry-limit.test.ts +++ b/packages/opencode/test/kilocode/session-processor-retry-limit.test.ts @@ -13,6 +13,7 @@ import path from "path" import { Agent as AgentSvc } from "../../src/agent/agent" import { Bus } from "../../src/bus" import { Config } from "../../src/config/config" +import { RuntimeFlags } from "../../src/effect/runtime-flags" import { Image } from "../../src/image/image" import { Permission } from "../../src/permission" import { Plugin } from "../../src/plugin" @@ -114,6 +115,7 @@ const deps = Layer.mergeAll( Permission.defaultLayer, Plugin.defaultLayer, Config.defaultLayer, + RuntimeFlags.layer(), SessionSummary.defaultLayer, Image.defaultLayer, SyncEvent.defaultLayer, diff --git a/packages/opencode/test/kilocode/session-prompt-compaction-safety.test.ts b/packages/opencode/test/kilocode/session-prompt-compaction-safety.test.ts index ffb4e394fa3..22c1cae000b 100644 --- a/packages/opencode/test/kilocode/session-prompt-compaction-safety.test.ts +++ b/packages/opencode/test/kilocode/session-prompt-compaction-safety.test.ts @@ -7,9 +7,11 @@ import { describe, expect } from "bun:test" import { Effect, Layer } from "effect" import { FetchHttpClient } from "effect/unstable/http" import { Agent as AgentSvc } from "../../src/agent/agent" +import { BackgroundJob } from "../../src/background/job" import { Bus } from "../../src/bus" import { Command } from "../../src/command" import { Config } from "../../src/config/config" +import { RuntimeFlags } from "../../src/effect/runtime-flags" import * as CrossSpawnSpawner from "@opencode-ai/core/cross-spawn-spawner" import { Env } from "../../src/env" import { Ripgrep } from "../../src/file/ripgrep" @@ -121,6 +123,7 @@ const infra = Layer.mergeAll(NodeFileSystem.layer, CrossSpawnSpawner.defaultLaye function makeHttp() { const deps = Layer.mergeAll( Session.defaultLayer, + BackgroundJob.defaultLayer, Snapshot.defaultLayer, LLM.defaultLayer, Env.defaultLayer, @@ -129,10 +132,12 @@ function makeHttp() { Permission.defaultLayer, plugin, Config.defaultLayer, + RuntimeFlags.layer(), ProviderSvc.defaultLayer, lsp, mcp, AppFileSystem.defaultLayer, + Reference.defaultLayer, SyncEvent.defaultLayer, status, ).pipe(Layer.provideMerge(infra)) @@ -173,7 +178,9 @@ function makeHttp() { Layer.provide(SystemPrompt.defaultLayer), Layer.provideMerge(deps), ), - ).pipe(Layer.provide(summary)) + ).pipe( + Layer.provide(Layer.mergeAll(summary, deps, Config.defaultLayer, RuntimeFlags.layer(), BackgroundJob.defaultLayer)), + ) } const it = testEffect(makeHttp()) diff --git a/packages/opencode/test/kilocode/session-prompt-permission-refresh.test.ts b/packages/opencode/test/kilocode/session-prompt-permission-refresh.test.ts index cac14071d4c..168977c9ac1 100644 --- a/packages/opencode/test/kilocode/session-prompt-permission-refresh.test.ts +++ b/packages/opencode/test/kilocode/session-prompt-permission-refresh.test.ts @@ -7,9 +7,11 @@ import { AppFileSystem } from "@opencode-ai/core/filesystem" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import * as Log from "@opencode-ai/core/util/log" import { Agent as AgentSvc } from "../../src/agent/agent" +import { BackgroundJob } from "../../src/background/job" import { Bus } from "../../src/bus" import { Command } from "../../src/command" import { Config } from "../../src/config/config" +import { RuntimeFlags } from "../../src/effect/runtime-flags" import { Env } from "../../src/env" import { Format } from "../../src/format" import { Git } from "../../src/git" @@ -115,6 +117,7 @@ const infra = Layer.mergeAll(NodeFileSystem.layer, CrossSpawnSpawner.defaultLaye function makeHttp() { const deps = Layer.mergeAll( Session.defaultLayer, + BackgroundJob.defaultLayer, Snapshot.defaultLayer, LLM.defaultLayer, Env.defaultLayer, @@ -123,10 +126,12 @@ function makeHttp() { Permission.defaultLayer, Plugin.defaultLayer, Config.defaultLayer, + RuntimeFlags.layer(), ProviderSvc.defaultLayer, lsp, mcp, AppFileSystem.defaultLayer, + Reference.defaultLayer, SyncEvent.defaultLayer, status, ).pipe(Layer.provideMerge(infra)) @@ -162,11 +167,14 @@ function makeHttp() { Layer.provideMerge(proc), Layer.provideMerge(registry), Layer.provideMerge(trunc), + Layer.provideMerge(question), Layer.provide(Instruction.defaultLayer), Layer.provide(SystemPrompt.defaultLayer), Layer.provideMerge(deps), ), - ).pipe(Layer.provide(summary)) + ).pipe( + Layer.provide(Layer.mergeAll(summary, deps, Config.defaultLayer, RuntimeFlags.layer(), BackgroundJob.defaultLayer)), + ) } const it = testEffect(makeHttp()) diff --git a/packages/opencode/test/kilocode/session-share.test.ts b/packages/opencode/test/kilocode/session-share.test.ts index 280c0147e6f..4f83bee74d7 100644 --- a/packages/opencode/test/kilocode/session-share.test.ts +++ b/packages/opencode/test/kilocode/session-share.test.ts @@ -3,13 +3,16 @@ import { Effect, Layer } from "effect" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { Auth } from "../../src/auth" import { Config } from "../../src/config/config" +import { RuntimeFlags } from "../../src/effect/runtime-flags" import { Session } from "../../src/session/session" import { SessionShare } from "../../src/share/session" import { Storage } from "../../src/storage/storage" import { SyncEvent } from "../../src/sync" import { testEffect } from "../lib/effect" -const it = testEffect(Layer.mergeAll(Auth.defaultLayer, Storage.defaultLayer, CrossSpawnSpawner.defaultLayer)) +const it = testEffect( + Layer.mergeAll(Auth.defaultLayer, Storage.defaultLayer, CrossSpawnSpawner.defaultLayer, RuntimeFlags.layer()), +) const layer = SessionShare.layer.pipe( Layer.provideMerge(Session.defaultLayer), diff --git a/packages/opencode/test/kilocode/session/session.test.ts b/packages/opencode/test/kilocode/session/session.test.ts index b6ced5c244d..323b7091b98 100644 --- a/packages/opencode/test/kilocode/session/session.test.ts +++ b/packages/opencode/test/kilocode/session/session.test.ts @@ -5,6 +5,8 @@ import { Bus } from "../../../src/bus" import * as Log from "@opencode-ai/core/util/log" import { WithInstance } from "../../../src/project/with-instance" import { AppRuntime } from "../../../src/effect/app-runtime" +import { RuntimeFlags } from "../../../src/effect/runtime-flags" +import { Effect } from "effect" import { tmpdir } from "../../fixture/fixture" import type { SessionID } from "../../../src/session/schema" @@ -31,12 +33,15 @@ describe("session.created event", () => { let eventReceived = false let receivedInfo: SessionNs.Info | undefined + const title = `created-event-${Date.now()}` const unsub = Bus.subscribe(SessionNs.Event.Created, (event) => { + const info = event.properties.info as SessionNs.Info + if (info.title !== title) return eventReceived = true - receivedInfo = event.properties.info as SessionNs.Info + receivedInfo = info }) - const info = await create({}) + const info = await create({ title }) await new Promise((resolve) => setTimeout(resolve, 100)) unsub() @@ -54,31 +59,43 @@ describe("session.created event", () => { }) test("session.created event should be emitted before session.updated", async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const events: string[] = [] - - const unsubCreated = Bus.subscribe(SessionNs.Event.Created, () => { - events.push("created") - }) - - const unsubUpdated = Bus.subscribe(SessionNs.Event.Updated, () => { - events.push("updated") - }) - - const info = await create({}) - await new Promise((resolve) => setTimeout(resolve, 100)) - unsubCreated() - unsubUpdated() - - expect(events).toContain("created") - expect(events).toContain("updated") - expect(events.indexOf("created")).toBeLessThan(events.indexOf("updated")) - - await remove(info.id) - }, - }) + const previous = process.env.KILO_EXPERIMENTAL_WORKSPACES + delete process.env.KILO_EXPERIMENTAL_WORKSPACES + try { + await WithInstance.provide({ + directory: projectRoot, + fn: async () => { + const flags = AppRuntime.runSync(Effect.service(RuntimeFlags.Service)) + const enabled = flags.experimentalWorkspaces + Object.assign(flags, { experimentalWorkspaces: false }) + const events: string[] = [] + const title = `event-order-${Date.now()}` + + const unsubCreated = Bus.subscribe(SessionNs.Event.Created, (event) => { + if (event.properties.info.title === title) events.push("created") + }) + + const unsubUpdated = Bus.subscribe(SessionNs.Event.Updated, (event) => { + if (event.properties.info.title === title) events.push("updated") + }) + + const info = await create({ title }) + await new Promise((resolve) => setTimeout(resolve, 100)) + unsubCreated() + unsubUpdated() + + expect(events).toContain("created") + expect(events).toContain("updated") + expect(events.indexOf("created")).toBeLessThan(events.indexOf("updated")) + + await remove(info.id) + Object.assign(flags, { experimentalWorkspaces: enabled }) + }, + }) + } finally { + if (previous === undefined) delete process.env.KILO_EXPERIMENTAL_WORKSPACES + else process.env.KILO_EXPERIMENTAL_WORKSPACES = previous + } }) }) diff --git a/packages/opencode/test/kilocode/task-nesting.test.ts b/packages/opencode/test/kilocode/task-nesting.test.ts index e8216daa5bf..912e4b554e3 100644 --- a/packages/opencode/test/kilocode/task-nesting.test.ts +++ b/packages/opencode/test/kilocode/task-nesting.test.ts @@ -1,7 +1,12 @@ import { afterEach, describe, expect, test } from "bun:test" import { Effect, Exit, Layer } from "effect" import { Agent } from "../../src/agent/agent" +import { BackgroundJob } from "../../src/background/job" +import { Bus } from "../../src/bus" +import { SessionRunState } from "../../src/session/run-state" +import { SessionStatus } from "../../src/session/status" import { Config } from "../../src/config/config" +import { RuntimeFlags } from "../../src/effect/runtime-flags" import * as CrossSpawnSpawner from "@opencode-ai/core/cross-spawn-spawner" import { Session } from "../../src/session/session" import { MessageV2 } from "../../src/session/message-v2" @@ -24,7 +29,12 @@ const ref = { const it = testEffect( Layer.mergeAll( Agent.defaultLayer, + BackgroundJob.defaultLayer, + Bus.defaultLayer, Config.defaultLayer, + RuntimeFlags.layer(), + SessionRunState.defaultLayer, + SessionStatus.defaultLayer, CrossSpawnSpawner.defaultLayer, Session.defaultLayer, Truncate.defaultLayer, @@ -67,40 +77,42 @@ const seed = Effect.fn("NestedTaskToolTest.seed")(function* () { }) function stubOps(opts?: { onPrompt?: (input: SessionPrompt.PromptInput) => void }): TaskPromptOps { + const prompt = (input: SessionPrompt.PromptInput) => + Effect.sync(() => { + opts?.onPrompt?.(input) + const id = MessageID.ascending() + return { + info: { + id, + role: "assistant", + parentID: input.messageID ?? MessageID.ascending(), + sessionID: input.sessionID, + mode: input.agent ?? "general", + agent: input.agent ?? "general", + cost: 0, + path: { cwd: "/tmp", root: "/tmp" }, + tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }, + modelID: ref.modelID, + providerID: ref.providerID, + time: { created: Date.now() }, + finish: "stop", + }, + parts: [ + { + id: PartID.ascending(), + messageID: id, + sessionID: input.sessionID, + type: "text", + text: "done", + }, + ], + } satisfies MessageV2.WithParts + }) return { cancel: () => Effect.void, resolvePromptParts: (template) => Effect.succeed([{ type: "text" as const, text: template }]), - prompt: (input) => - Effect.sync(() => { - opts?.onPrompt?.(input) - const id = MessageID.ascending() - return { - info: { - id, - role: "assistant", - parentID: input.messageID ?? MessageID.ascending(), - sessionID: input.sessionID, - mode: input.agent ?? "general", - agent: input.agent ?? "general", - cost: 0, - path: { cwd: "/tmp", root: "/tmp" }, - tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }, - modelID: ref.modelID, - providerID: ref.providerID, - time: { created: Date.now() }, - finish: "stop", - }, - parts: [ - { - id: PartID.ascending(), - messageID: id, - sessionID: input.sessionID, - type: "text", - text: "done", - }, - ], - } satisfies MessageV2.WithParts - }), + prompt, + loop: (input) => prompt({ sessionID: input.sessionID, parts: [] }), } } diff --git a/packages/opencode/test/kilocode/tool-registry-indexing.test.ts b/packages/opencode/test/kilocode/tool-registry-indexing.test.ts index 081ea88467f..90a6baf096d 100644 --- a/packages/opencode/test/kilocode/tool-registry-indexing.test.ts +++ b/packages/opencode/test/kilocode/tool-registry-indexing.test.ts @@ -42,6 +42,7 @@ describe("kilocode tool registry indexing", () => { expect(ids).toContain("question") expect(ids).toContain("read") expect(ids).toContain("suggest") + expect(ids).toContain("codesearch") expect(avail).not.toHaveBeenCalled() } finally { avail.mockRestore() diff --git a/packages/opencode/test/kilocode/tool-task-model.test.ts b/packages/opencode/test/kilocode/tool-task-model.test.ts index 58d69033976..1bf091152d1 100644 --- a/packages/opencode/test/kilocode/tool-task-model.test.ts +++ b/packages/opencode/test/kilocode/tool-task-model.test.ts @@ -3,7 +3,12 @@ import { Effect, Layer } from "effect" import fs from "fs/promises" import path from "path" import { Agent } from "../../src/agent/agent" +import { BackgroundJob } from "../../src/background/job" +import { Bus } from "../../src/bus" +import { SessionRunState } from "../../src/session/run-state" +import { SessionStatus } from "../../src/session/status" import { Config } from "../../src/config/config" +import { RuntimeFlags } from "../../src/effect/runtime-flags" import * as CrossSpawnSpawner from "@opencode-ai/core/cross-spawn-spawner" import { Global } from "@opencode-ai/core/global" import { Instance } from "../../src/project/instance" @@ -91,7 +96,12 @@ const catalog = { const it = testEffect( Layer.mergeAll( Agent.defaultLayer, + BackgroundJob.defaultLayer, + Bus.defaultLayer, Config.defaultLayer, + RuntimeFlags.layer(), + SessionRunState.defaultLayer, + SessionStatus.defaultLayer, CrossSpawnSpawner.defaultLayer, Session.defaultLayer, Truncate.defaultLayer, @@ -130,14 +140,16 @@ const seed = Effect.fn("TaskToolModelTest.seed")(function* (title = "Parent") { }) function stubOps(opts?: { onPrompt?: (input: SessionPrompt.PromptInput) => void; text?: string }): TaskPromptOps { + const prompt = (input: SessionPrompt.PromptInput) => + Effect.sync(() => { + opts?.onPrompt?.(input) + return reply(input, opts?.text ?? "done") + }) return { cancel: () => Effect.void, resolvePromptParts: (template) => Effect.succeed([{ type: "text" as const, text: template }]), - prompt: (input) => - Effect.sync(() => { - opts?.onPrompt?.(input) - return reply(input, opts?.text ?? "done") - }), + prompt, + loop: (input) => prompt({ sessionID: input.sessionID, parts: [] }), } } diff --git a/packages/opencode/test/kilocode/tui-sync-event.test.ts b/packages/opencode/test/kilocode/tui-sync-event.test.ts new file mode 100644 index 00000000000..b3814c52528 --- /dev/null +++ b/packages/opencode/test/kilocode/tui-sync-event.test.ts @@ -0,0 +1,130 @@ +/** @jsxImportSource @opentui/solid */ +import { describe, expect, test } from "bun:test" +import type { GlobalEvent } from "@kilocode/sdk/v2" +import { normalizeSyncEvent } from "../../src/cli/cmd/tui/context/event" +import { mount, wait } from "../cli/cmd/tui/sync-fixture" + +describe("TUI sync event wire format", () => { + test("normalizes the runtime sync envelope", () => { + const event = normalizeSyncEvent({ + type: "sync", + syncEvent: { + type: "message.part.updated.1", + id: "evt_1", + seq: 3, + aggregateID: "ses_1", + data: { + sessionID: "ses_1", + part: { + id: "prt_1", + sessionID: "ses_1", + messageID: "msg_1", + type: "text", + text: "response", + }, + time: 1, + }, + }, + }) + + expect(event?.type).toBe("sync") + expect(event?.name).toBe("message.part.updated.1") + expect(event?.id).toBe("evt_1") + expect(event?.seq).toBe(3) + expect(String(event?.aggregateID)).toBe("ses_1") + if (event?.name !== "message.part.updated.1") throw new Error("Expected message part update") + expect(event.data.part).toMatchObject({ + id: "prt_1", + sessionID: "ses_1", + messageID: "msg_1", + type: "text", + text: "response", + }) + }) + + test("preserves generated SDK sync payloads", () => { + const payload = { + type: "sync", + name: "message.removed.1", + id: "evt_2", + seq: 4, + aggregateID: "sessionID", + data: { sessionID: "ses_1", messageID: "msg_1" }, + } as const + + expect(normalizeSyncEvent(payload)).toBe(payload) + }) + + test("ignores non-sync events", () => { + expect(normalizeSyncEvent({ type: "session.status" })).toBeUndefined() + }) + + test("applies runtime sync envelopes to the TUI message store", async () => { + const { app, emit, sync } = await mount() + const sessionID = "ses_wire" + const messageID = "msg_wire" + + try { + emit({ + directory: "/tmp/opencode/packages/opencode", + project: "proj_test", + payload: { + type: "sync", + syncEvent: { + type: "message.updated.1", + id: "evt_message", + seq: 0, + aggregateID: sessionID, + data: { + sessionID, + info: { + id: messageID, + sessionID, + role: "assistant", + parentID: "msg_parent", + mode: "code", + agent: "code", + path: { cwd: "/tmp/opencode", root: "/tmp/opencode" }, + cost: 0, + tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }, + modelID: "kilo-auto/free", + providerID: "kilo", + time: { created: 1 }, + }, + }, + }, + }, + } as unknown as GlobalEvent) + emit({ + directory: "/tmp/opencode/packages/opencode", + project: "proj_test", + payload: { + type: "sync", + syncEvent: { + type: "message.part.updated.1", + id: "evt_part", + seq: 1, + aggregateID: sessionID, + data: { + sessionID, + part: { + id: "prt_wire", + sessionID, + messageID, + type: "text", + text: "rendered response", + }, + time: 2, + }, + }, + }, + } as unknown as GlobalEvent) + + await wait(() => sync.data.part[messageID]?.[0]?.type === "text") + expect(sync.data.message[sessionID]?.[0]?.id).toBe(messageID) + expect(sync.data.part[messageID]?.[0]).toMatchObject({ type: "text", text: "rendered response" }) + } finally { + app.renderer.destroy() + } + }) +}) diff --git a/packages/opencode/test/lib/effect.ts b/packages/opencode/test/lib/effect.ts index a4142b44a4b..ca95a00edfe 100644 --- a/packages/opencode/test/lib/effect.ts +++ b/packages/opencode/test/lib/effect.ts @@ -1,5 +1,5 @@ import { test, type TestOptions } from "bun:test" -import { Cause, Effect, Exit, Layer } from "effect" +import { Cause, Duration, Effect, Exit, Layer } from "effect" import type * as Scope from "effect/Scope" import * as TestClock from "effect/testing/TestClock" import * as TestConsole from "effect/testing/TestConsole" @@ -114,3 +114,33 @@ export const testEffect = (layer: Layer.Layer) => { return make(Layer.provideMerge(full, testEnv), Layer.provideMerge(full, liveEnv)) } // kilocode_change end + +export const awaitWithTimeout = ( + self: Effect.Effect, + message: string, + duration: Duration.Input = "2 seconds", +) => + self.pipe( + Effect.timeoutOrElse({ + duration, + orElse: () => Effect.fail(new Error(message)), + }), + ) + +export const pollWithTimeout = ( + self: Effect.Effect, + message: string, + duration: Duration.Input = "5 seconds", +) => + Effect.gen(function* () { + while (true) { + const result = yield* self + if (result !== undefined) return result + yield* Effect.sleep("20 millis") + } + }).pipe( + Effect.timeoutOrElse({ + duration, + orElse: () => Effect.fail(new Error(message)), + }), + ) diff --git a/packages/opencode/test/lsp/index.test.ts b/packages/opencode/test/lsp/index.test.ts index ce29e04beee..8520afae21e 100644 --- a/packages/opencode/test/lsp/index.test.ts +++ b/packages/opencode/test/lsp/index.test.ts @@ -2,6 +2,8 @@ import { describe, expect, spyOn, test } from "bun:test" import path from "path" import fs from "fs/promises" import { Effect, Layer } from "effect" +import { Config } from "@/config/config" +import { RuntimeFlags } from "@/effect/runtime-flags" import { LSP } from "@/lsp/lsp" import * as LSPServer from "@/lsp/server" import * as launch from "../../src/lsp/launch" // kilocode_change - spy on spawn @@ -15,8 +17,15 @@ import { TsCheck } from "../../src/kilocode/ts-check" // kilocode_change // kilocode_change - Typescript.spawn ignores ctx, so a cast is fine here. const fakeCtx = {} as InstanceContext +const fakeFlags = {} as RuntimeFlags.Info const it = testEffect(Layer.mergeAll(LSP.defaultLayer, CrossSpawnSpawner.defaultLayer)) +const experimentalTyIt = testEffect( + Layer.mergeAll( + LSP.layer.pipe(Layer.provide(Config.defaultLayer), Layer.provide(RuntimeFlags.layer({ experimentalLspTy: true }))), + CrossSpawnSpawner.defaultLayer, + ), +) describe("lsp.spawn", () => { it.live("does not spawn builtin LSP for files outside instance", () => @@ -64,14 +73,22 @@ describe("lsp.spawn", () => { ), ) - // kilocode_change start - enable flag so spawn() is reached past the TsClient short-circuit - it.live("would spawn builtin LSP for files inside instance when lsp is true", () => + // kilocode_change start - provide the runtime flag so spawn() is reached past the TsClient short-circuit + const experimentalToolIt = testEffect( + Layer.mergeAll( + LSP.layer.pipe( + Layer.provide(Config.defaultLayer), + Layer.provide(RuntimeFlags.layer({ experimentalLspTool: true })), + ), + CrossSpawnSpawner.defaultLayer, + ), + ) + + experimentalToolIt.live("would spawn builtin LSP for files inside instance when lsp is true", () => provideTmpdirInstance( (dir) => LSP.Service.use((lsp) => Effect.gen(function* () { - const saved = Flag.KILO_EXPERIMENTAL_LSP_TOOL - Flag.KILO_EXPERIMENTAL_LSP_TOOL = true const spy = spyOn(LSPServer.Typescript, "spawn").mockResolvedValue(undefined) try { @@ -82,7 +99,6 @@ describe("lsp.spawn", () => { }) expect(spy).toHaveBeenCalledTimes(1) } finally { - Flag.KILO_EXPERIMENTAL_LSP_TOOL = saved spy.mockRestore() } }), @@ -91,13 +107,11 @@ describe("lsp.spawn", () => { ), ) - it.live("would spawn builtin LSP for files inside instance when config object is provided", () => + experimentalToolIt.live("would spawn builtin LSP for files inside instance when config object is provided", () => provideTmpdirInstance( (dir) => LSP.Service.use((lsp) => Effect.gen(function* () { - const saved = Flag.KILO_EXPERIMENTAL_LSP_TOOL - Flag.KILO_EXPERIMENTAL_LSP_TOOL = true const spy = spyOn(LSPServer.Typescript, "spawn").mockResolvedValue(undefined) try { @@ -108,7 +122,6 @@ describe("lsp.spawn", () => { }) expect(spy).toHaveBeenCalledTimes(1) } finally { - Flag.KILO_EXPERIMENTAL_LSP_TOOL = saved spy.mockRestore() } }), @@ -139,7 +152,7 @@ describe("lsp.spawn", () => { await WithInstance.provide({ directory: tmp.path, fn: async () => { - const result = await LSPServer.Typescript.spawn(tmp.path, fakeCtx) + const result = await LSPServer.Typescript.spawn(tmp.path, fakeCtx, fakeFlags) expect(result).toBeDefined() expect(tsgoSpy).toHaveBeenCalledWith(tmp.path) expect(spawnSpy).toHaveBeenCalled() @@ -159,11 +172,62 @@ describe("lsp.spawn", () => { const saved = Flag.KILO_EXPERIMENTAL_LSP_TOOL Flag.KILO_EXPERIMENTAL_LSP_TOOL = false try { - const result = await LSPServer.Typescript.spawn("/tmp/any", fakeCtx) + const result = await LSPServer.Typescript.spawn("/tmp/any", fakeCtx, fakeFlags) expect(result).toBeUndefined() } finally { Flag.KILO_EXPERIMENTAL_LSP_TOOL = saved } }) // kilocode_change end + it.live("uses pyright instead of ty by default", () => + provideTmpdirInstance( + (dir) => + LSP.Service.use((lsp) => + Effect.gen(function* () { + const ty = spyOn(LSPServer.Ty, "spawn").mockResolvedValue(undefined) + const pyright = spyOn(LSPServer.Pyright, "spawn").mockResolvedValue(undefined) + + try { + yield* lsp.hover({ + file: path.join(dir, "src", "inside.py"), + line: 0, + character: 0, + }) + expect(ty).toHaveBeenCalledTimes(0) + expect(pyright).toHaveBeenCalledTimes(1) + } finally { + ty.mockRestore() + pyright.mockRestore() + } + }), + ), + { config: { lsp: true } }, + ), + ) + + experimentalTyIt.live("uses ty instead of pyright when experimentalLspTy is enabled", () => + provideTmpdirInstance( + (dir) => + LSP.Service.use((lsp) => + Effect.gen(function* () { + const ty = spyOn(LSPServer.Ty, "spawn").mockResolvedValue(undefined) + const pyright = spyOn(LSPServer.Pyright, "spawn").mockResolvedValue(undefined) + + try { + yield* lsp.hover({ + file: path.join(dir, "src", "inside.py"), + line: 0, + character: 0, + }) + expect(ty).toHaveBeenCalledTimes(1) + expect(pyright).toHaveBeenCalledTimes(0) + } finally { + ty.mockRestore() + pyright.mockRestore() + } + }), + ), + { config: { lsp: true } }, + ), + ) }) diff --git a/packages/opencode/test/mcp/headers.test.ts b/packages/opencode/test/mcp/headers.test.ts index 2a36e331cf9..c51ed00d32f 100644 --- a/packages/opencode/test/mcp/headers.test.ts +++ b/packages/opencode/test/mcp/headers.test.ts @@ -1,6 +1,6 @@ -import { test, expect, mock, beforeEach } from "bun:test" +import { describe, expect, mock, beforeEach } from "bun:test" import { Effect } from "effect" -import type { MCP as MCPNS } from "../../src/mcp/index" +import { testEffect } from "../lib/effect" // Track what options were passed to each transport constructor const transportCalls: Array<{ @@ -46,53 +46,22 @@ beforeEach(() => { // Import MCP after mocking const { MCP } = await import("../../src/mcp/index") -const { AppRuntime } = await import("../../src/effect/app-runtime") -const { Instance } = await import("../../src/project/instance") -const { WithInstance } = await import("../../src/project/with-instance") -const { tmpdir } = await import("../fixture/fixture") -const service = MCP.Service as unknown as Effect.Effect - -test("headers are passed to transports when oauth is enabled (default)", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - `${dir}/opencode.json`, - JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", - mcp: { - "test-server": { - type: "remote", - url: "https://example.com/mcp", - headers: { - Authorization: "Bearer test-token", - "X-Custom-Header": "custom-value", - }, - }, +const it = testEffect(MCP.defaultLayer) + +describe("mcp.headers", () => { + it.instance("headers are passed to transports when oauth is enabled (default)", () => + Effect.gen(function* () { + const mcp = yield* MCP.Service + yield* mcp + .add("test-server", { + type: "remote", + url: "https://example.com/mcp", + headers: { + Authorization: "Bearer test-token", + "X-Custom-Header": "custom-value", }, - }), - ) - }, - }) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // Trigger MCP initialization - it will fail to connect but we can check the transport options - await AppRuntime.runPromise( - Effect.gen(function* () { - const mcp = yield* service - yield* mcp - .add("test-server", { - type: "remote", - url: "https://example.com/mcp", - headers: { - Authorization: "Bearer test-token", - "X-Custom-Header": "custom-value", - }, - }) - .pipe(Effect.catch(() => Effect.void)) - }), - ) + }) + .pipe(Effect.catch(() => Effect.void)) // Both transports should have been created with headers expect(transportCalls.length).toBeGreaterThanOrEqual(1) @@ -106,33 +75,22 @@ test("headers are passed to transports when oauth is enabled (default)", async ( // OAuth should be enabled by default, so authProvider should exist expect(call.options.authProvider).toBeDefined() } - }, - }) -}) - -test("headers are passed to transports when oauth is explicitly disabled", async () => { - await using tmp = await tmpdir() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - transportCalls.length = 0 - - await AppRuntime.runPromise( - Effect.gen(function* () { - const mcp = yield* service - yield* mcp - .add("test-server-no-oauth", { - type: "remote", - url: "https://example.com/mcp", - oauth: false, - headers: { - Authorization: "Bearer test-token", - }, - }) - .pipe(Effect.catch(() => Effect.void)) - }), - ) + }), + ) + + it.instance("headers are passed to transports when oauth is explicitly disabled", () => + Effect.gen(function* () { + const mcp = yield* MCP.Service + yield* mcp + .add("test-server-no-oauth", { + type: "remote", + url: "https://example.com/mcp", + oauth: false, + headers: { + Authorization: "Bearer test-token", + }, + }) + .pipe(Effect.catch(() => Effect.void)) expect(transportCalls.length).toBeGreaterThanOrEqual(1) @@ -144,29 +102,18 @@ test("headers are passed to transports when oauth is explicitly disabled", async // OAuth is disabled, so no authProvider expect(call.options.authProvider).toBeUndefined() } - }, - }) -}) - -test("no requestInit when headers are not provided", async () => { - await using tmp = await tmpdir() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - transportCalls.length = 0 - - await AppRuntime.runPromise( - Effect.gen(function* () { - const mcp = yield* service - yield* mcp - .add("test-server-no-headers", { - type: "remote", - url: "https://example.com/mcp", - }) - .pipe(Effect.catch(() => Effect.void)) - }), - ) + }), + ) + + it.instance("no requestInit when headers are not provided", () => + Effect.gen(function* () { + const mcp = yield* MCP.Service + yield* mcp + .add("test-server-no-headers", { + type: "remote", + url: "https://example.com/mcp", + }) + .pipe(Effect.catch(() => Effect.void)) expect(transportCalls.length).toBeGreaterThanOrEqual(1) @@ -174,6 +121,6 @@ test("no requestInit when headers are not provided", async () => { // No headers means requestInit should be undefined expect(call.options.requestInit).toBeUndefined() } - }, - }) + }), + ) }) diff --git a/packages/opencode/test/mcp/lifecycle.test.ts b/packages/opencode/test/mcp/lifecycle.test.ts index 5afc85e3b5d..185086fe60a 100644 --- a/packages/opencode/test/mcp/lifecycle.test.ts +++ b/packages/opencode/test/mcp/lifecycle.test.ts @@ -1,7 +1,7 @@ -import { test, expect, mock, beforeEach } from "bun:test" -import { InstanceRuntime } from "../../src/project/instance-runtime" -import { Effect } from "effect" +import { expect, mock, beforeEach } from "bun:test" +import { Effect, Exit } from "effect" import type { MCP as MCPNS } from "../../src/mcp/index" +import { testEffect } from "../lib/effect" // --- Mock infrastructure --- @@ -179,39 +179,9 @@ beforeEach(() => { // Import after mocks const { MCP } = await import("../../src/mcp/index") -const { Instance } = await import("../../src/project/instance") -const { WithInstance } = await import("../../src/project/with-instance") -const { tmpdir } = await import("../fixture/fixture") - -// --- Helper --- - -function withInstance( - config: Record, - fn: (mcp: MCPNS.Interface) => Effect.Effect, -) { - return async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - `${dir}/opencode.json`, - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - mcp: config, - }), - ) - }, - }) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await Effect.runPromise(MCP.Service.use(fn).pipe(Effect.provide(MCP.defaultLayer))) - // dispose instance to clean up state between tests - await InstanceRuntime.disposeInstance(Instance.current) - }, - }) - } -} +const { McpOAuthCallback } = await import("../../src/mcp/oauth-callback") + +const it = testEffect(MCP.defaultLayer) function statusName(status: Record | MCPNS.Status, server: string) { if ("status" in status) return status.status @@ -222,82 +192,82 @@ function statusName(status: Record | MCPNS.Status, server: // Test: tools() are cached after connect // ======================================================================== -test( +it.instance( "tools() reuses cached tool definitions after connect", - withInstance({}, (mcp) => - Effect.gen(function* () { - lastCreatedClientName = "my-server" - const serverState = getOrCreateClientState("my-server") - serverState.tools = [ - { name: "do_thing", description: "does a thing", inputSchema: { type: "object", properties: {} } }, - ] - - // First: add the server successfully - const addResult = yield* mcp.add("my-server", { - type: "local", - command: ["echo", "test"], - }) - expect((addResult.status as any)["my-server"]?.status ?? (addResult.status as any).status).toBe("connected") - - expect(serverState.listToolsCalls).toBe(1) - - const toolsA = yield* mcp.tools() - const toolsB = yield* mcp.tools() - expect(Object.keys(toolsA).length).toBeGreaterThan(0) - expect(Object.keys(toolsB).length).toBeGreaterThan(0) - expect(serverState.listToolsCalls).toBe(1) - }), - ), + () => + MCP.Service.use((mcp: MCPNS.Interface) => + Effect.gen(function* () { + lastCreatedClientName = "my-server" + const serverState = getOrCreateClientState("my-server") + serverState.tools = [ + { name: "do_thing", description: "does a thing", inputSchema: { type: "object", properties: {} } }, + ] + + // First: add the server successfully + const addResult = yield* mcp.add("my-server", { + type: "local", + command: ["echo", "test"], + }) + expect((addResult.status as any)["my-server"]?.status ?? (addResult.status as any).status).toBe("connected") + + expect(serverState.listToolsCalls).toBe(1) + + const toolsA = yield* mcp.tools() + const toolsB = yield* mcp.tools() + expect(Object.keys(toolsA).length).toBeGreaterThan(0) + expect(Object.keys(toolsB).length).toBeGreaterThan(0) + expect(serverState.listToolsCalls).toBe(1) + }), + ), + { config: { mcp: {} } }, ) // ======================================================================== // Test: tool change notifications refresh the cache // ======================================================================== -test( +it.instance( "tool change notifications refresh cached tool definitions", - withInstance({}, (mcp) => - Effect.gen(function* () { - lastCreatedClientName = "status-server" - const serverState = getOrCreateClientState("status-server") - - yield* mcp.add("status-server", { - type: "local", - command: ["echo", "test"], - }) - - const before = yield* mcp.tools() - expect(Object.keys(before).some((key) => key.includes("test_tool"))).toBe(true) - expect(serverState.listToolsCalls).toBe(1) - - serverState.tools = [{ name: "next_tool", description: "next", inputSchema: { type: "object", properties: {} } }] - - const handler = Array.from(serverState.notificationHandlers.values())[0] - expect(handler).toBeDefined() - yield* Effect.promise(() => handler?.()) - - const after = yield* mcp.tools() - expect(Object.keys(after).some((key) => key.includes("next_tool"))).toBe(true) - expect(Object.keys(after).some((key) => key.includes("test_tool"))).toBe(false) - expect(serverState.listToolsCalls).toBe(2) - }), - ), + () => + MCP.Service.use((mcp: MCPNS.Interface) => + Effect.gen(function* () { + lastCreatedClientName = "status-server" + const serverState = getOrCreateClientState("status-server") + + yield* mcp.add("status-server", { + type: "local", + command: ["echo", "test"], + }) + + const before = yield* mcp.tools() + expect(Object.keys(before).some((key) => key.includes("test_tool"))).toBe(true) + expect(serverState.listToolsCalls).toBe(1) + + serverState.tools = [ + { name: "next_tool", description: "next", inputSchema: { type: "object", properties: {} } }, + ] + + const handler = Array.from(serverState.notificationHandlers.values())[0] + expect(handler).toBeDefined() + yield* Effect.promise(() => handler?.()) + + const after = yield* mcp.tools() + expect(Object.keys(after).some((key) => key.includes("next_tool"))).toBe(true) + expect(Object.keys(after).some((key) => key.includes("test_tool"))).toBe(false) + expect(serverState.listToolsCalls).toBe(2) + }), + ), + { config: { mcp: {} } }, ) // ======================================================================== // Test: connect() / disconnect() lifecycle // ======================================================================== -test( +it.instance( "disconnect sets status to disabled and removes client", - withInstance( - { - "disc-server": { - type: "local", - command: ["echo", "test"], - }, - }, - (mcp) => + () => + MCP.Service.use((mcp: MCPNS.Interface) => Effect.gen(function* () { lastCreatedClientName = "disc-server" getOrCreateClientState("disc-server") @@ -315,24 +285,27 @@ test( const statusAfter = yield* mcp.status() expect(statusAfter["disc-server"]?.status).toBe("disabled") - // Tools should be empty after disconnect const tools = yield* mcp.tools() const serverTools = Object.keys(tools).filter((k) => k.startsWith("disc-server")) expect(serverTools.length).toBe(0) }), - ), + ), + { + config: { + mcp: { + "disc-server": { + type: "local", + command: ["echo", "test"], + }, + }, + }, + }, ) -test( +it.instance( "connect() after disconnect() re-establishes the server", - withInstance( - { - "reconn-server": { - type: "local", - command: ["echo", "test"], - }, - }, - (mcp) => + () => + MCP.Service.use((mcp: MCPNS.Interface) => Effect.gen(function* () { lastCreatedClientName = "reconn-server" const serverState = getOrCreateClientState("reconn-server") @@ -348,70 +321,71 @@ test( yield* mcp.disconnect("reconn-server") expect((yield* mcp.status())["reconn-server"]?.status).toBe("disabled") - // Reconnect yield* mcp.connect("reconn-server") expect((yield* mcp.status())["reconn-server"]?.status).toBe("connected") const tools = yield* mcp.tools() expect(Object.keys(tools).some((k) => k.includes("my_tool"))).toBe(true) }), - ), + ), + { + config: { + mcp: { + "reconn-server": { + type: "local", + command: ["echo", "test"], + }, + }, + }, + }, ) // ======================================================================== // Test: add() closes existing client before replacing // ======================================================================== -test( +it.instance( "add() closes the old client when replacing a server", // Don't put the server in config — add it dynamically so we control // exactly which client instance is "first" vs "second". - withInstance({}, (mcp) => - Effect.gen(function* () { - lastCreatedClientName = "replace-server" - const firstState = getOrCreateClientState("replace-server") - - yield* mcp.add("replace-server", { - type: "local", - command: ["echo", "test"], - }) - - expect(firstState.closed).toBe(false) - - // Create new state for second client - clientStates.delete("replace-server") - const secondState = getOrCreateClientState("replace-server") - - // Re-add should close the first client - yield* mcp.add("replace-server", { - type: "local", - command: ["echo", "test"], - }) - - expect(firstState.closed).toBe(true) - expect(secondState.closed).toBe(false) - }), - ), + () => + MCP.Service.use((mcp: MCPNS.Interface) => + Effect.gen(function* () { + lastCreatedClientName = "replace-server" + const firstState = getOrCreateClientState("replace-server") + + yield* mcp.add("replace-server", { + type: "local", + command: ["echo", "test"], + }) + + expect(firstState.closed).toBe(false) + + // Create new state for second client + clientStates.delete("replace-server") + const secondState = getOrCreateClientState("replace-server") + + // Re-add should close the first client + yield* mcp.add("replace-server", { + type: "local", + command: ["echo", "test"], + }) + + expect(firstState.closed).toBe(true) + expect(secondState.closed).toBe(false) + }), + ), + { config: { mcp: {} } }, ) // ======================================================================== // Test: state init with mixed success/failure // ======================================================================== -test( +it.instance( "init connects available servers even when one fails", - withInstance( - { - "good-server": { - type: "local", - command: ["echo", "good"], - }, - "bad-server": { - type: "local", - command: ["echo", "bad"], - }, - }, - (mcp) => + () => + MCP.Service.use((mcp: MCPNS.Interface) => Effect.gen(function* () { // Set up good server const goodState = getOrCreateClientState("good-server") @@ -443,77 +417,88 @@ test( const tools = yield* mcp.tools() expect(Object.keys(tools).some((k) => k.includes("good_tool"))).toBe(true) }), - ), + ), + { + config: { + mcp: { + "good-server": { + type: "local", + command: ["echo", "good"], + }, + "bad-server": { + type: "local", + command: ["echo", "bad"], + }, + }, + }, + }, ) -test( +it.instance( "falls back when MCP output schema refs fail SDK tool discovery", - withInstance({}, (mcp) => - Effect.gen(function* () { - lastCreatedClientName = "stitch-like-server" - const serverState = getOrCreateClientState("stitch-like-server") - serverState.listToolsShouldFail = true - serverState.listToolsError = "can't resolve reference #/$defs/ScreenInstance from id #" - serverState.tools = [ - { - name: "render_screen", - description: "renders a screen", - inputSchema: { type: "object", properties: { prompt: { type: "string" } }, required: ["prompt"] }, - outputSchema: { type: "object", properties: { screen: { $ref: "#/$defs/ScreenInstance" } } }, - }, - ] + () => + MCP.Service.use((mcp: MCPNS.Interface) => + Effect.gen(function* () { + lastCreatedClientName = "stitch-like-server" + const serverState = getOrCreateClientState("stitch-like-server") + serverState.listToolsShouldFail = true + serverState.listToolsError = "can't resolve reference #/$defs/ScreenInstance from id #" + serverState.tools = [ + { + name: "render_screen", + description: "renders a screen", + inputSchema: { type: "object", properties: { prompt: { type: "string" } }, required: ["prompt"] }, + outputSchema: { type: "object", properties: { screen: { $ref: "#/$defs/ScreenInstance" } } }, + }, + ] - const addResult = yield* mcp.add("stitch-like-server", { - type: "local", - command: ["echo", "test"], - }) + const addResult = yield* mcp.add("stitch-like-server", { + type: "local", + command: ["echo", "test"], + }) - expect(statusName(addResult.status, "stitch-like-server")).toBe("connected") + expect(statusName(addResult.status, "stitch-like-server")).toBe("connected") - const tools = yield* mcp.tools() - expect(Object.keys(tools).some((key) => key.includes("render_screen"))).toBe(true) - expect(serverState.listToolsCalls).toBe(1) - expect(serverState.requestCalls).toBe(1) - }), - ), + const tools = yield* mcp.tools() + expect(Object.keys(tools).some((key) => key.includes("render_screen"))).toBe(true) + expect(serverState.listToolsCalls).toBe(1) + expect(serverState.requestCalls).toBe(1) + }), + ), + { config: { mcp: {} } }, ) -test( +it.instance( "does not fall back for non-schema MCP tool discovery errors", - withInstance({}, (mcp) => - Effect.gen(function* () { - lastCreatedClientName = "broken-server" - const serverState = getOrCreateClientState("broken-server") - serverState.listToolsShouldFail = true - serverState.listToolsError = "transport closed" - - const addResult = yield* mcp.add("broken-server", { - type: "local", - command: ["echo", "test"], - }) - - expect(statusName(addResult.status, "broken-server")).toBe("failed") - expect(serverState.listToolsCalls).toBe(1) - expect(serverState.requestCalls).toBe(0) - }), - ), + () => + MCP.Service.use((mcp: MCPNS.Interface) => + Effect.gen(function* () { + lastCreatedClientName = "broken-server" + const serverState = getOrCreateClientState("broken-server") + serverState.listToolsShouldFail = true + serverState.listToolsError = "transport closed" + + const addResult = yield* mcp.add("broken-server", { + type: "local", + command: ["echo", "test"], + }) + + expect(statusName(addResult.status, "broken-server")).toBe("failed") + expect(serverState.listToolsCalls).toBe(1) + expect(serverState.requestCalls).toBe(0) + }), + ), + { config: { mcp: {} } }, ) // ======================================================================== // Test: disabled server via config // ======================================================================== -test( +it.instance( "disabled server is marked as disabled without attempting connection", - withInstance( - { - "disabled-server": { - type: "local", - command: ["echo", "test"], - enabled: false, - }, - }, - (mcp) => + () => + MCP.Service.use((mcp: MCPNS.Interface) => Effect.gen(function* () { const countBefore = clientCreateCount @@ -529,23 +514,28 @@ test( const status = yield* mcp.status() expect(status["disabled-server"]?.status).toBe("disabled") }), - ), + ), + { + config: { + mcp: { + "disabled-server": { + type: "local", + command: ["echo", "test"], + enabled: false, + }, + }, + }, + }, ) // ======================================================================== // Test: prompts() and resources() // ======================================================================== -test( +it.instance( "prompts() returns prompts from connected servers", - withInstance( - { - "prompt-server": { - type: "local", - command: ["echo", "test"], - }, - }, - (mcp) => + () => + MCP.Service.use((mcp: MCPNS.Interface) => Effect.gen(function* () { lastCreatedClientName = "prompt-server" const serverState = getOrCreateClientState("prompt-server") @@ -562,19 +552,23 @@ test( expect(key).toContain("prompt-server") expect(key).toContain("my-prompt") }), - ), + ), + { + config: { + mcp: { + "prompt-server": { + type: "local", + command: ["echo", "test"], + }, + }, + }, + }, ) -test( +it.instance( "resources() returns resources from connected servers", - withInstance( - { - "resource-server": { - type: "local", - command: ["echo", "test"], - }, - }, - (mcp) => + () => + MCP.Service.use((mcp: MCPNS.Interface) => Effect.gen(function* () { lastCreatedClientName = "resource-server" const serverState = getOrCreateClientState("resource-server") @@ -591,19 +585,23 @@ test( expect(key).toContain("resource-server") expect(key).toContain("my-resource") }), - ), + ), + { + config: { + mcp: { + "resource-server": { + type: "local", + command: ["echo", "test"], + }, + }, + }, + }, ) -test( +it.instance( "prompts() skips disconnected servers", - withInstance( - { - "prompt-disc-server": { - type: "local", - command: ["echo", "test"], - }, - }, - (mcp) => + () => + MCP.Service.use((mcp: MCPNS.Interface) => Effect.gen(function* () { lastCreatedClientName = "prompt-disc-server" const serverState = getOrCreateClientState("prompt-disc-server") @@ -619,67 +617,77 @@ test( const prompts = yield* mcp.prompts() expect(Object.keys(prompts).length).toBe(0) }), - ), + ), + { + config: { + mcp: { + "prompt-disc-server": { + type: "local", + command: ["echo", "test"], + }, + }, + }, + }, ) // ======================================================================== // Test: connect() on nonexistent server // ======================================================================== -test( +it.instance( "connect() on nonexistent server does not throw", - withInstance({}, (mcp) => - Effect.gen(function* () { - // Should not throw - yield* mcp.connect("nonexistent") - const status = yield* mcp.status() - expect(status["nonexistent"]).toBeUndefined() - }), - ), + () => + MCP.Service.use((mcp: MCPNS.Interface) => + Effect.gen(function* () { + // Should not throw + yield* mcp.connect("nonexistent") + const status = yield* mcp.status() + expect(status["nonexistent"]).toBeUndefined() + }), + ), + { config: { mcp: {} } }, ) // ======================================================================== // Test: disconnect() on nonexistent server // ======================================================================== -test( +it.instance( "disconnect() on nonexistent server does not throw", - withInstance({}, (mcp) => - Effect.gen(function* () { - yield* mcp.disconnect("nonexistent") - // Should complete without error - }), - ), + () => + MCP.Service.use((mcp: MCPNS.Interface) => + Effect.gen(function* () { + yield* mcp.disconnect("nonexistent") + // Should complete without error + }), + ), + { config: { mcp: {} } }, ) // ======================================================================== // Test: tools() with no MCP servers configured // ======================================================================== -test( +it.instance( "tools() returns empty when no MCP servers are configured", - withInstance({}, (mcp) => - Effect.gen(function* () { - const tools = yield* mcp.tools() - expect(Object.keys(tools).length).toBe(0) - }), - ), + () => + MCP.Service.use((mcp: MCPNS.Interface) => + Effect.gen(function* () { + const tools = yield* mcp.tools() + expect(Object.keys(tools).length).toBe(0) + }), + ), + { config: { mcp: {} } }, ) // ======================================================================== // Test: connect failure during create() // ======================================================================== -test( +it.instance( "server that fails to connect is marked as failed", - withInstance( - { - "fail-connect": { - type: "local", - command: ["echo", "test"], - }, - }, - (mcp) => + () => + MCP.Service.use((mcp: MCPNS.Interface) => Effect.gen(function* () { lastCreatedClientName = "fail-connect" getOrCreateClientState("fail-connect") @@ -701,51 +709,55 @@ test( const tools = yield* mcp.tools() expect(Object.keys(tools).length).toBe(0) }), - ), + ), + { + config: { + mcp: { + "fail-connect": { + type: "local", + command: ["echo", "test"], + }, + }, + }, + }, ) // ======================================================================== // Bug #5: McpOAuthCallback.cancelPending uses wrong key // ======================================================================== -test("McpOAuthCallback.cancelPending is keyed by mcpName but pendingAuths uses oauthState", async () => { - const { McpOAuthCallback } = await import("../../src/mcp/oauth-callback") - - // Register a pending auth with an oauthState key, associated to an mcpName - const oauthState = "abc123hexstate" - const callbackPromise = McpOAuthCallback.waitForCallback(oauthState, "my-mcp-server") - - // cancelPending is called with mcpName — should find the entry via reverse index - McpOAuthCallback.cancelPending("my-mcp-server") - - // The callback should still be pending because cancelPending looked up - // "my-mcp-server" in a map keyed by "abc123hexstate" - let rejected = false - callbackPromise.then(() => {}).catch(() => (rejected = true)) - - // Give it a tick - await new Promise((r) => setTimeout(r, 50)) - - // cancelPending("my-mcp-server") should have rejected the pending callback - expect(rejected).toBe(true) +it.live("McpOAuthCallback.cancelPending is keyed by mcpName but pendingAuths uses oauthState", () => + Effect.acquireUseRelease( + Effect.sync(() => McpOAuthCallback.waitForCallback("abc123hexstate", "my-mcp-server")), + (callback) => + Effect.gen(function* () { + McpOAuthCallback.cancelPending("my-mcp-server") + + const exit = yield* Effect.tryPromise({ + try: () => callback, + catch: (error) => (error instanceof Error ? error : new Error(String(error))), + }).pipe( + Effect.timeoutOrElse({ + duration: "1 second", + orElse: () => Effect.fail(new Error("timed out waiting for OAuth cancellation")), + }), + Effect.exit, + ) - await McpOAuthCallback.stop() -}) + expect(Exit.isFailure(exit)).toBe(true) + }), + () => Effect.promise(() => McpOAuthCallback.stop()).pipe(Effect.ignore), + ), +) // ======================================================================== // Test: multiple tools from same server get correct name prefixes // ======================================================================== -test( +it.instance( "tools() prefixes tool names with sanitized server name", - withInstance( - { - "my.special-server": { - type: "local", - command: ["echo", "test"], - }, - }, - (mcp) => + () => + MCP.Service.use((mcp: MCPNS.Interface) => Effect.gen(function* () { lastCreatedClientName = "my.special-server" const serverState = getOrCreateClientState("my.special-server") @@ -768,87 +780,103 @@ test( expect(keys.some((k) => k.endsWith("tool_b"))).toBe(true) expect(keys.length).toBe(2) }), - ), + ), + { + config: { + mcp: { + "my.special-server": { + type: "local", + command: ["echo", "test"], + }, + }, + }, + }, ) // ======================================================================== // Test: transport leak — local stdio timeout (#19168) // ======================================================================== -test( +it.instance( "local stdio transport is closed when connect times out (no process leak)", - withInstance({}, (mcp) => - Effect.gen(function* () { - lastCreatedClientName = "hanging-server" - getOrCreateClientState("hanging-server") - connectShouldHang = true - - const addResult = yield* mcp.add("hanging-server", { - type: "local", - command: ["node", "fake.js"], - timeout: 100, - }) - - const serverStatus = (addResult.status as any)["hanging-server"] ?? addResult.status - expect(serverStatus.status).toBe("failed") - expect(serverStatus.error).toContain("timed out") - // Transport must be closed to avoid orphaned child process - expect(transportCloseCount).toBeGreaterThanOrEqual(1) - }), - ), + () => + MCP.Service.use((mcp: MCPNS.Interface) => + Effect.gen(function* () { + lastCreatedClientName = "hanging-server" + getOrCreateClientState("hanging-server") + connectShouldHang = true + + const addResult = yield* mcp.add("hanging-server", { + type: "local", + command: ["node", "fake.js"], + timeout: 100, + }) + + const serverStatus = (addResult.status as any)["hanging-server"] ?? addResult.status + expect(serverStatus.status).toBe("failed") + expect(serverStatus.error).toContain("timed out") + // Transport must be closed to avoid orphaned child process + expect(transportCloseCount).toBeGreaterThanOrEqual(1) + }), + ), + { config: { mcp: {} } }, ) // ======================================================================== // Test: transport leak — remote timeout (#19168) // ======================================================================== -test( +it.instance( "remote transport is closed when connect times out", - withInstance({}, (mcp) => - Effect.gen(function* () { - lastCreatedClientName = "hanging-remote" - getOrCreateClientState("hanging-remote") - connectShouldHang = true - - const addResult = yield* mcp.add("hanging-remote", { - type: "remote", - url: "http://localhost:9999/mcp", - timeout: 100, - oauth: false, - }) - - const serverStatus = (addResult.status as any)["hanging-remote"] ?? addResult.status - expect(serverStatus.status).toBe("failed") - // Transport must be closed to avoid leaked HTTP connections - expect(transportCloseCount).toBeGreaterThanOrEqual(1) - }), - ), + () => + MCP.Service.use((mcp: MCPNS.Interface) => + Effect.gen(function* () { + lastCreatedClientName = "hanging-remote" + getOrCreateClientState("hanging-remote") + connectShouldHang = true + + const addResult = yield* mcp.add("hanging-remote", { + type: "remote", + url: "http://localhost:9999/mcp", + timeout: 100, + oauth: false, + }) + + const serverStatus = (addResult.status as any)["hanging-remote"] ?? addResult.status + expect(serverStatus.status).toBe("failed") + // Transport must be closed to avoid leaked HTTP connections + expect(transportCloseCount).toBeGreaterThanOrEqual(1) + }), + ), + { config: { mcp: {} } }, ) // ======================================================================== // Test: transport leak — failed remote transports not closed (#19168) // ======================================================================== -test( +it.instance( "failed remote transport is closed before trying next transport", - withInstance({}, (mcp) => - Effect.gen(function* () { - lastCreatedClientName = "fail-remote" - getOrCreateClientState("fail-remote") - connectShouldFail = true - connectError = "Connection refused" - - const addResult = yield* mcp.add("fail-remote", { - type: "remote", - url: "http://localhost:9999/mcp", - timeout: 5000, - oauth: false, - }) - - const serverStatus = (addResult.status as any)["fail-remote"] ?? addResult.status - expect(serverStatus.status).toBe("failed") - // Both StreamableHTTP and SSE transports should be closed - expect(transportCloseCount).toBeGreaterThanOrEqual(2) - }), - ), + () => + MCP.Service.use((mcp: MCPNS.Interface) => + Effect.gen(function* () { + lastCreatedClientName = "fail-remote" + getOrCreateClientState("fail-remote") + connectShouldFail = true + connectError = "Connection refused" + + const addResult = yield* mcp.add("fail-remote", { + type: "remote", + url: "http://localhost:9999/mcp", + timeout: 5000, + oauth: false, + }) + + const serverStatus = (addResult.status as any)["fail-remote"] ?? addResult.status + expect(serverStatus.status).toBe("failed") + // Both StreamableHTTP and SSE transports should be closed + expect(transportCloseCount).toBeGreaterThanOrEqual(2) + }), + ), + { config: { mcp: {} } }, ) diff --git a/packages/opencode/test/mcp/oauth-auto-connect.test.ts b/packages/opencode/test/mcp/oauth-auto-connect.test.ts index d4830c5e95d..6fb15c45947 100644 --- a/packages/opencode/test/mcp/oauth-auto-connect.test.ts +++ b/packages/opencode/test/mcp/oauth-auto-connect.test.ts @@ -1,6 +1,6 @@ -import { test, expect, mock, beforeEach } from "bun:test" -import { Effect } from "effect" -import { createServer } from "http" // kilocode_change +import { expect, mock, beforeEach } from "bun:test" +import { Effect, Layer } from "effect" +import { testEffect } from "../lib/effect" // Mock UnauthorizedError to match the SDK's class class MockUnauthorizedError extends Error { @@ -110,208 +110,127 @@ beforeEach(() => { connectSucceedsImmediately = false }) -// kilocode_change start -async function occupy(port: number) { - const srv = createServer() - const bound = await new Promise((resolve, reject) => { - const fail = (err: Error & { code?: string }) => { - srv.off("error", fail) - if (err.code === "EADDRINUSE") { - resolve(false) - return - } - reject(err) - } - - srv.once("error", fail) - srv.listen(port, "127.0.0.1", () => { - srv.off("error", fail) - resolve(true) - }) - }) - - return { - async [Symbol.asyncDispose]() { - if (!bound) return - await new Promise((resolve) => srv.close(() => resolve())) - }, - } -} -// kilocode_change end - // Import modules after mocking const { MCP } = await import("../../src/mcp/index") -const { Instance } = await import("../../src/project/instance") -const { WithInstance } = await import("../../src/project/with-instance") -const { tmpdir } = await import("../fixture/fixture") -const { OAUTH_CALLBACK_PORT } = await import("../../src/mcp/oauth-provider") // kilocode_change - -test("first connect to OAuth server shows needs_auth instead of failed", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - `${dir}/opencode.json`, - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - mcp: { - "test-oauth": { - type: "remote", - url: "https://example.com/mcp", - }, - }, - }), - ) - }, - }) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const result = await Effect.runPromise( - MCP.Service.use((mcp) => - mcp.add("test-oauth", { - type: "remote", - url: "https://example.com/mcp", - }), - ).pipe(Effect.provide(MCP.defaultLayer)), - ) - - const serverStatus = result.status as Record - - // The server should be detected as needing auth, NOT as failed. - // Before the fix, provider.state() would throw a plain Error - // ("No OAuth state saved for MCP server: test-oauth") which was - // not caught as UnauthorizedError, causing status to be "failed". - expect(serverStatus["test-oauth"]).toBeDefined() - expect(serverStatus["test-oauth"].status).toBe("needs_auth") - }, - }) -}) - -test("state() generates a new state when none is saved", async () => { - const { McpOAuthProvider } = await import("../../src/mcp/oauth-provider") - const { McpAuth } = await import("../../src/mcp/auth") - - await using tmp = await tmpdir() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const auth = await Effect.runPromise( - Effect.gen(function* () { - return yield* McpAuth.Service - }).pipe(Effect.provide(McpAuth.defaultLayer)), - ) - const provider = new McpOAuthProvider( - "test-state-gen", - "https://example.com/mcp", - {}, - { onRedirect: async () => {} }, - auth, - ) - - const entryBefore = await Effect.runPromise( - McpAuth.Service.use((auth) => auth.get("test-state-gen")).pipe(Effect.provide(McpAuth.defaultLayer)), - ) - expect(entryBefore?.oauthState).toBeUndefined() - - // state() should generate and return a new state, not throw - const state = await provider.state() - expect(typeof state).toBe("string") - expect(state.length).toBe(64) // 32 bytes as hex - - // The generated state should be persisted - const entryAfter = await Effect.runPromise( - McpAuth.Service.use((auth) => auth.get("test-state-gen")).pipe(Effect.provide(McpAuth.defaultLayer)), - ) - expect(entryAfter?.oauthState).toBe(state) - }, - }) -}) - -test("state() returns existing state when one is saved", async () => { - const { McpOAuthProvider } = await import("../../src/mcp/oauth-provider") - const { McpAuth } = await import("../../src/mcp/auth") - - await using tmp = await tmpdir() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const auth = await Effect.runPromise( - Effect.gen(function* () { - return yield* McpAuth.Service - }).pipe(Effect.provide(McpAuth.defaultLayer)), - ) - const provider = new McpOAuthProvider( - "test-state-existing", - "https://example.com/mcp", - {}, - { onRedirect: async () => {} }, - auth, - ) - - // Pre-save a state - const existingState = "pre-saved-state-value" - await Effect.runPromise( - McpAuth.Service.use((auth) => auth.updateOAuthState("test-state-existing", existingState)).pipe( - Effect.provide(McpAuth.defaultLayer), - ), - ) - - // state() should return the existing state - const state = await provider.state() - expect(state).toBe(existingState) +const { Bus } = await import("../../src/bus") +const { Config } = await import("../../src/config/config") +const { McpAuth } = await import("../../src/mcp/auth") +const { McpOAuthProvider } = await import("../../src/mcp/oauth-provider") +const { AppFileSystem } = await import("@opencode-ai/core/filesystem") +const { CrossSpawnSpawner } = await import("@opencode-ai/core/cross-spawn-spawner") + +const mcpTest = testEffect( + Layer.mergeAll( + MCP.layer.pipe( + Layer.provide(McpAuth.defaultLayer), + Layer.provideMerge(Bus.layer), + Layer.provide(Config.defaultLayer), + Layer.provide(CrossSpawnSpawner.defaultLayer), + Layer.provide(AppFileSystem.defaultLayer), + ), + McpAuth.defaultLayer, + ), +) + +const config = (name: string) => ({ + mcp: { + [name]: { + type: "remote" as const, + url: "https://example.com/mcp", }, - }) + }, }) -test("authenticate() stores a connected client when auth completes without redirect", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - `${dir}/opencode.json`, - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - mcp: { - "test-oauth-connect": { - type: "remote", - url: "https://example.com/mcp", - }, - }, - }), - ) - }, - }) - // kilocode_change start - await using port = await occupy(OAUTH_CALLBACK_PORT) - void port - // kilocode_change end - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await Effect.runPromise( - MCP.Service.use((mcp) => - Effect.gen(function* () { - const added = yield* mcp.add("test-oauth-connect", { - type: "remote", - url: "https://example.com/mcp", - }) - const before = added.status as Record - expect(before["test-oauth-connect"]?.status).toBe("needs_auth") - - simulateAuthFlow = false - connectSucceedsImmediately = true - - const result = yield* mcp.authenticate("test-oauth-connect") - expect(result.status).toBe("connected") - - const after = yield* mcp.status() - expect(after["test-oauth-connect"]?.status).toBe("connected") - }), - ).pipe(Effect.provide(MCP.defaultLayer)), - ) - }, - }) -}) +mcpTest.instance( + "first connect to OAuth server shows needs_auth instead of failed", + () => + MCP.Service.use((mcp) => + Effect.gen(function* () { + const result = yield* mcp.add("test-oauth", { + type: "remote", + url: "https://example.com/mcp", + }) + + const serverStatus = result.status as Record + + // The server should be detected as needing auth, NOT as failed. + // Before the fix, provider.state() would throw a plain Error + // ("No OAuth state saved for MCP server: test-oauth") which was + // not caught as UnauthorizedError, causing status to be "failed". + expect(serverStatus["test-oauth"]).toBeDefined() + expect(serverStatus["test-oauth"].status).toBe("needs_auth") + }), + ), + { config: config("test-oauth") }, +) + +mcpTest.instance("state() generates a new state when none is saved", () => + Effect.gen(function* () { + const auth = yield* McpAuth.Service + const provider = new McpOAuthProvider( + "test-state-gen", + "https://example.com/mcp", + {}, + { onRedirect: async () => {} }, + auth, + ) + + const entryBefore = yield* McpAuth.Service.use((auth) => auth.get("test-state-gen")) + expect(entryBefore?.oauthState).toBeUndefined() + + // state() should generate and return a new state, not throw + const state = yield* Effect.promise(() => provider.state()) + expect(typeof state).toBe("string") + expect(state.length).toBe(64) // 32 bytes as hex + + // The generated state should be persisted + const entryAfter = yield* McpAuth.Service.use((auth) => auth.get("test-state-gen")) + expect(entryAfter?.oauthState).toBe(state) + }), +) + +mcpTest.instance("state() returns existing state when one is saved", () => + Effect.gen(function* () { + const auth = yield* McpAuth.Service + const provider = new McpOAuthProvider( + "test-state-existing", + "https://example.com/mcp", + {}, + { onRedirect: async () => {} }, + auth, + ) + + // Pre-save a state + const existingState = "pre-saved-state-value" + yield* McpAuth.Service.use((auth) => auth.updateOAuthState("test-state-existing", existingState)) + + // state() should return the existing state + const state = yield* Effect.promise(() => provider.state()) + expect(state).toBe(existingState) + }), +) + +mcpTest.instance( + "authenticate() stores a connected client when auth completes without redirect", + () => + MCP.Service.use((mcp) => + Effect.gen(function* () { + const added = yield* mcp.add("test-oauth-connect", { + type: "remote", + url: "https://example.com/mcp", + }) + const before = added.status as Record + expect(before["test-oauth-connect"]?.status).toBe("needs_auth") + + simulateAuthFlow = false + connectSucceedsImmediately = true + + const result = yield* mcp.authenticate("test-oauth-connect") + expect(result.status).toBe("connected") + + const after = yield* mcp.status() + expect(after["test-oauth-connect"]?.status).toBe("connected") + }), + ), + { config: config("test-oauth-connect") }, +) diff --git a/packages/opencode/test/mcp/oauth-browser.test.ts b/packages/opencode/test/mcp/oauth-browser.test.ts index 70a097ccd24..ae7a0ed2ae3 100644 --- a/packages/opencode/test/mcp/oauth-browser.test.ts +++ b/packages/opencode/test/mcp/oauth-browser.test.ts @@ -1,23 +1,24 @@ -import { test, expect, mock, beforeEach } from "bun:test" +import { expect, mock, beforeEach } from "bun:test" import { EventEmitter } from "events" -import { Effect } from "effect" +import { Deferred, Effect, Layer, Option } from "effect" +import { awaitWithTimeout, testEffect } from "../lib/effect" import type { MCP as MCPNS } from "../../src/mcp/index" // Track open() calls and control failure behavior let openShouldFail = false let openCalledWith: string | undefined +let openDeferred: Deferred.Deferred | undefined void mock.module("open", () => ({ default: async (url: string) => { openCalledWith = url + if (openDeferred) Effect.runSync(Deferred.succeed(openDeferred, url).pipe(Effect.ignore)) // Return a mock subprocess that emits an error if openShouldFail is true const subprocess = new EventEmitter() if (openShouldFail) { // kilocode_change start - buffer the error until the consumer attaches - // its listener. The previous setTimeout(10) raced listener attachment - // on slow Windows CI; emit() before `.on("error", ...)` was silently - // lost and BrowserOpenFailed was never published. + // its listener. Emitting on a timer races listener attachment on slow CI. const err = new Error("spawn xdg-open ENOENT") const originalOn = subprocess.on.bind(subprocess) subprocess.on = function (event, listener) { @@ -97,7 +98,6 @@ void mock.module("@modelcontextprotocol/sdk/client/index.js", () => ({ }, })) -// kilocode_change start - reset mock state and wait for OAuth redirect signal in CI // Mock UnauthorizedError in the auth module void mock.module("@modelcontextprotocol/sdk/client/auth.js", () => ({ UnauthorizedError: MockUnauthorizedError, @@ -106,188 +106,127 @@ void mock.module("@modelcontextprotocol/sdk/client/auth.js", () => ({ beforeEach(() => { openShouldFail = false openCalledWith = undefined + openDeferred = undefined transportCalls.length = 0 }) -async function waitFor(fn: () => boolean, timeout = 5_000) { - const deadline = Date.now() + timeout - while (!fn() && Date.now() < deadline) { - await new Promise((resolve) => setTimeout(resolve, 25)) - } -} -// kilocode_change end - // Import modules after mocking const { MCP } = await import("../../src/mcp/index") -const { AppRuntime } = await import("../../src/effect/app-runtime") const { Bus } = await import("../../src/bus") +const { Config } = await import("../../src/config/config") +const { McpAuth } = await import("../../src/mcp/auth") const { McpOAuthCallback } = await import("../../src/mcp/oauth-callback") -const { Instance } = await import("../../src/project/instance") -const { WithInstance } = await import("../../src/project/with-instance") -const { tmpdir } = await import("../fixture/fixture") +const { AppFileSystem } = await import("@opencode-ai/core/filesystem") +const { CrossSpawnSpawner } = await import("@opencode-ai/core/cross-spawn-spawner") +const mcpTest = testEffect( + MCP.layer.pipe( + Layer.provide(McpAuth.defaultLayer), + Layer.provideMerge(Bus.layer), + Layer.provide(Config.defaultLayer), + Layer.provide(CrossSpawnSpawner.defaultLayer), + Layer.provide(AppFileSystem.defaultLayer), + ), +) const service = MCP.Service as unknown as Effect.Effect -test("BrowserOpenFailed event is published when open() throws", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - `${dir}/opencode.json`, - JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", - mcp: { - "test-oauth-server": { - type: "remote", - url: "https://example.com/mcp", - }, - }, - }), - ) +const config = (name: string) => ({ + mcp: { + [name]: { + type: "remote" as const, + url: "https://example.com/mcp", }, - }) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - openShouldFail = true - - const events: Array<{ mcpName: string; url: string }> = [] - const unsubscribe = Bus.subscribe(MCP.BrowserOpenFailed, (evt) => { - events.push(evt.properties) - }) // kilocode_change - - // kilocode_change start - attach rejection handler before stopping callback server - // Run authenticate with a timeout to avoid waiting forever for the callback - // Attach a handler immediately so callback shutdown rejections - // don't show up as unhandled between tests. - const authPromise = AppRuntime.runPromise( - Effect.gen(function* () { - const mcp = yield* service - return yield* mcp.authenticate("test-oauth-server") - }), - ).catch(() => undefined) - // kilocode_change end - - await waitFor(() => events.length > 0) // kilocode_change - - // Stop the callback server and cancel any pending auth - await McpOAuthCallback.stop() + }, +}) - await authPromise +const withCallbackStop = Effect.addFinalizer(() => Effect.promise(() => McpOAuthCallback.stop()).pipe(Effect.ignore)) - unsubscribe() +const trackBrowserOpen = Effect.gen(function* () { + const opened = yield* Deferred.make() + openDeferred = opened + yield* Effect.addFinalizer(() => Effect.sync(() => (openDeferred = undefined))) + return opened +}) - // Verify the BrowserOpenFailed event was published - expect(events.length).toBe(1) - expect(events[0].mcpName).toBe("test-oauth-server") - expect(events[0].url).toContain("https://") - }, +const trackBrowserOpenFailed = Effect.gen(function* () { + const bus = yield* Bus.Service + const event = yield* Deferred.make<{ mcpName: string; url: string }>() + const unsubscribe = yield* bus.subscribeCallback(MCP.BrowserOpenFailed, (evt) => { + Effect.runSync(Deferred.succeed(event, evt.properties).pipe(Effect.ignore)) }) + yield* Effect.addFinalizer(() => Effect.sync(unsubscribe)) + return event }) -test("BrowserOpenFailed event is NOT published when open() succeeds", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - `${dir}/opencode.json`, - JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", - mcp: { - "test-oauth-server-2": { - type: "remote", - url: "https://example.com/mcp", - }, - }, - }), - ) - }, +const authenticateScoped = (name: string) => + Effect.gen(function* () { + const mcp = yield* service + yield* mcp.authenticate(name).pipe( + Effect.ignore, + Effect.catchCause(() => Effect.void), + Effect.forkScoped, + ) }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // kilocode_change - openShouldFail = false // kilocode_change +mcpTest.instance( + "BrowserOpenFailed event is published when open() throws", + () => + Effect.gen(function* () { + yield* withCallbackStop + openShouldFail = true - const events: Array<{ mcpName: string; url: string }> = [] - const unsubscribe = Bus.subscribe(MCP.BrowserOpenFailed, (evt) => { - events.push(evt.properties) - }) // kilocode_change + const event = yield* trackBrowserOpenFailed + yield* authenticateScoped("test-oauth-server") - // kilocode_change start - attach rejection handler before stopping callback server - // Run authenticate with a timeout to avoid waiting forever for the callback - const authPromise = AppRuntime.runPromise( - Effect.gen(function* () { - const mcp = yield* service - return yield* mcp.authenticate("test-oauth-server-2") - }), - ).catch(() => undefined) - // kilocode_change end + const failure = yield* awaitWithTimeout( + Deferred.await(event), + "Timed out waiting for BrowserOpenFailed event", + "5 seconds", + ) - await waitFor(() => openCalledWith !== undefined) // kilocode_change - await new Promise((resolve) => setTimeout(resolve, 600)) // kilocode_change - let authenticate await callbackPromise before stop rejects it + expect(failure.mcpName).toBe("test-oauth-server") + expect(failure.url).toContain("https://") + }), + { config: config("test-oauth-server") }, +) - // Stop the callback server and cancel any pending auth - await McpOAuthCallback.stop() +mcpTest.instance( + "BrowserOpenFailed event is NOT published when open() succeeds", + () => + Effect.gen(function* () { + yield* withCallbackStop + openShouldFail = false - await authPromise + const opened = yield* trackBrowserOpen + const event = yield* trackBrowserOpenFailed + yield* authenticateScoped("test-oauth-server-2") - unsubscribe() + yield* awaitWithTimeout(Deferred.await(opened), "Timed out waiting for open()", "5 seconds") + const failure = yield* Deferred.await(event).pipe(Effect.timeoutOption("700 millis")) - // Verify NO BrowserOpenFailed event was published - expect(events.length).toBe(0) - // Verify open() was still called + expect(failure).toEqual(Option.none()) expect(openCalledWith).toBeDefined() - }, - }) -}) - -test("open() is called with the authorization URL", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - `${dir}/opencode.json`, - JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", - mcp: { - "test-oauth-server-3": { - type: "remote", - url: "https://example.com/mcp", - }, - }, - }), - ) - }, - }) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // kilocode_change - openShouldFail = false // kilocode_change + }), + { config: config("test-oauth-server-2") }, +) + +mcpTest.instance( + "open() is called with the authorization URL", + () => + Effect.gen(function* () { + yield* withCallbackStop + openShouldFail = false openCalledWith = undefined - // kilocode_change start - attach rejection handler before stopping callback server - // Run authenticate with a timeout to avoid waiting forever for the callback - const authPromise = AppRuntime.runPromise( - Effect.gen(function* () { - const mcp = yield* service - return yield* mcp.authenticate("test-oauth-server-3") - }), - ).catch(() => undefined) - // kilocode_change end - - await waitFor(() => openCalledWith !== undefined) // kilocode_change - await new Promise((resolve) => setTimeout(resolve, 600)) // kilocode_change - let authenticate await callbackPromise before stop rejects it - - // Stop the callback server and cancel any pending auth - await McpOAuthCallback.stop() + const opened = yield* trackBrowserOpen + const event = yield* trackBrowserOpenFailed + yield* authenticateScoped("test-oauth-server-3") - await authPromise + const url = yield* awaitWithTimeout(Deferred.await(opened), "Timed out waiting for open()", "5 seconds") + const failure = yield* Deferred.await(event).pipe(Effect.timeoutOption("700 millis")) - // Verify open was called with a URL - expect(openCalledWith).toBeDefined() - expect(typeof openCalledWith).toBe("string") - expect(openCalledWith!).toContain("https://") - }, - }) -}) + expect(failure).toEqual(Option.none()) + expect(typeof url).toBe("string") + expect(url).toContain("https://") + }), + { config: config("test-oauth-server-3") }, +) diff --git a/packages/opencode/test/memory/abort-leak-webfetch.ts b/packages/opencode/test/memory/abort-leak-webfetch.ts deleted file mode 100644 index c3197f8dd55..00000000000 --- a/packages/opencode/test/memory/abort-leak-webfetch.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { abortAfterAny } from "../../src/util/abort" - -const MB = 1024 * 1024 -const ITERATIONS = 50 - -const heap = () => { - Bun.gc(true) - return process.memoryUsage().heapUsed / MB -} - -const server = Bun.serve({ - port: 0, - fetch() { - return new Response("hello from local", { - headers: { - "content-type": "text/plain", - }, - }) - }, -}) - -const url = `http://127.0.0.1:${server.port}` - -async function run() { - const { signal, clearTimeout } = abortAfterAny(30000, new AbortController().signal) - try { - const response = await fetch(url, { signal }) - await response.text() - } finally { - clearTimeout() - } -} - -try { - await run() - Bun.sleepSync(100) - const baseline = heap() - - for (let i = 0; i < ITERATIONS; i++) { - await run() - } - - Bun.sleepSync(100) - const after = heap() - process.stdout.write(JSON.stringify({ baseline, after, growth: after - baseline })) -} finally { - void server.stop(true) - process.exit(0) -} diff --git a/packages/opencode/test/memory/abort-leak.test.ts b/packages/opencode/test/memory/abort-leak.test.ts deleted file mode 100644 index a9e6396bdef..00000000000 --- a/packages/opencode/test/memory/abort-leak.test.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { describe, test, expect } from "bun:test" -import path from "path" - -const projectRoot = path.join(import.meta.dir, "../..") -const worker = path.join(import.meta.dir, "abort-leak-webfetch.ts") - -const MB = 1024 * 1024 -const ITERATIONS = 50 - -const getHeapMB = () => { - Bun.gc(true) - return process.memoryUsage().heapUsed / MB -} - -describe("memory: abort controller leak", () => { - test("webfetch does not leak memory over many invocations", async () => { - // Measure the abort-timed fetch path in a fresh process so shared tool - // runtime state does not dominate the heap signal. - const proc = Bun.spawn({ - cmd: [process.execPath, worker], - cwd: projectRoot, - stdout: "pipe", - stderr: "pipe", - env: process.env, - }) - - const [code, stdout, stderr] = await Promise.all([ - proc.exited, - new Response(proc.stdout).text(), - new Response(proc.stderr).text(), - ]) - - if (code !== 0) { - throw new Error(stderr.trim() || stdout.trim() || `worker exited with code ${code}`) - } - - const result = JSON.parse(stdout.trim()) as { - baseline: number - after: number - growth: number - } - - console.log(`Baseline: ${result.baseline.toFixed(2)} MB`) - console.log(`After ${ITERATIONS} fetches: ${result.after.toFixed(2)} MB`) - console.log(`Growth: ${result.growth.toFixed(2)} MB`) - - // kilocode_change start - // Memory growth should be well below the old closure pattern (~0.5MB/req = ~25MB). - // Windows Bun has higher per-fetch heap overhead (~13MB observed), so use a - // threshold that still catches the leak but accommodates platform variance. - expect(result.growth).toBeLessThan(20) - // kilocode_change end - }, 60000) - - test("compare closure vs bind pattern directly", async () => { - const ITERATIONS = 500 - - // Test OLD pattern: arrow function closure - // Store closures in a map keyed by content to force retention - const closureMap = new Map void>() - const timers: Timer[] = [] - const controllers: AbortController[] = [] - - Bun.gc(true) - Bun.sleepSync(100) - const baseline = getHeapMB() - - for (let i = 0; i < ITERATIONS; i++) { - // Simulate large response body like webfetch would have - const content = `${i}:${"x".repeat(50 * 1024)}` // 50KB unique per iteration - const controller = new AbortController() - controllers.push(controller) - - // OLD pattern - closure captures `content` - const handler = () => { - // Actually use content so it can't be optimized away - if (content.length > 1000000000) controller.abort() - } - closureMap.set(content, handler) - const timeoutId = setTimeout(handler, 30000) - timers.push(timeoutId) - } - - Bun.gc(true) - Bun.sleepSync(100) - const after = getHeapMB() - const oldGrowth = after - baseline - - console.log(`OLD pattern (closure): ${oldGrowth.toFixed(2)} MB growth (${closureMap.size} closures)`) - - // Cleanup after measuring - timers.forEach(clearTimeout) - controllers.forEach((c) => c.abort()) - closureMap.clear() - - // Test NEW pattern: bind - Bun.gc(true) - Bun.sleepSync(100) - const baseline2 = getHeapMB() - const handlers2: (() => void)[] = [] - const timers2: Timer[] = [] - const controllers2: AbortController[] = [] - - for (let i = 0; i < ITERATIONS; i++) { - const _content = `${i}:${"x".repeat(50 * 1024)}` // 50KB - won't be captured - const controller = new AbortController() - controllers2.push(controller) - - // NEW pattern - bind doesn't capture surrounding scope - const handler = controller.abort.bind(controller) - handlers2.push(handler) - const timeoutId = setTimeout(handler, 30000) - timers2.push(timeoutId) - } - - Bun.gc(true) - Bun.sleepSync(100) - const after2 = getHeapMB() - const newGrowth = after2 - baseline2 - - // Cleanup after measuring - timers2.forEach(clearTimeout) - controllers2.forEach((c) => c.abort()) - handlers2.length = 0 - - console.log(`NEW pattern (bind): ${newGrowth.toFixed(2)} MB growth`) - console.log(`Improvement: ${(oldGrowth - newGrowth).toFixed(2)} MB saved`) - - expect(newGrowth).toBeLessThanOrEqual(oldGrowth) - }) -}) diff --git a/packages/opencode/test/patch/patch.test.ts b/packages/opencode/test/patch/patch.test.ts index 020253bfe2d..e4952b9e003 100644 --- a/packages/opencode/test/patch/patch.test.ts +++ b/packages/opencode/test/patch/patch.test.ts @@ -1,8 +1,13 @@ import { describe, test, expect, beforeEach, afterEach } from "bun:test" -import { Patch } from "../../src/patch" +import { Effect } from "effect" import * as fs from "fs/promises" import * as path from "path" import { tmpdir } from "os" +import { Patch } from "../../src/patch" +import { AppFileSystem } from "@opencode-ai/core/filesystem" +import { testEffect } from "../lib/effect" + +const it = testEffect(AppFileSystem.defaultLayer) describe("Patch namespace", () => { let tempDir: string @@ -134,46 +139,53 @@ PATCH` }) describe("applyPatch", () => { - test("should add a new file", async () => { - const patchText = `*** Begin Patch + it.live("should add a new file", () => + Effect.gen(function* () { + const patchText = `*** Begin Patch *** Add File: ${tempDir}/new-file.txt +Hello World +This is a new file *** End Patch` - const result = await Patch.applyPatch(patchText) - expect(result.added).toHaveLength(1) - expect(result.modified).toHaveLength(0) - expect(result.deleted).toHaveLength(0) + const result = yield* Patch.applyPatch(patchText) + expect(result.added).toHaveLength(1) + expect(result.modified).toHaveLength(0) + expect(result.deleted).toHaveLength(0) - const content = await fs.readFile(result.added[0], "utf-8") - expect(content).toBe("Hello World\nThis is a new file") - }) + const content = yield* Effect.promise(() => fs.readFile(result.added[0], "utf-8")) + expect(content).toBe("Hello World\nThis is a new file") + }), + ) - test("should delete an existing file", async () => { - const filePath = path.join(tempDir, "to-delete.txt") - await fs.writeFile(filePath, "This file will be deleted") + it.live("should delete an existing file", () => + Effect.gen(function* () { + const filePath = path.join(tempDir, "to-delete.txt") + yield* Effect.promise(() => fs.writeFile(filePath, "This file will be deleted")) - const patchText = `*** Begin Patch + const patchText = `*** Begin Patch *** Delete File: ${filePath} *** End Patch` - const result = await Patch.applyPatch(patchText) - expect(result.deleted).toHaveLength(1) - expect(result.deleted[0]).toBe(filePath) - - const exists = await fs - .access(filePath) - .then(() => true) - .catch(() => false) - expect(exists).toBe(false) - }) - - test("should update an existing file", async () => { - const filePath = path.join(tempDir, "to-update.txt") - await fs.writeFile(filePath, "line 1\nline 2\nline 3\n") - - const patchText = `*** Begin Patch + const result = yield* Patch.applyPatch(patchText) + expect(result.deleted).toHaveLength(1) + expect(result.deleted[0]).toBe(filePath) + + const exists = yield* Effect.promise(() => + fs + .access(filePath) + .then(() => true) + .catch(() => false), + ) + expect(exists).toBe(false) + }), + ) + + it.live("should update an existing file", () => + Effect.gen(function* () { + const filePath = path.join(tempDir, "to-update.txt") + yield* Effect.promise(() => fs.writeFile(filePath, "line 1\nline 2\nline 3\n")) + + const patchText = `*** Begin Patch *** Update File: ${filePath} @@ line 1 @@ -182,20 +194,22 @@ PATCH` line 3 *** End Patch` - const result = await Patch.applyPatch(patchText) - expect(result.modified).toHaveLength(1) - expect(result.modified[0]).toBe(filePath) + const result = yield* Patch.applyPatch(patchText) + expect(result.modified).toHaveLength(1) + expect(result.modified[0]).toBe(filePath) - const content = await fs.readFile(filePath, "utf-8") - expect(content).toBe("line 1\nline 2 updated\nline 3\n") - }) + const content = yield* Effect.promise(() => fs.readFile(filePath, "utf-8")) + expect(content).toBe("line 1\nline 2 updated\nline 3\n") + }), + ) - test("should move and update a file", async () => { - const oldPath = path.join(tempDir, "old-name.txt") - const newPath = path.join(tempDir, "new-name.txt") - await fs.writeFile(oldPath, "old content\n") + it.live("should move and update a file", () => + Effect.gen(function* () { + const oldPath = path.join(tempDir, "old-name.txt") + const newPath = path.join(tempDir, "new-name.txt") + yield* Effect.promise(() => fs.writeFile(oldPath, "old content\n")) - const patchText = `*** Begin Patch + const patchText = `*** Begin Patch *** Update File: ${oldPath} *** Move to: ${newPath} @@ @@ -203,29 +217,33 @@ PATCH` +new content *** End Patch` - const result = await Patch.applyPatch(patchText) - expect(result.modified).toHaveLength(1) - expect(result.modified[0]).toBe(newPath) - - const oldExists = await fs - .access(oldPath) - .then(() => true) - .catch(() => false) - expect(oldExists).toBe(false) - - const newContent = await fs.readFile(newPath, "utf-8") - expect(newContent).toBe("new content\n") - }) - - test("should handle multiple operations in one patch", async () => { - const file1 = path.join(tempDir, "file1.txt") - const file2 = path.join(tempDir, "file2.txt") - const file3 = path.join(tempDir, "file3.txt") - - await fs.writeFile(file1, "content 1") - await fs.writeFile(file2, "content 2") - - const patchText = `*** Begin Patch + const result = yield* Patch.applyPatch(patchText) + expect(result.modified).toHaveLength(1) + expect(result.modified[0]).toBe(newPath) + + const oldExists = yield* Effect.promise(() => + fs + .access(oldPath) + .then(() => true) + .catch(() => false), + ) + expect(oldExists).toBe(false) + + const newContent = yield* Effect.promise(() => fs.readFile(newPath, "utf-8")) + expect(newContent).toBe("new content\n") + }), + ) + + it.live("should handle multiple operations in one patch", () => + Effect.gen(function* () { + const file1 = path.join(tempDir, "file1.txt") + const file2 = path.join(tempDir, "file2.txt") + const file3 = path.join(tempDir, "file3.txt") + + yield* Effect.promise(() => fs.writeFile(file1, "content 1")) + yield* Effect.promise(() => fs.writeFile(file2, "content 2")) + + const patchText = `*** Begin Patch *** Add File: ${file3} +new file content *** Update File: ${file1} @@ -235,98 +253,114 @@ PATCH` *** Delete File: ${file2} *** End Patch` - const result = await Patch.applyPatch(patchText) - expect(result.added).toHaveLength(1) - expect(result.modified).toHaveLength(1) - expect(result.deleted).toHaveLength(1) - }) + const result = yield* Patch.applyPatch(patchText) + expect(result.added).toHaveLength(1) + expect(result.modified).toHaveLength(1) + expect(result.deleted).toHaveLength(1) + }), + ) - test("should create parent directories when adding files", async () => { - const nestedPath = path.join(tempDir, "deep", "nested", "file.txt") + it.live("should create parent directories when adding files", () => + Effect.gen(function* () { + const nestedPath = path.join(tempDir, "deep", "nested", "file.txt") - const patchText = `*** Begin Patch + const patchText = `*** Begin Patch *** Add File: ${nestedPath} +Deep nested content *** End Patch` - const result = await Patch.applyPatch(patchText) - expect(result.added).toHaveLength(1) - expect(result.added[0]).toBe(nestedPath) - - const exists = await fs - .access(nestedPath) - .then(() => true) - .catch(() => false) - expect(exists).toBe(true) - }) + const result = yield* Patch.applyPatch(patchText) + expect(result.added).toHaveLength(1) + expect(result.added[0]).toBe(nestedPath) + + const exists = yield* Effect.promise(() => + fs + .access(nestedPath) + .then(() => true) + .catch(() => false), + ) + expect(exists).toBe(true) + }), + ) }) describe("error handling", () => { - test("should throw error when updating non-existent file", async () => { - const nonExistent = path.join(tempDir, "does-not-exist.txt") + it.live("should fail when updating non-existent file", () => + Effect.gen(function* () { + const nonExistent = path.join(tempDir, "does-not-exist.txt") - const patchText = `*** Begin Patch + const patchText = `*** Begin Patch *** Update File: ${nonExistent} @@ -old line +new line *** End Patch` - await expect(Patch.applyPatch(patchText)).rejects.toThrow() - }) + const exit = yield* Effect.exit(Patch.applyPatch(patchText)) + expect(exit._tag).toBe("Failure") + }), + ) - test("should throw error when deleting non-existent file", async () => { - const nonExistent = path.join(tempDir, "does-not-exist.txt") + it.live("should fail when deleting non-existent file", () => + Effect.gen(function* () { + const nonExistent = path.join(tempDir, "does-not-exist.txt") - const patchText = `*** Begin Patch + const patchText = `*** Begin Patch *** Delete File: ${nonExistent} *** End Patch` - await expect(Patch.applyPatch(patchText)).rejects.toThrow() - }) + const exit = yield* Effect.exit(Patch.applyPatch(patchText)) + expect(exit._tag).toBe("Failure") + }), + ) }) describe("edge cases", () => { - test("should handle empty files", async () => { - const emptyFile = path.join(tempDir, "empty.txt") - await fs.writeFile(emptyFile, "") + it.live("should handle empty files", () => + Effect.gen(function* () { + const emptyFile = path.join(tempDir, "empty.txt") + yield* Effect.promise(() => fs.writeFile(emptyFile, "")) - const patchText = `*** Begin Patch + const patchText = `*** Begin Patch *** Update File: ${emptyFile} @@ +First line *** End Patch` - const result = await Patch.applyPatch(patchText) - expect(result.modified).toHaveLength(1) + const result = yield* Patch.applyPatch(patchText) + expect(result.modified).toHaveLength(1) - const content = await fs.readFile(emptyFile, "utf-8") - expect(content).toBe("First line\n") - }) + const content = yield* Effect.promise(() => fs.readFile(emptyFile, "utf-8")) + expect(content).toBe("First line\n") + }), + ) - test("should handle files with no trailing newline", async () => { - const filePath = path.join(tempDir, "no-newline.txt") - await fs.writeFile(filePath, "no newline") + it.live("should handle files with no trailing newline", () => + Effect.gen(function* () { + const filePath = path.join(tempDir, "no-newline.txt") + yield* Effect.promise(() => fs.writeFile(filePath, "no newline")) - const patchText = `*** Begin Patch + const patchText = `*** Begin Patch *** Update File: ${filePath} @@ -no newline +has newline now *** End Patch` - const result = await Patch.applyPatch(patchText) - expect(result.modified).toHaveLength(1) + const result = yield* Patch.applyPatch(patchText) + expect(result.modified).toHaveLength(1) - const content = await fs.readFile(filePath, "utf-8") - expect(content).toBe("has newline now\n") - }) + const content = yield* Effect.promise(() => fs.readFile(filePath, "utf-8")) + expect(content).toBe("has newline now\n") + }), + ) - test("should handle multiple update chunks in single file", async () => { - const filePath = path.join(tempDir, "multi-chunk.txt") - await fs.writeFile(filePath, "line 1\nline 2\nline 3\nline 4\n") + it.live("should handle multiple update chunks in single file", () => + Effect.gen(function* () { + const filePath = path.join(tempDir, "multi-chunk.txt") + yield* Effect.promise(() => fs.writeFile(filePath, "line 1\nline 2\nline 3\nline 4\n")) - const patchText = `*** Begin Patch + const patchText = `*** Begin Patch *** Update File: ${filePath} @@ line 1 @@ -338,11 +372,12 @@ PATCH` +LINE 4 *** End Patch` - const result = await Patch.applyPatch(patchText) - expect(result.modified).toHaveLength(1) + const result = yield* Patch.applyPatch(patchText) + expect(result.modified).toHaveLength(1) - const content = await fs.readFile(filePath, "utf-8") - expect(content).toBe("line 1\nLINE 2\nline 3\nLINE 4\n") - }) + const content = yield* Effect.promise(() => fs.readFile(filePath, "utf-8")) + expect(content).toBe("line 1\nLINE 2\nline 3\nLINE 4\n") + }), + ) }) }) diff --git a/packages/opencode/test/permission-task.test.ts b/packages/opencode/test/permission-task.test.ts index 64b93bb8bcb..f2084b095d3 100644 --- a/packages/opencode/test/permission-task.test.ts +++ b/packages/opencode/test/permission-task.test.ts @@ -1,16 +1,12 @@ -import { afterEach, describe, test, expect } from "bun:test" +import { describe, test, expect } from "bun:test" +import { Effect } from "effect" import { Permission } from "../src/permission" import { Config } from "@/config/config" -import { Instance } from "../src/project/instance" -import { WithInstance } from "../src/project/with-instance" -import { disposeAllInstances, tmpdir } from "./fixture/fixture" -import { AppRuntime } from "../src/effect/app-runtime" +import { testEffect } from "./lib/effect" -const load = () => AppRuntime.runPromise(Config.Service.use((svc) => svc.get())) +const it = testEffect(Config.defaultLayer) -afterEach(async () => { - await disposeAllInstances() -}) +const load = Config.Service.use((svc) => svc.get()) describe("Permission.evaluate for permission.task", () => { const createRuleset = (rules: Record): Permission.Ruleset => @@ -147,99 +143,83 @@ describe("Permission.disabled for task tool", () => { // Integration tests that load permissions from real config files describe("permission.task with real config files", () => { - test("loads task permissions from opencode.json config", async () => { - await using tmp = await tmpdir({ - git: true, - config: { - permission: { - task: { - "*": "allow", - "code-reviewer": "deny", - }, - }, - }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const config = await load() + it.instance( + "loads task permissions from opencode.json config", + () => + Effect.gen(function* () { + const config = yield* load const ruleset = Permission.fromConfig(config.permission ?? {}) // general and orchestrator-fast should be allowed, code-reviewer denied expect(Permission.evaluate("task", "general", ruleset).action).toBe("allow") expect(Permission.evaluate("task", "orchestrator-fast", ruleset).action).toBe("allow") expect(Permission.evaluate("task", "code-reviewer", ruleset).action).toBe("deny") - }, - }) - }) - - test("loads task permissions with wildcard patterns from config", async () => { - await using tmp = await tmpdir({ + }), + { git: true, config: { permission: { task: { - "*": "ask", - "orchestrator-*": "deny", + "*": "allow", + "code-reviewer": "deny", }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const config = await load() + }, + ) + + it.instance( + "loads task permissions with wildcard patterns from config", + () => + Effect.gen(function* () { + const config = yield* load const ruleset = Permission.fromConfig(config.permission ?? {}) // general and code-reviewer should be ask, orchestrator-* denied expect(Permission.evaluate("task", "general", ruleset).action).toBe("ask") expect(Permission.evaluate("task", "code-reviewer", ruleset).action).toBe("ask") expect(Permission.evaluate("task", "orchestrator-fast", ruleset).action).toBe("deny") - }, - }) - }) - - test("evaluate respects task permission from config", async () => { - await using tmp = await tmpdir({ + }), + { git: true, config: { permission: { task: { - general: "allow", - "code-reviewer": "deny", + "*": "ask", + "orchestrator-*": "deny", }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const config = await load() + }, + ) + + it.instance( + "evaluate respects task permission from config", + () => + Effect.gen(function* () { + const config = yield* load const ruleset = Permission.fromConfig(config.permission ?? {}) expect(Permission.evaluate("task", "general", ruleset).action).toBe("allow") expect(Permission.evaluate("task", "code-reviewer", ruleset).action).toBe("deny") // Unspecified agents default to "ask" expect(Permission.evaluate("task", "unknown-agent", ruleset).action).toBe("ask") - }, - }) - }) - - test("mixed permission config with task and other tools", async () => { - await using tmp = await tmpdir({ + }), + { git: true, config: { permission: { - bash: "allow", - edit: "ask", task: { - "*": "deny", general: "allow", + "code-reviewer": "deny", }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const config = await load() + }, + ) + + it.instance( + "mixed permission config with task and other tools", + () => + Effect.gen(function* () { + const config = yield* load const ruleset = Permission.fromConfig(config.permission ?? {}) // Verify task permissions @@ -257,27 +237,27 @@ describe("permission.task with real config files", () => { // task is NOT disabled because disabled() uses findLast, and the last rule // matching "task" permission is {pattern: "general", action: "allow"}, not pattern: "*" expect(disabled.has("task")).toBe(false) - }, - }) - }) - - test("task tool disabled when global deny comes last in config", async () => { - await using tmp = await tmpdir({ + }), + { git: true, config: { permission: { + bash: "allow", + edit: "ask", task: { - general: "allow", - "code-reviewer": "allow", "*": "deny", + general: "allow", }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const config = await load() + }, + ) + + it.instance( + "task tool disabled when global deny comes last in config", + () => + Effect.gen(function* () { + const config = yield* load const ruleset = Permission.fromConfig(config.permission ?? {}) // Last matching rule wins - "*" deny is last, so all agents are denied @@ -289,26 +269,26 @@ describe("permission.task with real config files", () => { // and sees pattern: "*" with action: "deny", so task is disabled const disabled = Permission.disabled(["task"], ruleset) expect(disabled.has("task")).toBe(true) - }, - }) - }) - - test("task tool NOT disabled when specific allow comes last in config", async () => { - await using tmp = await tmpdir({ + }), + { git: true, config: { permission: { task: { - "*": "deny", general: "allow", + "code-reviewer": "allow", + "*": "deny", }, }, }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const config = await load() + }, + ) + + it.instance( + "task tool NOT disabled when specific allow comes last in config", + () => + Effect.gen(function* () { + const config = yield* load const ruleset = Permission.fromConfig(config.permission ?? {}) // Evaluate uses findLast - "general" allow comes after "*" deny @@ -321,7 +301,17 @@ describe("permission.task with real config files", () => { // So the task tool is NOT disabled (even though most subagents are denied) const disabled = Permission.disabled(["task"], ruleset) expect(disabled.has("task")).toBe(false) + }), + { + git: true, + config: { + permission: { + task: { + "*": "deny", + general: "allow", + }, + }, }, - }) - }) + }, + ) }) diff --git a/packages/opencode/test/permission/next.test.ts b/packages/opencode/test/permission/next.test.ts index 479af6ba584..a084a4664a0 100644 --- a/packages/opencode/test/permission/next.test.ts +++ b/packages/opencode/test/permission/next.test.ts @@ -1,57 +1,27 @@ -// kilocode_change start -import { afterAll, afterEach, test, expect } from "bun:test" -import fs from "fs/promises" -// kilocode_change end +import { test, expect } from "bun:test" import os from "os" -import path from "path" // kilocode_change -import { Cause, Effect, Exit, Fiber, Layer } from "effect" +import { Cause, Deferred, Effect, Exit, Fiber, Layer } from "effect" import { Bus } from "../../src/bus" -// kilocode_change start -import { Config } from "../../src/config/config" -import { Global } from "@opencode-ai/core/global" -// kilocode_change end import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { Permission } from "../../src/permission" import { PermissionID } from "../../src/permission/schema" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" -import { InstanceRuntime } from "../../src/project/instance-runtime" -import { - disposeAllInstances, - provideInstance, - provideTmpdirInstance, - reloadTestInstance, - tmpdirScoped, -} from "../fixture/fixture" +import { InstanceBootstrap } from "../../src/project/bootstrap-service" +import { InstanceStore } from "../../src/project/instance-store" +import { TestInstance, tmpdirScoped } from "../fixture/fixture" import { testEffect } from "../lib/effect" import { MessageID, SessionID } from "../../src/session/schema" +import { RuntimeFlags } from "../../src/effect/runtime-flags" +import { Config } from "../../src/config/config" const bus = Bus.layer -// kilocode_change start +const noopBootstrap = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void })) const env = Layer.mergeAll( - Permission.layer.pipe(Layer.provide(bus), Layer.provide(Config.defaultLayer)), + Permission.layer.pipe(Layer.provide(bus)), bus, CrossSpawnSpawner.defaultLayer, -) -// kilocode_change end -const it = testEffect(env) - -afterEach(async () => { - await disposeAllInstances() -}) - -// kilocode_change start -afterAll(async () => { - const dir = Global.Path.config - for (const file of ["kilo.jsonc", "kilo.json", "config.json", "opencode.json", "opencode.jsonc"]) { - await fs.rm(path.join(dir, file), { force: true }).catch(() => {}) - } - await Effect.runPromise( - Config.Service.use((svc) => svc.invalidate()).pipe(Effect.scoped, Effect.provide(Config.defaultLayer)), - ) - await InstanceRuntime.disposeAllInstances() -}) -// kilocode_change end + InstanceStore.defaultLayer.pipe(Layer.provide(noopBootstrap)), +).pipe(Layer.provide(RuntimeFlags.layer()), Layer.provide(Config.defaultLayer)) +const it = testEffect(Layer.mergeAll(env, RuntimeFlags.layer())) const rejectAll = (message?: string) => Effect.gen(function* () { @@ -68,12 +38,18 @@ const rejectAll = (message?: string) => const waitForPending = (count: number) => Effect.gen(function* () { const permission = yield* Permission.Service - for (let i = 0; i < 100; i++) { - const list = yield* permission.list() - if (list.length === count) return list - yield* Effect.sleep("10 millis") - } - return yield* Effect.fail(new Error(`timed out waiting for ${count} pending permission request(s)`)) + return yield* Effect.gen(function* () { + while (true) { + const list = yield* permission.list() + if (list.length === count) return list + yield* Effect.sleep("10 millis") + } + }).pipe( + Effect.timeoutOrElse({ + duration: "1 second", + orElse: () => Effect.fail(new Error(`timed out waiting for ${count} pending permission request(s)`)), + }), + ) }) const fail = (self: Effect.Effect) => @@ -101,14 +77,6 @@ const list = () => return yield* permission.list() }) -function withDir(options: { git?: boolean } | undefined, self: (dir: string) => Effect.Effect) { - return provideTmpdirInstance(self, options) -} - -function withProvided(dir: string) { - return (self: Effect.Effect) => self.pipe(provideInstance(dir)) -} - // fromConfig tests test("fromConfig - string value becomes wildcard rule", () => { @@ -590,8 +558,9 @@ test("disabled - specific allow overrides wildcard deny", () => { // ask tests -it.live("ask - resolves immediately when action is allow", () => - withDir({ git: true }, () => +it.instance( + "ask - resolves immediately when action is allow", + () => Effect.gen(function* () { const result = yield* ask({ sessionID: SessionID.make("session_test"), @@ -603,11 +572,12 @@ it.live("ask - resolves immediately when action is allow", () => }) expect(result).toBeUndefined() }), - ), + { git: true }, ) -it.live("ask - throws DeniedError when action is deny", () => - withDir({ git: true }, () => +it.instance( + "ask - throws DeniedError when action is deny", + () => Effect.gen(function* () { const err = yield* fail( ask({ @@ -621,11 +591,12 @@ it.live("ask - throws DeniedError when action is deny", () => ) expect(err).toBeInstanceOf(Permission.DeniedError) }), - ), + { git: true }, ) -it.live("ask - stays pending when action is ask", () => - withDir({ git: true }, () => +it.instance( + "ask - stays pending when action is ask", + () => Effect.gen(function* () { const fiber = yield* ask({ sessionID: SessionID.make("session_test"), @@ -640,11 +611,12 @@ it.live("ask - stays pending when action is ask", () => yield* rejectAll() yield* Fiber.await(fiber) }), - ), + { git: true }, ) -it.live("ask - adds request to pending list", () => - withDir({ git: true }, () => +it.instance( + "ask - adds request to pending list", + () => Effect.gen(function* () { const fiber = yield* ask({ sessionID: SessionID.make("session_test"), @@ -676,53 +648,58 @@ it.live("ask - adds request to pending list", () => yield* rejectAll() yield* Fiber.await(fiber) }), - ), + { git: true }, ) -it.live("ask - publishes asked event", () => - withDir({ git: true }, () => +it.instance( + "ask - publishes asked event", + () => Effect.gen(function* () { const bus = yield* Bus.Service - let seen: Permission.Request | undefined + const seen = yield* Deferred.make() const unsub = yield* bus.subscribeCallback(Permission.Event.Asked, (event) => { - seen = event.properties + Deferred.doneUnsafe(seen, Effect.succeed(event.properties)) }) + yield* Effect.addFinalizer(() => Effect.sync(unsub)) - try { - const fiber = yield* ask({ - sessionID: SessionID.make("session_test"), - permission: "bash", - patterns: ["ls"], - metadata: { cmd: "ls" }, - always: ["ls"], - tool: { - messageID: MessageID.make("msg_test"), - callID: "call_test", - }, - ruleset: [], - }).pipe(Effect.forkScoped) + const fiber = yield* ask({ + sessionID: SessionID.make("session_test"), + permission: "bash", + patterns: ["ls"], + metadata: { cmd: "ls" }, + always: ["ls"], + tool: { + messageID: MessageID.make("msg_test"), + callID: "call_test", + }, + ruleset: [], + }).pipe(Effect.forkScoped) - expect(yield* waitForPending(1)).toHaveLength(1) - expect(seen).toBeDefined() - expect(seen).toMatchObject({ - sessionID: SessionID.make("session_test"), - permission: "bash", - patterns: ["ls"], - }) + expect(yield* waitForPending(1)).toHaveLength(1) + expect( + yield* Deferred.await(seen).pipe( + Effect.timeoutOrElse({ + duration: "1 second", + orElse: () => Effect.fail(new Error("timed out waiting for permission asked event")), + }), + ), + ).toMatchObject({ + sessionID: SessionID.make("session_test"), + permission: "bash", + patterns: ["ls"], + }) - yield* rejectAll() - yield* Fiber.await(fiber) - } finally { - unsub() - } + yield* rejectAll() + yield* Fiber.await(fiber) }), - ), + { git: true }, ) // reply tests -it.live("reply - once resolves the pending ask", () => - withDir({ git: true }, () => +it.instance( + "reply - once resolves the pending ask", + () => Effect.gen(function* () { const fiber = yield* ask({ id: PermissionID.make("per_test1"), @@ -738,11 +715,12 @@ it.live("reply - once resolves the pending ask", () => yield* reply({ requestID: PermissionID.make("per_test1"), reply: "once" }) yield* Fiber.join(fiber) }), - ), + { git: true }, ) -it.live("reply - reject throws RejectedError", () => - withDir({ git: true }, () => +it.instance( + "reply - reject throws RejectedError", + () => Effect.gen(function* () { const fiber = yield* ask({ id: PermissionID.make("per_test2"), @@ -761,11 +739,12 @@ it.live("reply - reject throws RejectedError", () => expect(Exit.isFailure(exit)).toBe(true) if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Permission.RejectedError) }), - ), + { git: true }, ) -it.live("reply - reject with message throws CorrectedError", () => - withDir({ git: true }, () => +it.instance( + "reply - reject with message throws CorrectedError", + () => Effect.gen(function* () { const fiber = yield* ask({ id: PermissionID.make("per_test2b"), @@ -792,102 +771,43 @@ it.live("reply - reject with message throws CorrectedError", () => expect(String(err)).toContain("Use a safer command") } }), - ), + { git: true }, ) -it.live("reply - always persists approval and resolves", () => - Effect.gen(function* () { - const dir = yield* tmpdirScoped({ git: true }) - const run = withProvided(dir) - const fiber = yield* ask({ - id: PermissionID.make("per_test3"), - sessionID: SessionID.make("session_test"), - permission: "bash", - patterns: ["ls"], - metadata: {}, - always: ["ls"], - ruleset: [], - }).pipe(run, Effect.forkScoped) - - yield* waitForPending(1).pipe(run) - yield* reply({ requestID: PermissionID.make("per_test3"), reply: "always" }).pipe(run) - yield* Fiber.join(fiber) - - const result = yield* ask({ - sessionID: SessionID.make("session_test2"), - permission: "bash", - patterns: ["ls"], - metadata: {}, - always: [], - ruleset: [], - }).pipe(run) - expect(result).toBeUndefined() - }), -) - -// kilocode_change start - session-scoped allowEverything enable -it.live("allowEverything - session-scoped enable stays within one session", () => - withDir({ git: true }, () => +it.instance( + "reply - always persists approval and resolves", + () => Effect.gen(function* () { - const permission = yield* Permission.Service - - const first = yield* ask({ - id: PermissionID.make("permission_session_allow"), - sessionID: SessionID.make("session_allowed"), + const fiber = yield* ask({ + id: PermissionID.make("per_test3"), + sessionID: SessionID.make("session_test"), permission: "bash", - patterns: ["pwd"], + patterns: ["ls"], metadata: {}, - always: [], + always: ["ls"], ruleset: [], }).pipe(Effect.forkScoped) yield* waitForPending(1) - yield* permission.allowEverything({ - enable: true, - requestID: PermissionID.make("permission_session_allow"), - sessionID: SessionID.make("session_allowed"), - }) - - yield* Fiber.join(first) + yield* reply({ requestID: PermissionID.make("per_test3"), reply: "always" }) + yield* Fiber.join(fiber) - const allowed = yield* ask({ - sessionID: SessionID.make("session_allowed"), + const result = yield* ask({ + sessionID: SessionID.make("session_test2"), permission: "bash", patterns: ["ls"], metadata: {}, always: [], ruleset: [], }) - expect(allowed).toBeUndefined() - - const blocked = yield* ask({ - id: PermissionID.make("permission_session_blocked"), - sessionID: SessionID.make("session_blocked"), - permission: "bash", - patterns: ["ls"], - metadata: {}, - always: [], - ruleset: [], - }).pipe(Effect.forkScoped) - - yield* waitForPending(1) - yield* reply({ - requestID: PermissionID.make("permission_session_blocked"), - reply: "reject", - }) - - const exit = yield* Fiber.await(blocked) - expect(Exit.isFailure(exit)).toBe(true) - if (Exit.isFailure(exit)) { - expect(Cause.squash(exit.cause)).toBeInstanceOf(Permission.RejectedError) - } + expect(result).toBeUndefined() }), - ), + { git: true }, ) -// kilocode_change end -it.live("reply - reject cancels all pending for same session", () => - withDir({ git: true }, () => +it.instance( + "reply - reject cancels all pending for same session", + () => Effect.gen(function* () { const a = yield* ask({ id: PermissionID.make("per_test4a"), @@ -918,11 +838,12 @@ it.live("reply - reject cancels all pending for same session", () => if (Exit.isFailure(ea)) expect(Cause.squash(ea.cause)).toBeInstanceOf(Permission.RejectedError) if (Exit.isFailure(eb)) expect(Cause.squash(eb.cause)).toBeInstanceOf(Permission.RejectedError) }), - ), + { git: true }, ) -it.live("reply - always resolves matching pending requests in same session", () => - withDir({ git: true }, () => +it.instance( + "reply - always resolves matching pending requests in same session", + () => Effect.gen(function* () { const a = yield* ask({ id: PermissionID.make("per_test5a"), @@ -951,12 +872,12 @@ it.live("reply - always resolves matching pending requests in same session", () yield* Fiber.join(b) expect(yield* list()).toHaveLength(0) }), - ), + { git: true }, ) -// kilocode_change start -it.live("reply - always resolves matching pending requests from other sessions", () => - withDir({ git: true }, () => +it.instance( + "reply - always resolves matching pending requests from other sessions", + () => Effect.gen(function* () { const a = yield* ask({ id: PermissionID.make("per_test6a"), @@ -985,60 +906,15 @@ it.live("reply - always resolves matching pending requests from other sessions", yield* Fiber.join(b) expect(yield* list()).toHaveLength(0) }), - ), + { git: true }, ) -it.live("reply - always does not resolve dangerous variants from other sessions", () => - withDir({ git: true }, () => - Effect.gen(function* () { - const a = yield* ask({ - id: PermissionID.make("per_test_safe_a"), - sessionID: SessionID.make("session_a"), - permission: "bash", - patterns: ["npm test"], - metadata: {}, - always: ["npm test"], - ruleset: [], - }).pipe(Effect.forkScoped) - - const b = yield* ask({ - id: PermissionID.make("per_test_safe_b"), - sessionID: SessionID.make("session_b"), - permission: "bash", - patterns: ["npm test > ~/.ssh/authorized_keys"], - metadata: {}, - always: [], - ruleset: [], - }).pipe(Effect.forkScoped) - - yield* waitForPending(2) - yield* reply({ requestID: PermissionID.make("per_test_safe_a"), reply: "always" }) - - yield* Fiber.join(a) - expect((yield* list()).map((item) => item.id)).toEqual([PermissionID.make("per_test_safe_b")]) - - yield* rejectAll() - yield* Fiber.await(b) - }), - ), -) -// kilocode_change end - -it.live("reply - publishes replied event", () => - withDir({ git: true }, () => +it.instance( + "reply - publishes replied event", + () => Effect.gen(function* () { const bus = yield* Bus.Service - let resolve!: (value: { sessionID: SessionID; requestID: PermissionID; reply: Permission.Reply }) => void - const seen = Effect.promise<{ - sessionID: SessionID - requestID: PermissionID - reply: Permission.Reply - }>( - () => - new Promise((res) => { - resolve = res - }), - ) + const seen = yield* Deferred.make<{ sessionID: SessionID; requestID: PermissionID; reply: Permission.Reply }>() const fiber = yield* ask({ id: PermissionID.make("per_test7"), @@ -1053,129 +929,146 @@ it.live("reply - publishes replied event", () => yield* waitForPending(1) const unsub = yield* bus.subscribeCallback(Permission.Event.Replied, (event) => { - resolve(event.properties) + Deferred.doneUnsafe(seen, Effect.succeed(event.properties)) }) + yield* Effect.addFinalizer(() => Effect.sync(unsub)) - try { - yield* reply({ requestID: PermissionID.make("per_test7"), reply: "once" }) - yield* Fiber.join(fiber) - expect(yield* seen).toEqual({ - sessionID: SessionID.make("session_test"), - requestID: PermissionID.make("per_test7"), - reply: "once", - }) - } finally { - unsub() - } + yield* reply({ requestID: PermissionID.make("per_test7"), reply: "once" }) + yield* Fiber.join(fiber) + expect( + yield* Deferred.await(seen).pipe( + Effect.timeoutOrElse({ + duration: "1 second", + orElse: () => Effect.fail(new Error("timed out waiting for permission replied event")), + }), + ), + ).toEqual({ + sessionID: SessionID.make("session_test"), + requestID: PermissionID.make("per_test7"), + reply: "once", + }) }), - ), + { git: true }, ) it.live("permission requests stay isolated by directory", () => Effect.gen(function* () { const one = yield* tmpdirScoped({ git: true }) const two = yield* tmpdirScoped({ git: true }) - const runOne = withProvided(one) - const runTwo = withProvided(two) - - const a = yield* ask({ - id: PermissionID.make("per_dir_a"), - sessionID: SessionID.make("session_dir_a"), - permission: "bash", - patterns: ["ls"], - metadata: {}, - always: [], - ruleset: [], - }).pipe(runOne, Effect.forkScoped) - - const b = yield* ask({ - id: PermissionID.make("per_dir_b"), - sessionID: SessionID.make("session_dir_b"), - permission: "bash", - patterns: ["pwd"], - metadata: {}, - always: [], - ruleset: [], - }).pipe(runTwo, Effect.forkScoped) - - const onePending = yield* waitForPending(1).pipe(runOne) - const twoPending = yield* waitForPending(1).pipe(runTwo) + const store = yield* InstanceStore.Service + + const a = yield* store + .provide( + { directory: one }, + ask({ + id: PermissionID.make("per_dir_a"), + sessionID: SessionID.make("session_dir_a"), + permission: "bash", + patterns: ["ls"], + metadata: {}, + always: [], + ruleset: [], + }), + ) + .pipe(Effect.forkScoped) + + const b = yield* store + .provide( + { directory: two }, + ask({ + id: PermissionID.make("per_dir_b"), + sessionID: SessionID.make("session_dir_b"), + permission: "bash", + patterns: ["pwd"], + metadata: {}, + always: [], + ruleset: [], + }), + ) + .pipe(Effect.forkScoped) + + const onePending = yield* store.provide({ directory: one }, waitForPending(1)) + const twoPending = yield* store.provide({ directory: two }, waitForPending(1)) expect(onePending).toHaveLength(1) expect(twoPending).toHaveLength(1) expect(onePending[0].id).toBe(PermissionID.make("per_dir_a")) expect(twoPending[0].id).toBe(PermissionID.make("per_dir_b")) - yield* reply({ requestID: onePending[0].id, reply: "reject" }).pipe(runOne) - yield* reply({ requestID: twoPending[0].id, reply: "reject" }).pipe(runTwo) + yield* store.provide({ directory: one }, reply({ requestID: onePending[0].id, reply: "reject" })) + yield* store.provide({ directory: two }, reply({ requestID: twoPending[0].id, reply: "reject" })) yield* Fiber.await(a) yield* Fiber.await(b) }), ) -it.live("pending permission rejects on instance dispose", () => - Effect.gen(function* () { - const dir = yield* tmpdirScoped({ git: true }) - const run = withProvided(dir) - const fiber = yield* ask({ - id: PermissionID.make("per_dispose"), - sessionID: SessionID.make("session_dispose"), - permission: "bash", - patterns: ["ls"], - metadata: {}, - always: [], - ruleset: [], - }).pipe(run, Effect.forkScoped) - - expect(yield* waitForPending(1).pipe(run)).toHaveLength(1) - yield* Effect.promise(() => - WithInstance.provide({ directory: dir, fn: () => void InstanceRuntime.disposeInstance(Instance.current) }), - ) +it.instance( + "pending permission rejects on instance dispose", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const store = yield* InstanceStore.Service + const fiber = yield* ask({ + id: PermissionID.make("per_dispose"), + sessionID: SessionID.make("session_dispose"), + permission: "bash", + patterns: ["ls"], + metadata: {}, + always: [], + ruleset: [], + }).pipe(Effect.forkScoped) - const exit = yield* Fiber.await(fiber) - expect(Exit.isFailure(exit)).toBe(true) - if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Permission.RejectedError) - }), + expect(yield* waitForPending(1)).toHaveLength(1) + const ctx = yield* store.load({ directory: test.directory }) + yield* store.dispose(ctx) + + const exit = yield* Fiber.await(fiber) + expect(Exit.isFailure(exit)).toBe(true) + if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Permission.RejectedError) + }), + { git: true }, ) -it.live("pending permission rejects on instance reload", () => - Effect.gen(function* () { - const dir = yield* tmpdirScoped({ git: true }) - const run = withProvided(dir) - const fiber = yield* ask({ - id: PermissionID.make("per_reload"), - sessionID: SessionID.make("session_reload"), - permission: "bash", - patterns: ["ls"], - metadata: {}, - always: [], - ruleset: [], - }).pipe(run, Effect.forkScoped) - - expect(yield* waitForPending(1).pipe(run)).toHaveLength(1) - yield* Effect.promise(() => reloadTestInstance({ directory: dir })) - - const exit = yield* Fiber.await(fiber) - expect(Exit.isFailure(exit)).toBe(true) - if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Permission.RejectedError) - }), +it.instance( + "pending permission rejects on instance reload", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const store = yield* InstanceStore.Service + const fiber = yield* ask({ + id: PermissionID.make("per_reload"), + sessionID: SessionID.make("session_reload"), + permission: "bash", + patterns: ["ls"], + metadata: {}, + always: [], + ruleset: [], + }).pipe(Effect.forkScoped) + + expect(yield* waitForPending(1)).toHaveLength(1) + yield* store.reload({ directory: test.directory }) + + const exit = yield* Fiber.await(fiber) + expect(Exit.isFailure(exit)).toBe(true) + if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Permission.RejectedError) + }), + { git: true }, ) -// kilocode_change start -it.live("reply - returns false for unknown requestID", () => - withDir({ git: true }, () => +it.instance( + "reply - does nothing for unknown requestID", + () => Effect.gen(function* () { - const accepted = yield* reply({ requestID: PermissionID.make("per_unknown"), reply: "once" }) - expect(accepted).toBe(false) + yield* reply({ requestID: PermissionID.make("per_unknown"), reply: "once" }) expect(yield* list()).toHaveLength(0) }), - ), + { git: true }, ) -// kilocode_change end -it.live("ask - checks all patterns and stops on first deny", () => - withDir({ git: true }, () => +it.instance( + "ask - checks all patterns and stops on first deny", + () => Effect.gen(function* () { const err = yield* fail( ask({ @@ -1192,11 +1085,12 @@ it.live("ask - checks all patterns and stops on first deny", () => ) expect(err).toBeInstanceOf(Permission.DeniedError) }), - ), + { git: true }, ) -it.live("ask - allows all patterns when all match allow rules", () => - withDir({ git: true }, () => +it.instance( + "ask - allows all patterns when all match allow rules", + () => Effect.gen(function* () { const result = yield* ask({ sessionID: SessionID.make("session_test"), @@ -1208,11 +1102,12 @@ it.live("ask - allows all patterns when all match allow rules", () => }) expect(result).toBeUndefined() }), - ), + { git: true }, ) -it.live("ask - should deny even when an earlier pattern is ask", () => - withDir({ git: true }, () => +it.instance( + "ask - should deny even when an earlier pattern is ask", + () => Effect.gen(function* () { const err = yield* fail( ask({ @@ -1231,30 +1126,33 @@ it.live("ask - should deny even when an earlier pattern is ask", () => expect(err).toBeInstanceOf(Permission.DeniedError) expect(yield* list()).toHaveLength(0) }), - ), + { git: true }, ) -it.live("ask - abort should clear pending request", () => - Effect.gen(function* () { - const dir = yield* tmpdirScoped({ git: true }) - const run = withProvided(dir) - - const fiber = yield* ask({ - id: PermissionID.make("per_reload"), - sessionID: SessionID.make("session_reload"), - permission: "bash", - patterns: ["ls"], - metadata: {}, - always: [], - ruleset: [{ permission: "bash", pattern: "*", action: "ask" }], - }).pipe(run, Effect.forkScoped) - - const pending = yield* waitForPending(1).pipe(run) - expect(pending).toHaveLength(1) - yield* Effect.promise(() => reloadTestInstance({ directory: dir })) - - const exit = yield* Fiber.await(fiber) - expect(Exit.isFailure(exit)).toBe(true) - if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Permission.RejectedError) - }), +it.instance( + "ask - abort should clear pending request", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const store = yield* InstanceStore.Service + + const fiber = yield* ask({ + id: PermissionID.make("per_reload"), + sessionID: SessionID.make("session_reload"), + permission: "bash", + patterns: ["ls"], + metadata: {}, + always: [], + ruleset: [{ permission: "bash", pattern: "*", action: "ask" }], + }).pipe(Effect.forkScoped) + + const pending = yield* waitForPending(1) + expect(pending).toHaveLength(1) + yield* store.reload({ directory: test.directory }) + + const exit = yield* Fiber.await(fiber) + expect(Exit.isFailure(exit)).toBe(true) + if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Permission.RejectedError) + }), + { git: true }, ) diff --git a/packages/opencode/test/plugin/auth-override.test.ts b/packages/opencode/test/plugin/auth-override.test.ts index 761ba8ab7ed..a4365873568 100644 --- a/packages/opencode/test/plugin/auth-override.test.ts +++ b/packages/opencode/test/plugin/auth-override.test.ts @@ -1,16 +1,21 @@ import { describe, expect, test } from "bun:test" import path from "path" -import fs from "fs/promises" import { pathToFileURL } from "url" import { Effect, Layer } from "effect" -import { provideTestInstance, tmpdir } from "../fixture/fixture" +import { AppFileSystem } from "@opencode-ai/core/filesystem" +import { provideInstance, TestInstance, tmpdirScoped } from "../fixture/fixture" import { ProviderAuth } from "@/provider/auth" import { ProviderID } from "../../src/provider/schema" import { Plugin } from "@/plugin" +import { RuntimeFlags } from "@/effect/runtime-flags" import { Auth } from "@/auth" import { ModelCache } from "@/provider/model-cache" // kilocode_change import { Bus } from "@/bus" import { TestConfig } from "../fixture/config" +import { testEffect } from "../lib/effect" +import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" + +const it = testEffect(Layer.mergeAll(CrossSpawnSpawner.defaultLayer, AppFileSystem.defaultLayer)) function layer(directory: string, plugins: string[]) { return ProviderAuth.layer.pipe( @@ -19,6 +24,7 @@ function layer(directory: string, plugins: string[]) { Layer.provide( Plugin.layer.pipe( Layer.provide(Bus.layer), + Layer.provide(RuntimeFlags.layer()), Layer.provide( TestConfig.layer({ get: () => @@ -39,13 +45,15 @@ function layer(directory: string, plugins: string[]) { } describe("plugin.auth-override", () => { - test("user plugin overrides built-in github-copilot auth", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - const pluginDir = path.join(dir, ".opencode", "plugin") - await fs.mkdir(pluginDir, { recursive: true }) + it.instance( + "user plugin overrides built-in github-copilot auth", + () => + Effect.gen(function* () { + const tmp = yield* TestInstance + const fs = yield* AppFileSystem.Service + const pluginDir = path.join(tmp.directory, ".opencode", "plugin") - await Bun.write( + yield* fs.writeWithDirs( path.join(pluginDir, "custom-copilot-auth.ts"), [ "export default {", @@ -63,37 +71,26 @@ describe("plugin.auth-override", () => { "", ].join("\n"), ) - }, - }) - await using plain = await tmpdir() + const plain = yield* tmpdirScoped({ git: true }) + const plugin = pathToFileURL(path.join(pluginDir, "custom-copilot-auth.ts")).href + const methods = yield* ProviderAuth.Service.use((svc) => svc.methods()).pipe( + Effect.provide(layer(tmp.directory, [plugin])), + ) + const plainMethods = yield* ProviderAuth.Service.use((svc) => svc.methods()).pipe( + Effect.provide(layer(plain, [])), + provideInstance(plain), + ) - const plugin = pathToFileURL(path.join(tmp.path, ".opencode", "plugin", "custom-copilot-auth.ts")).href - const [methods, plainMethods] = await Promise.all([ - provideTestInstance({ - directory: tmp.path, - fn: async () => { - return Effect.runPromise( - ProviderAuth.Service.use((svc) => svc.methods()).pipe(Effect.provide(layer(tmp.path, [plugin]))), - ) - }, + const copilot = methods[ProviderID.make("github-copilot")] + expect(copilot).toBeDefined() + expect(copilot.length).toBe(1) + expect(copilot[0].label).toBe("Test Override Auth") + expect(plainMethods[ProviderID.make("github-copilot")][0].label).not.toBe("Test Override Auth") }), - provideTestInstance({ - directory: plain.path, - fn: async () => { - return Effect.runPromise( - ProviderAuth.Service.use((svc) => svc.methods()).pipe(Effect.provide(layer(plain.path, []))), - ) - }, - }), - ]) - - const copilot = methods[ProviderID.make("github-copilot")] - expect(copilot).toBeDefined() - expect(copilot.length).toBe(1) - expect(copilot[0].label).toBe("Test Override Auth") - expect(plainMethods[ProviderID.make("github-copilot")][0].label).not.toBe("Test Override Auth") - }, 30000) + { git: true }, + 30000, + ) }) const file = path.join(import.meta.dir, "../../src/plugin/index.ts") diff --git a/packages/opencode/test/plugin/loader-shared.test.ts b/packages/opencode/test/plugin/loader-shared.test.ts index 384df3ddf69..c283488632c 100644 --- a/packages/opencode/test/plugin/loader-shared.test.ts +++ b/packages/opencode/test/plugin/loader-shared.test.ts @@ -1,13 +1,12 @@ -import { afterAll, afterEach, describe, expect, spyOn, test } from "bun:test" +import { afterEach, describe, expect, spyOn } from "bun:test" import { Effect, Layer } from "effect" import fs from "fs/promises" import path from "path" import { pathToFileURL } from "url" -import { disposeAllInstances, provideInstance, tmpdir } from "../fixture/fixture" -import { Filesystem } from "@/util/filesystem" - -const disableDefault = process.env.KILO_DISABLE_DEFAULT_PLUGINS -process.env.KILO_DISABLE_DEFAULT_PLUGINS = "1" +import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" +import { AppFileSystem } from "@opencode-ai/core/filesystem" +import { disposeAllInstances, provideInstance, tmpdirScoped } from "../fixture/fixture" +import { testEffect } from "../lib/effect" const { Plugin } = await import("../../src/plugin/index") const { PluginLoader } = await import("../../src/plugin/loader") @@ -15,51 +14,61 @@ const { readPackageThemes } = await import("../../src/plugin/shared") const { Bus } = await import("../../src/bus") const { Npm } = await import("@opencode-ai/core/npm") const { TestConfig } = await import("../fixture/config") - -afterAll(() => { - if (disableDefault === undefined) { - delete process.env.KILO_DISABLE_DEFAULT_PLUGINS - return - } - process.env.KILO_DISABLE_DEFAULT_PLUGINS = disableDefault -}) +const { RuntimeFlags } = await import("../../src/effect/runtime-flags") afterEach(async () => { await disposeAllInstances() }) -async function load(dir: string) { +const it = testEffect(Layer.mergeAll(CrossSpawnSpawner.defaultLayer, AppFileSystem.defaultLayer)) + +function withTmp( + init: (dir: string) => Promise, + body: (tmp: { path: string; extra: T }) => Effect.Effect, +) { + return Effect.gen(function* () { + const dir = yield* tmpdirScoped() + const extra = yield* Effect.promise(() => init(dir)) + return yield* body({ path: dir, extra }) + }) +} + +function load(dir: string, flags?: Parameters[0]) { const source = path.join(dir, "opencode.json") - const config = (await Bun.file(source).json()) as { plugin?: Array]> } - const plugins = config.plugin ?? [] return Effect.gen(function* () { - const plugin = yield* Plugin.Service - yield* plugin.list() - }).pipe( - Effect.provide( - Plugin.layer.pipe( - Layer.provide(Bus.layer), - Layer.provide( - TestConfig.layer({ - get: () => - Effect.succeed({ - plugin: plugins, - plugin_origins: plugins.map((plugin) => ({ spec: plugin, source, scope: "local" as const })), - }), - directories: () => Effect.succeed([dir]), - }), + const config = yield* Effect.promise( + () => Bun.file(source).json() as Promise<{ plugin?: Array]> }>, + ) + const plugins = config.plugin ?? [] + return yield* Effect.gen(function* () { + const plugin = yield* Plugin.Service + yield* plugin.list() + }).pipe( + Effect.provide( + Plugin.layer.pipe( + Layer.provide(Bus.layer), + Layer.provide(RuntimeFlags.layer({ disableDefaultPlugins: true, ...flags })), + Layer.provide( + TestConfig.layer({ + get: () => + Effect.succeed({ + plugin: plugins, + plugin_origins: plugins.map((plugin) => ({ spec: plugin, source, scope: "local" as const })), + }), + directories: () => Effect.succeed([dir]), + }), + ), ), ), - ), - provideInstance(dir), - Effect.runPromise, - ) + provideInstance(dir), + ) + }) } describe("plugin.loader.shared", () => { - test("loads a file:// plugin function export", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("loads a file:// plugin function export", () => + withTmp( + async (dir) => { const file = path.join(dir, "plugin.ts") const mark = path.join(dir, "called.txt") await Bun.write( @@ -80,15 +89,17 @@ describe("plugin.loader.shared", () => { return { mark } }, - }) - - await load(tmp.path) - expect(await fs.readFile(tmp.extra.mark, "utf8")).toBe("called") - }) + (tmp) => + Effect.gen(function* () { + yield* load(tmp.path) + expect(yield* Effect.promise(() => fs.readFile(tmp.extra.mark, "utf8"))).toBe("called") + }), + ), + ) - test("deduplicates same function exported as default and named", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("deduplicates same function exported as default and named", () => + withTmp( + async (dir) => { const file = path.join(dir, "plugin.ts") const mark = path.join(dir, "count.txt") await Bun.write(mark, "") @@ -113,15 +124,17 @@ describe("plugin.loader.shared", () => { return { mark } }, - }) - - await load(tmp.path) - expect(await fs.readFile(tmp.extra.mark, "utf8")).toBe("1") - }) + (tmp) => + Effect.gen(function* () { + yield* load(tmp.path) + expect(yield* Effect.promise(() => fs.readFile(tmp.extra.mark, "utf8"))).toBe("1") + }), + ), + ) - test("uses only default v1 server plugin when present", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("uses only default v1 server plugin when present", () => + withTmp( + async (dir) => { const file = path.join(dir, "plugin.ts") const mark = path.join(dir, "count.txt") await Bun.write( @@ -149,15 +162,17 @@ describe("plugin.loader.shared", () => { return { mark } }, - }) - - await load(tmp.path) - expect(await Bun.file(tmp.extra.mark).text()).toBe("default") - }) + (tmp) => + Effect.gen(function* () { + yield* load(tmp.path) + expect(yield* Effect.promise(() => Bun.file(tmp.extra.mark).text())).toBe("default") + }), + ), + ) - test("rejects v1 file server plugin without id", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("rejects v1 file server plugin without id", () => + withTmp( + async (dir) => { const file = path.join(dir, "plugin.ts") const mark = path.join(dir, "called.txt") await Bun.write( @@ -180,20 +195,24 @@ describe("plugin.loader.shared", () => { return { mark } }, - }) - - await load(tmp.path) - const called = await Bun.file(tmp.extra.mark) - .text() - .then(() => true) - .catch(() => false) - - expect(called).toBe(false) - }) + (tmp) => + Effect.gen(function* () { + yield* load(tmp.path) + const called = yield* Effect.promise(() => + Bun.file(tmp.extra.mark) + .text() + .then(() => true) + .catch(() => false), + ) + + expect(called).toBe(false) + }), + ), + ) - test("rejects v1 plugin that exports server and tui together", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("rejects v1 plugin that exports server and tui together", () => + withTmp( + async (dir) => { const file = path.join(dir, "plugin.ts") const mark = path.join(dir, "called.txt") await Bun.write( @@ -218,20 +237,24 @@ describe("plugin.loader.shared", () => { return { mark } }, - }) - - await load(tmp.path) - const called = await Bun.file(tmp.extra.mark) - .text() - .then(() => true) - .catch(() => false) - - expect(called).toBe(false) - }) + (tmp) => + Effect.gen(function* () { + yield* load(tmp.path) + const called = yield* Effect.promise(() => + Bun.file(tmp.extra.mark) + .text() + .then(() => true) + .catch(() => false), + ) + + expect(called).toBe(false) + }), + ), + ) - test("resolves npm plugin specs with explicit and default versions", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("resolves npm plugin specs with explicit and default versions", () => + withTmp( + async (dir) => { const acme = path.join(dir, "node_modules", "acme-plugin") const scope = path.join(dir, "node_modules", "scope-plugin") await fs.mkdir(acme, { recursive: true }) @@ -254,26 +277,28 @@ describe("plugin.loader.shared", () => { return { acme, scope } }, - }) - - const add = spyOn(Npm, "add").mockImplementation(async (pkg) => { - if (pkg === "acme-plugin") return { directory: tmp.extra.acme, entrypoint: undefined } - return { directory: tmp.extra.scope, entrypoint: undefined } - }) - - try { - await load(tmp.path) - - expect(add.mock.calls).toContainEqual(["acme-plugin@latest"]) - expect(add.mock.calls).toContainEqual(["scope-plugin@2.3.4"]) - } finally { - add.mockRestore() - } - }) + (tmp) => + Effect.gen(function* () { + const add = spyOn(Npm, "add").mockImplementation(async (pkg) => { + if (pkg === "acme-plugin") return { directory: tmp.extra.acme, entrypoint: undefined } + return { directory: tmp.extra.scope, entrypoint: undefined } + }) + + try { + yield* load(tmp.path) + + expect(add.mock.calls).toContainEqual(["acme-plugin@latest"]) + expect(add.mock.calls).toContainEqual(["scope-plugin@2.3.4"]) + } finally { + add.mockRestore() + } + }), + ), + ) - test("loads npm server plugin from package ./server export", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("loads npm server plugin from package ./server export", () => + withTmp( + async (dir) => { const mod = path.join(dir, "mods", "acme-plugin") const mark = path.join(dir, "server-called.txt") await fs.mkdir(mod, { recursive: true }) @@ -317,21 +342,23 @@ describe("plugin.loader.shared", () => { mark, } }, - }) - - const install = spyOn(Npm, "add").mockResolvedValue({ directory: tmp.extra.mod, entrypoint: undefined }) - - try { - await load(tmp.path) - expect(await Bun.file(tmp.extra.mark).text()).toBe("called") - } finally { - install.mockRestore() - } - }) + (tmp) => + Effect.gen(function* () { + const install = spyOn(Npm, "add").mockResolvedValue({ directory: tmp.extra.mod, entrypoint: undefined }) + + try { + yield* load(tmp.path) + expect(yield* Effect.promise(() => Bun.file(tmp.extra.mark).text())).toBe("called") + } finally { + install.mockRestore() + } + }), + ), + ) - test("loads npm server plugin from package server export without leading dot", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("loads npm server plugin from package server export without leading dot", () => + withTmp( + async (dir) => { const mod = path.join(dir, "mods", "acme-plugin") const dist = path.join(mod, "dist") const mark = path.join(dir, "server-called.txt") @@ -374,21 +401,23 @@ describe("plugin.loader.shared", () => { mark, } }, - }) - - const install = spyOn(Npm, "add").mockResolvedValue({ directory: tmp.extra.mod, entrypoint: undefined }) - - try { - await load(tmp.path) - expect(await Bun.file(tmp.extra.mark).text()).toBe("called") - } finally { - install.mockRestore() - } - }) + (tmp) => + Effect.gen(function* () { + const install = spyOn(Npm, "add").mockResolvedValue({ directory: tmp.extra.mod, entrypoint: undefined }) + + try { + yield* load(tmp.path) + expect(yield* Effect.promise(() => Bun.file(tmp.extra.mark).text())).toBe("called") + } finally { + install.mockRestore() + } + }), + ), + ) - test("loads npm server plugin from package main without leading dot", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("loads npm server plugin from package main without leading dot", () => + withTmp( + async (dir) => { const mod = path.join(dir, "mods", "acme-plugin") const dist = path.join(mod, "dist") const mark = path.join(dir, "main-called.txt") @@ -426,21 +455,23 @@ describe("plugin.loader.shared", () => { mark, } }, - }) - - const install = spyOn(Npm, "add").mockResolvedValue({ directory: tmp.extra.mod, entrypoint: undefined }) - - try { - await load(tmp.path) - expect(await Bun.file(tmp.extra.mark).text()).toBe("called") - } finally { - install.mockRestore() - } - }) + (tmp) => + Effect.gen(function* () { + const install = spyOn(Npm, "add").mockResolvedValue({ directory: tmp.extra.mod, entrypoint: undefined }) + + try { + yield* load(tmp.path) + expect(yield* Effect.promise(() => Bun.file(tmp.extra.mark).text())).toBe("called") + } finally { + install.mockRestore() + } + }), + ), + ) - test("does not use npm package exports dot for server entry", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("does not use npm package exports dot for server entry", () => + withTmp( + async (dir) => { const mod = path.join(dir, "mods", "acme-plugin") const mark = path.join(dir, "dot-server.txt") await fs.mkdir(mod, { recursive: true }) @@ -471,26 +502,30 @@ describe("plugin.loader.shared", () => { return { mod, mark } }, - }) - - const install = spyOn(Npm, "add").mockResolvedValue({ directory: tmp.extra.mod, entrypoint: undefined }) - - try { - await load(tmp.path) - const called = await Bun.file(tmp.extra.mark) - .text() - .then(() => true) - .catch(() => false) - - expect(called).toBe(false) - } finally { - install.mockRestore() - } - }) + (tmp) => + Effect.gen(function* () { + const install = spyOn(Npm, "add").mockResolvedValue({ directory: tmp.extra.mod, entrypoint: undefined }) + + try { + yield* load(tmp.path) + const called = yield* Effect.promise(() => + Bun.file(tmp.extra.mark) + .text() + .then(() => true) + .catch(() => false), + ) + + expect(called).toBe(false) + } finally { + install.mockRestore() + } + }), + ), + ) - test("rejects npm server export that resolves outside plugin directory", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("rejects npm server export that resolves outside plugin directory", () => + withTmp( + async (dir) => { const mod = path.join(dir, "mods", "acme-plugin") const outside = path.join(dir, "outside") const mark = path.join(dir, "outside-server.txt") @@ -534,25 +569,29 @@ describe("plugin.loader.shared", () => { mark, } }, - }) - - const install = spyOn(Npm, "add").mockResolvedValue({ directory: tmp.extra.mod, entrypoint: undefined }) - - try { - await load(tmp.path) - const called = await Bun.file(tmp.extra.mark) - .text() - .then(() => true) - .catch(() => false) - expect(called).toBe(false) - } finally { - install.mockRestore() - } - }) + (tmp) => + Effect.gen(function* () { + const install = spyOn(Npm, "add").mockResolvedValue({ directory: tmp.extra.mod, entrypoint: undefined }) + + try { + yield* load(tmp.path) + const called = yield* Effect.promise(() => + Bun.file(tmp.extra.mark) + .text() + .then(() => true) + .catch(() => false), + ) + expect(called).toBe(false) + } finally { + install.mockRestore() + } + }), + ), + ) - test("skips legacy codex and copilot auth plugin specs", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("skips legacy codex and copilot auth plugin specs", () => + withTmp( + async (dir) => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify( @@ -564,25 +603,27 @@ describe("plugin.loader.shared", () => { ), ) }, - }) - - const install = spyOn(Npm, "add").mockResolvedValue({ directory: "", entrypoint: undefined }) - - try { - await load(tmp.path) - - const pkgs = install.mock.calls.map((call) => call[0]) - expect(pkgs).toContain("regular-plugin@1.0.0") - expect(pkgs).not.toContain("opencode-openai-codex-auth@1.0.0") - expect(pkgs).not.toContain("opencode-copilot-auth@1.0.0") - } finally { - install.mockRestore() - } - }) + (_tmp) => + Effect.gen(function* () { + const install = spyOn(Npm, "add").mockResolvedValue({ directory: "", entrypoint: undefined }) + + try { + yield* load(_tmp.path) + + const pkgs = install.mock.calls.map((call) => call[0]) + expect(pkgs).toContain("regular-plugin@1.0.0") + expect(pkgs).not.toContain("opencode-openai-codex-auth@1.0.0") + expect(pkgs).not.toContain("opencode-copilot-auth@1.0.0") + } finally { + install.mockRestore() + } + }), + ), + ) - test("skips broken plugin when install fails", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("skips broken plugin when install fails", () => + withTmp( + async (dir) => { const ok = path.join(dir, "ok.ts") const mark = path.join(dir, "ok.txt") await Bun.write( @@ -604,22 +645,24 @@ describe("plugin.loader.shared", () => { ) return { mark } }, - }) - - const install = spyOn(Npm, "add").mockRejectedValue(new Error("boom")) - - try { - await load(tmp.path) - expect(install).toHaveBeenCalledWith("broken-plugin@9.9.9") - expect(await Bun.file(tmp.extra.mark).text()).toBe("ok") - } finally { - install.mockRestore() - } - }) + (tmp) => + Effect.gen(function* () { + const install = spyOn(Npm, "add").mockRejectedValue(new Error("boom")) + + try { + yield* load(tmp.path) + expect(install).toHaveBeenCalledWith("broken-plugin@9.9.9") + expect(yield* Effect.promise(() => Bun.file(tmp.extra.mark).text())).toBe("ok") + } finally { + install.mockRestore() + } + }), + ), + ) - test("continues loading plugins when plugin init throws", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("continues loading plugins when plugin init throws", () => + withTmp( + async (dir) => { const file = pathToFileURL(path.join(dir, "throws.ts")).href const ok = pathToFileURL(path.join(dir, "ok.ts")).href const mark = path.join(dir, "ok.txt") @@ -653,15 +696,17 @@ describe("plugin.loader.shared", () => { return { mark } }, - }) - - await load(tmp.path) - expect(await Bun.file(tmp.extra.mark).text()).toBe("ok") - }) + (tmp) => + Effect.gen(function* () { + yield* load(tmp.path) + expect(yield* Effect.promise(() => Bun.file(tmp.extra.mark).text())).toBe("ok") + }), + ), + ) - test("continues loading plugins when plugin module has invalid export", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("continues loading plugins when plugin module has invalid export", () => + withTmp( + async (dir) => { const file = pathToFileURL(path.join(dir, "invalid.ts")).href const ok = pathToFileURL(path.join(dir, "ok.ts")).href const mark = path.join(dir, "ok.txt") @@ -687,15 +732,17 @@ describe("plugin.loader.shared", () => { return { mark } }, - }) - - await load(tmp.path) - expect(await Bun.file(tmp.extra.mark).text()).toBe("ok") - }) + (tmp) => + Effect.gen(function* () { + yield* load(tmp.path) + expect(yield* Effect.promise(() => Bun.file(tmp.extra.mark).text())).toBe("ok") + }), + ), + ) - test("continues loading plugins when plugin import fails", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("continues loading plugins when plugin import fails", () => + withTmp( + async (dir) => { const missing = pathToFileURL(path.join(dir, "missing-plugin.ts")).href const ok = pathToFileURL(path.join(dir, "ok.ts")).href const mark = path.join(dir, "ok.txt") @@ -716,15 +763,17 @@ describe("plugin.loader.shared", () => { return { mark } }, - }) - - await load(tmp.path) - expect(await Bun.file(tmp.extra.mark).text()).toBe("ok") - }) + (tmp) => + Effect.gen(function* () { + yield* load(tmp.path) + expect(yield* Effect.promise(() => Bun.file(tmp.extra.mark).text())).toBe("ok") + }), + ), + ) - test("loads object plugin via plugin.server", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("loads object plugin via plugin.server", () => + withTmp( + async (dir) => { const file = path.join(dir, "object-plugin.ts") const mark = path.join(dir, "object-called.txt") await Bun.write( @@ -749,15 +798,17 @@ describe("plugin.loader.shared", () => { return { mark } }, - }) - - await load(tmp.path) - expect(await fs.readFile(tmp.extra.mark, "utf8")).toBe("called") - }) + (tmp) => + Effect.gen(function* () { + yield* load(tmp.path) + expect(yield* Effect.promise(() => fs.readFile(tmp.extra.mark, "utf8"))).toBe("called") + }), + ), + ) - test("passes tuple plugin options into server plugin", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("passes tuple plugin options into server plugin", () => + withTmp( + async (dir) => { const file = path.join(dir, "options-plugin.ts") const mark = path.join(dir, "options.json") await Bun.write( @@ -782,18 +833,22 @@ describe("plugin.loader.shared", () => { return { mark } }, - }) - - await load(tmp.path) - expect(await Filesystem.readJson<{ source: string; enabled: boolean }>(tmp.extra.mark)).toEqual({ - source: "tuple", - enabled: true, - }) - }) + (tmp) => + Effect.gen(function* () { + yield* load(tmp.path) + expect( + (yield* (yield* AppFileSystem.Service).readJson(tmp.extra.mark)) as { source: string; enabled: boolean }, + ).toEqual({ + source: "tuple", + enabled: true, + }) + }), + ), + ) - test("initializes server plugins in config order", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("initializes server plugins in config order", () => + withTmp( + async (dir) => { const a = path.join(dir, "a-plugin.ts") const b = path.join(dir, "b-plugin.ts") const marker = path.join(dir, "server-order.txt") @@ -833,16 +888,18 @@ export default { return { marker } }, - }) - - await load(tmp.path) - const lines = (await fs.readFile(tmp.extra.marker, "utf8")).trim().split("\n") - expect(lines).toEqual(["a-start", "a-end", "b"]) - }) + (tmp) => + Effect.gen(function* () { + yield* load(tmp.path) + const lines = (yield* Effect.promise(() => fs.readFile(tmp.extra.marker, "utf8"))).trim().split("\n") + expect(lines).toEqual(["a-start", "a-end", "b"]) + }), + ), + ) - test("skips external plugins in pure mode", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("skips external plugins in pure mode", () => + withTmp( + async (dir) => { const file = path.join(dir, "plugin.ts") const mark = path.join(dir, "called.txt") await Bun.write( @@ -866,30 +923,23 @@ export default { return { mark } }, - }) - - const pure = process.env.KILO_PURE - process.env.KILO_PURE = "1" - - try { - await load(tmp.path) - const called = await fs - .readFile(tmp.extra.mark, "utf8") - .then(() => true) - .catch(() => false) - expect(called).toBe(false) - } finally { - if (pure === undefined) { - delete process.env.KILO_PURE - } else { - process.env.KILO_PURE = pure - } - } - }) + (tmp) => + Effect.gen(function* () { + yield* load(tmp.path, { pure: true }) + const called = yield* Effect.promise(() => + fs + .readFile(tmp.extra.mark, "utf8") + .then(() => true) + .catch(() => false), + ) + expect(called).toBe(false) + }), + ), + ) - test("reads oc-themes from package manifest", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("reads oc-themes from package manifest", () => + withTmp( + async (dir) => { const mod = path.join(dir, "mod") await fs.mkdir(path.join(mod, "themes"), { recursive: true }) await Bun.write( @@ -907,25 +957,28 @@ export default { return { mod } }, - }) - - const file = path.join(tmp.extra.mod, "package.json") - const json = await Filesystem.readJson>(file) - const list = readPackageThemes("acme-plugin", { - dir: tmp.extra.mod, - pkg: file, - json, - }) - - expect(list).toEqual([ - Filesystem.resolve(path.join(tmp.extra.mod, "themes", "one.json")), - Filesystem.resolve(path.join(tmp.extra.mod, "themes", "two.json")), - ]) - }) + (tmp) => + Effect.gen(function* () { + const file = path.join(tmp.extra.mod, "package.json") + const fsys = yield* AppFileSystem.Service + const json = (yield* fsys.readJson(file)) as Record + const list = readPackageThemes("acme-plugin", { + dir: tmp.extra.mod, + pkg: file, + json, + }) + + expect(list).toEqual([ + AppFileSystem.resolve(path.join(tmp.extra.mod, "themes", "one.json")), + AppFileSystem.resolve(path.join(tmp.extra.mod, "themes", "two.json")), + ]) + }), + ), + ) - test("handles no-entrypoint tui packages via missing callback", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("handles no-entrypoint tui packages via missing callback", () => + withTmp( + async (dir) => { const mod = path.join(dir, "mods", "acme-plugin") await fs.mkdir(path.join(mod, "themes"), { recursive: true }) await Bun.write( @@ -943,54 +996,58 @@ export default { await Bun.write(path.join(mod, "themes", "night.json"), "{}\n") return { mod } }, - }) - - const install = spyOn(Npm, "add").mockResolvedValue({ directory: tmp.extra.mod, entrypoint: undefined }) - const missing: string[] = [] - - try { - const loaded = await PluginLoader.loadExternal({ - items: [ - { - spec: "acme-plugin@1.0.0", - scope: "local" as const, - source: tmp.path, - }, - ], - kind: "tui", - missing: async (item) => { - if (!item.pkg) return - const themes = readPackageThemes(item.spec, item.pkg) - if (!themes.length) return - return { - spec: item.spec, - target: item.target, - themes, + (tmp) => + Effect.gen(function* () { + const install = spyOn(Npm, "add").mockResolvedValue({ directory: tmp.extra.mod, entrypoint: undefined }) + const missing: string[] = [] + + try { + const loaded = yield* Effect.promise(() => + PluginLoader.loadExternal({ + items: [ + { + spec: "acme-plugin@1.0.0", + scope: "local" as const, + source: tmp.path, + }, + ], + kind: "tui", + missing: async (item) => { + if (!item.pkg) return + const themes = readPackageThemes(item.spec, item.pkg) + if (!themes.length) return + return { + spec: item.spec, + target: item.target, + themes, + } + }, + report: { + missing(_candidate, _retry, message) { + missing.push(message) + }, + }, + }), + ) + + expect(loaded).toEqual([ + { + spec: "acme-plugin@1.0.0", + target: tmp.extra.mod, + themes: [AppFileSystem.resolve(path.join(tmp.extra.mod, "themes", "night.json"))], + }, + ]) + expect(missing).toHaveLength(0) + } finally { + install.mockRestore() } - }, - report: { - missing(_candidate, _retry, message) { - missing.push(message) - }, - }, - }) - - expect(loaded).toEqual([ - { - spec: "acme-plugin@1.0.0", - target: tmp.extra.mod, - themes: [Filesystem.resolve(path.join(tmp.extra.mod, "themes", "night.json"))], - }, - ]) - expect(missing).toHaveLength(0) - } finally { - install.mockRestore() - } - }) + }), + ), + ) - test("passes package metadata for entrypoint tui plugins", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("passes package metadata for entrypoint tui plugins", () => + withTmp( + async (dir) => { const mod = path.join(dir, "mods", "acme-plugin") await fs.mkdir(path.join(mod, "themes"), { recursive: true }) await Bun.write( @@ -1012,64 +1069,71 @@ export default { await Bun.write(path.join(mod, "themes", "night.json"), "{}\n") return { mod } }, - }) - - const install = spyOn(Npm, "add").mockResolvedValue({ directory: tmp.extra.mod, entrypoint: undefined }) - - try { - const loaded = await PluginLoader.loadExternal({ - items: [ - { - spec: "acme-plugin@1.0.0", - scope: "local" as const, - source: tmp.path, - }, - ], - kind: "tui", - finish: async (item) => { - if (!item.pkg) return - return { - spec: item.spec, - themes: readPackageThemes(item.spec, item.pkg), + (tmp) => + Effect.gen(function* () { + const install = spyOn(Npm, "add").mockResolvedValue({ directory: tmp.extra.mod, entrypoint: undefined }) + + try { + const loaded = yield* Effect.promise(() => + PluginLoader.loadExternal({ + items: [ + { + spec: "acme-plugin@1.0.0", + scope: "local" as const, + source: tmp.path, + }, + ], + kind: "tui", + finish: async (item) => { + if (!item.pkg) return + return { + spec: item.spec, + themes: readPackageThemes(item.spec, item.pkg), + } + }, + }), + ) + + expect(loaded).toEqual([ + { + spec: "acme-plugin@1.0.0", + themes: [AppFileSystem.resolve(path.join(tmp.extra.mod, "themes", "night.json"))], + }, + ]) + } finally { + install.mockRestore() } - }, - }) - - expect(loaded).toEqual([ - { - spec: "acme-plugin@1.0.0", - themes: [Filesystem.resolve(path.join(tmp.extra.mod, "themes", "night.json"))], - }, - ]) - } finally { - install.mockRestore() - } - }) + }), + ), + ) - test("rejects oc-themes path traversal", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("rejects oc-themes path traversal", () => + withTmp( + async (dir) => { const mod = path.join(dir, "mod") await fs.mkdir(mod, { recursive: true }) const file = path.join(mod, "package.json") await Bun.write(file, JSON.stringify({ name: "acme", "oc-themes": ["../escape.json"] }, null, 2)) return { mod, file } }, - }) - - const json = await Filesystem.readJson>(tmp.extra.file) - expect(() => - readPackageThemes("acme", { - dir: tmp.extra.mod, - pkg: tmp.extra.file, - json, - }), - ).toThrow("outside plugin directory") - }) + (tmp) => + Effect.gen(function* () { + const fsys = yield* AppFileSystem.Service + const json = (yield* fsys.readJson(tmp.extra.file)) as Record + expect(() => + readPackageThemes("acme", { + dir: tmp.extra.mod, + pkg: tmp.extra.file, + json, + }), + ).toThrow("outside plugin directory") + }), + ), + ) - test("retries failed file plugins once after wait and keeps order", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("retries failed file plugins once after wait and keeps order", () => + withTmp( + async (dir) => { const a = path.join(dir, "a") const b = path.join(dir, "b") const aSpec = pathToFileURL(a).href @@ -1078,110 +1142,122 @@ export default { await fs.mkdir(b, { recursive: true }) return { a, b, aSpec, bSpec } }, - }) - - let wait = 0 - const calls: Array<[string, boolean]> = [] - - const loaded = await PluginLoader.loadExternal({ - items: [tmp.extra.aSpec, tmp.extra.bSpec].map((spec) => ({ - spec, - scope: "local" as const, - source: tmp.path, - })), - kind: "tui", - wait: async () => { - wait += 1 - await Bun.write(path.join(tmp.extra.a, "index.ts"), "export default {}\n") - await Bun.write(path.join(tmp.extra.b, "index.ts"), "export default {}\n") - }, - report: { - start(candidate, retry) { - calls.push([candidate.plan.spec, retry]) - }, - }, - }) - - expect(wait).toBe(1) - expect(calls).toEqual([ - [tmp.extra.aSpec, false], - [tmp.extra.bSpec, false], - [tmp.extra.aSpec, true], - [tmp.extra.bSpec, true], - ]) - expect(loaded.map((item) => item.spec)).toEqual([tmp.extra.aSpec, tmp.extra.bSpec]) - }) + (tmp) => + Effect.gen(function* () { + let wait = 0 + const calls: Array<[string, boolean]> = [] + + const loaded = yield* Effect.promise(() => + PluginLoader.loadExternal({ + items: [tmp.extra.aSpec, tmp.extra.bSpec].map((spec) => ({ + spec, + scope: "local" as const, + source: tmp.path, + })), + kind: "tui", + wait: async () => { + wait += 1 + await Bun.write(path.join(tmp.extra.a, "index.ts"), "export default {}\n") + await Bun.write(path.join(tmp.extra.b, "index.ts"), "export default {}\n") + }, + report: { + start(candidate, retry) { + calls.push([candidate.plan.spec, retry]) + }, + }, + }), + ) + + expect(wait).toBe(1) + expect(calls).toEqual([ + [tmp.extra.aSpec, false], + [tmp.extra.bSpec, false], + [tmp.extra.aSpec, true], + [tmp.extra.bSpec, true], + ]) + expect(loaded.map((item) => item.spec)).toEqual([tmp.extra.aSpec, tmp.extra.bSpec]) + }), + ), + ) - test("retries file plugins when finish returns undefined", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { + it.live("retries file plugins when finish returns undefined", () => + withTmp( + async (dir) => { const file = path.join(dir, "plugin.ts") const spec = pathToFileURL(file).href await Bun.write(file, "export default {}\n") return { spec } }, - }) - - let wait = 0 - let count = 0 - - const loaded = await PluginLoader.loadExternal({ - items: [ - { - spec: tmp.extra.spec, - scope: "local" as const, - source: tmp.path, - }, - ], - kind: "tui", - wait: async () => { - wait += 1 - }, - finish: async (load, _item, retry) => { - count += 1 - if (!retry) return - return { - retry, - spec: load.spec, - } - }, - }) + (tmp) => + Effect.gen(function* () { + let wait = 0 + let count = 0 + + const loaded = yield* Effect.promise(() => + PluginLoader.loadExternal({ + items: [ + { + spec: tmp.extra.spec, + scope: "local" as const, + source: tmp.path, + }, + ], + kind: "tui", + wait: async () => { + wait += 1 + }, + finish: async (load, _item, retry) => { + count += 1 + if (!retry) return + return { + retry, + spec: load.spec, + } + }, + }), + ) - expect(wait).toBe(1) - expect(count).toBe(2) - expect(loaded).toEqual([{ retry: true, spec: tmp.extra.spec }]) - }) + expect(wait).toBe(1) + expect(count).toBe(2) + expect(loaded).toEqual([{ retry: true, spec: tmp.extra.spec }]) + }), + ), + ) - test("does not wait or retry npm plugin failures", async () => { - const install = spyOn(Npm, "add").mockRejectedValue(new Error("boom")) - let wait = 0 - const errors: Array<[string, boolean]> = [] - - try { - const loaded = await PluginLoader.loadExternal({ - items: [ - { - spec: "acme-plugin@1.0.0", - scope: "local" as const, - source: "test", - }, - ], - kind: "tui", - wait: async () => { - wait += 1 - }, - report: { - error(_candidate, retry, stage) { - errors.push([stage, retry]) - }, - }, - }) - - expect(loaded).toEqual([]) - expect(wait).toBe(0) - expect(errors).toEqual([["install", false]]) - } finally { - install.mockRestore() - } - }) + it.live("does not wait or retry npm plugin failures", () => + Effect.gen(function* () { + const install = spyOn(Npm, "add").mockRejectedValue(new Error("boom")) + let wait = 0 + const errors: Array<[string, boolean]> = [] + + try { + const loaded = yield* Effect.promise(() => + PluginLoader.loadExternal({ + items: [ + { + spec: "acme-plugin@1.0.0", + scope: "local" as const, + source: "test", + }, + ], + kind: "tui", + wait: async () => { + wait += 1 + }, + report: { + error(_candidate, retry, stage) { + errors.push([stage, retry]) + }, + }, + }), + ) + + expect(loaded).toEqual([]) + expect(wait).toBe(0) + expect(errors).toEqual([["install", false]]) + } finally { + install.mockRestore() + } + }), + ) }) diff --git a/packages/opencode/test/plugin/trigger.test.ts b/packages/opencode/test/plugin/trigger.test.ts index d9c0dfa8c2b..94642fba629 100644 --- a/packages/opencode/test/plugin/trigger.test.ts +++ b/packages/opencode/test/plugin/trigger.test.ts @@ -1,26 +1,48 @@ -import { afterAll, describe, expect } from "bun:test" -import { Effect, Layer } from "effect" +import { describe, expect } from "bun:test" +import { Effect, Layer, Option } from "effect" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" +import { AppFileSystem } from "@opencode-ai/core/filesystem" +import { EffectFlock } from "@opencode-ai/core/util/effect-flock" import path from "path" import { pathToFileURL } from "url" +import { Account } from "../../src/account/account" +import { Auth } from "../../src/auth" +import { Bus } from "../../src/bus" +import { Config } from "../../src/config/config" +import { Env } from "../../src/env" +import { RuntimeFlags } from "../../src/effect/runtime-flags" +import { Plugin } from "../../src/plugin/index" import { ModelID, ProviderID } from "../../src/provider/schema" import { provideTmpdirInstance } from "../fixture/fixture" import { testEffect } from "../lib/effect" +import { NpmTest } from "../fake/npm" -const disableDefault = process.env.KILO_DISABLE_DEFAULT_PLUGINS -process.env.KILO_DISABLE_DEFAULT_PLUGINS = "1" - -const { Plugin } = await import("../../src/plugin/index") -const it = testEffect(Layer.mergeAll(Plugin.defaultLayer, CrossSpawnSpawner.defaultLayer)) -const systemHook = "experimental.chat.system.transform" - -afterAll(() => { - if (disableDefault === undefined) { - delete process.env.KILO_DISABLE_DEFAULT_PLUGINS - return - } - process.env.KILO_DISABLE_DEFAULT_PLUGINS = disableDefault +const emptyAccount = Layer.mock(Account.Service)({ + active: () => Effect.succeed(Option.none()), + activeOrg: () => Effect.succeed(Option.none()), }) +const emptyAuth = Layer.mock(Auth.Service)({ + all: () => Effect.succeed({}), +}) +const configLayer = Config.layer.pipe( + Layer.provide(EffectFlock.defaultLayer), + Layer.provide(AppFileSystem.defaultLayer), + Layer.provide(Env.defaultLayer), + Layer.provide(emptyAuth), + Layer.provide(emptyAccount), + Layer.provide(NpmTest.noop), +) +const it = testEffect( + Layer.mergeAll( + Plugin.layer.pipe( + Layer.provide(Bus.layer), + Layer.provide(configLayer), + Layer.provide(RuntimeFlags.layer({ disableDefaultPlugins: true })), + ), + CrossSpawnSpawner.defaultLayer, + ), +) +const systemHook = "experimental.chat.system.transform" function withProject(source: string, self: Effect.Effect) { return provideTmpdirInstance((dir) => diff --git a/packages/opencode/test/plugin/workspace-adapter.test.ts b/packages/opencode/test/plugin/workspace-adapter.test.ts index 503400c685e..41dbf53445d 100644 --- a/packages/opencode/test/plugin/workspace-adapter.test.ts +++ b/packages/opencode/test/plugin/workspace-adapter.test.ts @@ -1,44 +1,70 @@ -import { afterAll, afterEach, describe, expect } from "bun:test" -import { Effect, Layer } from "effect" +import { afterEach, describe, expect } from "bun:test" +import { Effect, Layer, Option } from "effect" +import { FetchHttpClient } from "effect/unstable/http" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" +import { AppFileSystem } from "@opencode-ai/core/filesystem" +import { EffectFlock } from "@opencode-ai/core/util/effect-flock" import path from "path" import { pathToFileURL } from "url" +import { Account } from "../../src/account/account" +import { Auth } from "../../src/auth" +import { Bus } from "../../src/bus" +import { Config } from "../../src/config/config" +import { Env } from "../../src/env" +import { RuntimeFlags } from "../../src/effect/runtime-flags" +import { Workspace } from "../../src/control-plane/workspace" +import { Plugin } from "../../src/plugin/index" +import { InstanceBootstrap } from "../../src/project/bootstrap-service" +import { Instance } from "../../src/project/instance" +import { InstanceStore } from "../../src/project/instance-store" +import { Project } from "../../src/project/project" +import { Vcs } from "../../src/project/vcs" +import { Session } from "../../src/session/session" +import { SessionPrompt } from "../../src/session/prompt" +import { SyncEvent } from "../../src/sync" import { disposeAllInstances, provideTmpdirInstance } from "../fixture/fixture" import { testEffect } from "../lib/effect" +import { NpmTest } from "../fake/npm" -const disableDefault = process.env.KILO_DISABLE_DEFAULT_PLUGINS -process.env.KILO_DISABLE_DEFAULT_PLUGINS = "1" - -const { Flag } = await import("@opencode-ai/core/flag/flag") -const { Plugin } = await import("../../src/plugin/index") -const { Workspace } = await import("../../src/control-plane/workspace") -const { InstanceBootstrap } = await import("../../src/project/bootstrap") -const { Instance } = await import("../../src/project/instance") -const { InstanceStore } = await import("../../src/project/instance-store") -const workspaceLayer = Workspace.defaultLayer.pipe( - Layer.provide(InstanceStore.defaultLayer), - Layer.provide(InstanceBootstrap.defaultLayer), +const emptyAccount = Layer.mock(Account.Service)({ + active: () => Effect.succeed(Option.none()), + activeOrg: () => Effect.succeed(Option.none()), +}) +const emptyAuth = Layer.mock(Auth.Service)({ + all: () => Effect.succeed({}), +}) +const configLayer = Config.layer.pipe( + Layer.provide(EffectFlock.defaultLayer), + Layer.provide(AppFileSystem.defaultLayer), + Layer.provide(Env.defaultLayer), + Layer.provide(emptyAuth), + Layer.provide(emptyAccount), + Layer.provide(NpmTest.noop), ) -const it = testEffect(Layer.mergeAll(Plugin.defaultLayer, workspaceLayer, CrossSpawnSpawner.defaultLayer)) - -const experimental = Flag.KILO_EXPERIMENTAL_WORKSPACES - -Flag.KILO_EXPERIMENTAL_WORKSPACES = true +const pluginLayer = Plugin.layer.pipe( + Layer.provide(Bus.layer), + Layer.provide(configLayer), + Layer.provide(RuntimeFlags.layer({ disableDefaultPlugins: true })), +) +const noopBootstrapLayer = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void })) +const workspaceLayer = Workspace.layer.pipe( + Layer.provide(Auth.defaultLayer), + Layer.provide(Session.defaultLayer), + Layer.provide(SyncEvent.defaultLayer), + Layer.provide(SessionPrompt.defaultLayer), + Layer.provide(Project.defaultLayer), + Layer.provide(Vcs.defaultLayer), + Layer.provide(FetchHttpClient.layer), + Layer.provide(AppFileSystem.defaultLayer), + Layer.provide(InstanceStore.defaultLayer.pipe(Layer.provide(noopBootstrapLayer))), + Layer.provide(RuntimeFlags.layer({ experimentalWorkspaces: true })), +) +const it = testEffect(Layer.mergeAll(pluginLayer, workspaceLayer, CrossSpawnSpawner.defaultLayer)) afterEach(async () => { await disposeAllInstances() }) -afterAll(() => { - if (disableDefault === undefined) { - delete process.env.KILO_DISABLE_DEFAULT_PLUGINS - } else { - process.env.KILO_DISABLE_DEFAULT_PLUGINS = disableDefault - } - - Flag.KILO_EXPERIMENTAL_WORKSPACES = experimental -}) - describe("plugin.workspace", () => { it.live("plugin can install a workspace adapter", () => provideTmpdirInstance((dir) => diff --git a/packages/opencode/test/preload.ts b/packages/opencode/test/preload.ts index 71830cfab77..6d2601143b0 100644 --- a/packages/opencode/test/preload.ts +++ b/packages/opencode/test/preload.ts @@ -23,11 +23,7 @@ process.env["XDG_CONFIG_HOME"] = path.join(dir, "config") process.env["XDG_STATE_HOME"] = path.join(dir, "state") process.env["KILO_MODELS_PATH"] = path.join(import.meta.dir, "tool", "fixtures", "models-api.json") process.env["KILO_EXPERIMENTAL_EVENT_SYSTEM"] = "true" -// Tests assert exact skill counts from disk discovery; the built-in -// customize-opencode skill is opt-in for stable channels and on by default -// for unstable channels (including "local" where CI runs). Disable it here -// so disk-discovery tests aren't off-by-one. -process.env["KILO_EXPERIMENTAL_CUSTOMIZE_SKILL"] = "false" +process.env["KILO_EXPERIMENTAL_WORKSPACES"] = "true" // Set test home directory to isolate tests from user's actual home directory // This prevents tests from picking up real user configs/skills from ~/.claude/skills @@ -38,7 +34,6 @@ process.env["KILO_TEST_HOME"] = testHome // Set test managed config directory to isolate tests from system managed settings const testManagedConfigDir = path.join(dir, "managed") process.env["KILO_TEST_MANAGED_CONFIG_DIR"] = testManagedConfigDir -process.env["KILO_DISABLE_DEFAULT_PLUGINS"] = "true" // Write the cache version file to prevent global/index.ts from clearing the cache const cacheDir = path.join(dir, "cache", "kilo") diff --git a/packages/opencode/test/project/instance-bootstrap.test.ts b/packages/opencode/test/project/instance-bootstrap.test.ts index baad8df592b..4be2a76113d 100644 --- a/packages/opencode/test/project/instance-bootstrap.test.ts +++ b/packages/opencode/test/project/instance-bootstrap.test.ts @@ -1,11 +1,16 @@ -import { afterEach, expect, test } from "bun:test" +import { afterEach, expect } from "bun:test" import { existsSync } from "node:fs" import path from "node:path" import { pathToFileURL } from "node:url" +import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" +import { Effect, Layer } from "effect" import { bootstrap as cliBootstrap } from "../../src/cli/bootstrap" -import { WithInstance } from "../../src/project/with-instance" -import { InstanceRuntime } from "../../src/project/instance-runtime" -import { disposeAllInstances, tmpdir } from "../fixture/fixture" +import { InstanceLayer } from "../../src/project/instance-layer" +import { InstanceStore } from "../../src/project/instance-store" +import { disposeAllInstances, tmpdirScoped } from "../fixture/fixture" +import { testEffect } from "../lib/effect" + +const it = testEffect(Layer.mergeAll(InstanceLayer.layer, CrossSpawnSpawner.defaultLayer)) // InstanceBootstrap must run before any code touches the instance — // originally tracked by PRs #25389 and #25449, now a permanent @@ -13,66 +18,70 @@ import { disposeAllInstances, tmpdir } from "../fixture/fixture" // bodies deliberately avoid Plugin/config directly. The marker only // appears if InstanceBootstrap ran at the instance boundary. // -// The Hono variant of this check lived alongside these tests and is -// going away with the Hono backend. The boundaries below are backend- -// agnostic and stay. +// The boundaries below are transport-agnostic and stay. afterEach(async () => { await disposeAllInstances() }) -async function bootstrapFixture() { - return tmpdir({ - init: async (dir) => { - const marker = path.join(dir, "config-hook-fired") - const pluginFile = path.join(dir, "plugin.ts") - await Bun.write( - pluginFile, - [ - `const MARKER = ${JSON.stringify(marker)}`, - "export default async () => ({", - " config: async () => {", - ' await Bun.write(MARKER, "ran")', - " },", - "})", - "", - ].join("\n"), - ) - await Bun.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - plugin: [pathToFileURL(pluginFile).href], - }), - ) - return marker - }, - }) -} +const bootstrapFixture = Effect.gen(function* () { + const dir = yield* tmpdirScoped({ git: true }) + const marker = path.join(dir, "config-hook-fired") + const pluginFile = path.join(dir, "plugin.ts") + yield* Effect.promise(() => + Bun.write( + pluginFile, + [ + `const MARKER = ${JSON.stringify(marker)}`, + "export default async () => ({", + " config: async () => {", + ' await Bun.write(MARKER, "ran")', + " },", + "})", + "", + ].join("\n"), + ), + ) + yield* Effect.promise(() => + Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ + $schema: "https://opencode.ai/config.json", + plugin: [pathToFileURL(pluginFile).href], + }), + ), + ) + return { directory: dir, marker } +}) -test("WithInstance.provide runs InstanceBootstrap before fn", async () => { - await using tmp = await bootstrapFixture() +it.live("InstanceStore.provide runs InstanceBootstrap before effect", () => + Effect.gen(function* () { + const tmp = yield* bootstrapFixture + const store = yield* InstanceStore.Service - await WithInstance.provide({ - directory: tmp.path, - fn: async () => "ok", - }) + yield* store.provide({ directory: tmp.directory }, Effect.succeed("ok")) - expect(existsSync(tmp.extra)).toBe(true) -}) + expect(existsSync(tmp.marker)).toBe(true) + }), +) -test("CLI bootstrap runs InstanceBootstrap before callback", async () => { - await using tmp = await bootstrapFixture() +it.live("CLI bootstrap runs InstanceBootstrap before callback", () => + Effect.gen(function* () { + const tmp = yield* bootstrapFixture - await cliBootstrap(tmp.path, async () => "ok") + yield* Effect.promise(() => cliBootstrap(tmp.directory, async () => "ok")) - expect(existsSync(tmp.extra)).toBe(true) -}) + expect(existsSync(tmp.marker)).toBe(true) + }), +) -test("InstanceRuntime.reloadInstance runs InstanceBootstrap", async () => { - await using tmp = await bootstrapFixture() +it.live("InstanceStore.reload runs InstanceBootstrap", () => + Effect.gen(function* () { + const tmp = yield* bootstrapFixture + const store = yield* InstanceStore.Service - await InstanceRuntime.reloadInstance({ directory: tmp.path }) + yield* store.reload({ directory: tmp.directory }) - expect(existsSync(tmp.extra)).toBe(true) -}) + expect(existsSync(tmp.marker)).toBe(true) + }), +) diff --git a/packages/opencode/test/project/instance.test.ts b/packages/opencode/test/project/instance.test.ts index c119f5477e5..5d5337d584c 100644 --- a/packages/opencode/test/project/instance.test.ts +++ b/packages/opencode/test/project/instance.test.ts @@ -1,13 +1,12 @@ -import { afterEach, describe, expect } from "bun:test" +import { describe, expect } from "bun:test" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" -import { Effect, Fiber, Layer } from "effect" +import { Deferred, Effect, Fiber, Layer } from "effect" import { InstanceRef } from "../../src/effect/instance-ref" import { registerDisposer } from "../../src/effect/instance-registry" import { InstanceBootstrap } from "../../src/project/bootstrap-service" import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" import { InstanceStore } from "../../src/project/instance-store" -import { disposeAllInstances, tmpdirScoped } from "../fixture/fixture" +import { TestInstance, tmpdirScoped } from "../fixture/fixture" import { testEffect } from "../lib/effect" let bootstrapRun: Effect.Effect = Effect.void @@ -20,10 +19,22 @@ const it = testEffect( Layer.mergeAll(InstanceStore.defaultLayer, CrossSpawnSpawner.defaultLayer).pipe(Layer.provide(noopBootstrap)), ) -afterEach(async () => { - bootstrapRun = Effect.void - await disposeAllInstances() -}) +const setBootstrap = (run: Effect.Effect) => + Effect.acquireRelease( + Effect.sync(() => { + bootstrapRun = run + }), + () => + Effect.sync(() => { + bootstrapRun = Effect.void + }), + ) + +const registerDisposerScoped = (disposer: (directory: string) => Promise) => + Effect.acquireRelease( + Effect.sync(() => registerDisposer(disposer)), + (off) => Effect.sync(off), + ) describe("InstanceStore", () => { it.live("loads instance context without installing ALS for the caller", () => @@ -44,9 +55,11 @@ describe("InstanceStore", () => { const store = yield* InstanceStore.Service let initializedDirectory: string | undefined - bootstrapRun = Effect.gen(function* () { - initializedDirectory = (yield* InstanceRef)?.directory - }) + yield* setBootstrap( + Effect.gen(function* () { + initializedDirectory = (yield* InstanceRef)?.directory + }), + ) yield* store.load({ directory: dir }) expect(initializedDirectory).toBe(dir) @@ -60,9 +73,11 @@ describe("InstanceStore", () => { const store = yield* InstanceStore.Service let initialized = 0 - bootstrapRun = Effect.sync(() => { - initialized++ - }) + yield* setBootstrap( + Effect.sync(() => { + initialized++ + }), + ) const first = yield* store.load({ directory: dir }) const second = yield* store.load({ directory: dir }) @@ -75,26 +90,30 @@ describe("InstanceStore", () => { Effect.gen(function* () { const dir = yield* tmpdirScoped({ git: true }) const store = yield* InstanceStore.Service - const started = Promise.withResolvers() - const release = Promise.withResolvers() + const started = yield* Deferred.make() + const release = yield* Deferred.make() let initialized = 0 - bootstrapRun = Effect.promise(async () => { - initialized++ - started.resolve() - await release.promise - }) + yield* setBootstrap( + Effect.gen(function* () { + initialized++ + yield* Deferred.succeed(started, undefined) + yield* Deferred.await(release) + }), + ) const first = yield* store.load({ directory: dir }).pipe(Effect.forkScoped) - yield* Effect.promise(() => started.promise) + yield* Deferred.await(started) - bootstrapRun = Effect.sync(() => { - initialized++ - }) + yield* setBootstrap( + Effect.sync(() => { + initialized++ + }), + ) const second = yield* store.load({ directory: dir }).pipe(Effect.forkScoped) expect(initialized).toBe(1) - release.resolve() + yield* Deferred.succeed(release, undefined) const [firstCtx, secondCtx] = yield* Effect.all([Fiber.join(first), Fiber.join(second)]) expect(secondCtx).toBe(firstCtx) @@ -108,10 +127,12 @@ describe("InstanceStore", () => { const store = yield* InstanceStore.Service let attempts = 0 - bootstrapRun = Effect.sync(() => { - attempts++ - throw new Error("init failed") - }) + yield* setBootstrap( + Effect.sync(() => { + attempts++ + throw new Error("init failed") + }), + ) const failed = yield* store.load({ directory: dir }).pipe( Effect.as(false), Effect.catchCause(() => Effect.succeed(true)), @@ -119,9 +140,11 @@ describe("InstanceStore", () => { expect(failed).toBe(true) - bootstrapRun = Effect.sync(() => { - attempts++ - }) + yield* setBootstrap( + Effect.sync(() => { + attempts++ + }), + ) const ctx = yield* store.load({ directory: dir }) expect(ctx.directory).toBe(dir) @@ -147,24 +170,25 @@ describe("InstanceStore", () => { Effect.gen(function* () { const dir = yield* tmpdirScoped({ git: true }) const store = yield* InstanceStore.Service - const reloading = Promise.withResolvers() - const releaseReload = Promise.withResolvers() + const reloading = yield* Deferred.make() + const releaseReload = yield* Deferred.make() const disposed: Array = [] - const off = registerDisposer(async (directory) => { + yield* registerDisposerScoped(async (directory) => { disposed.push(directory) }) - yield* Effect.addFinalizer(() => Effect.sync(off)) const first = yield* store.load({ directory: dir }) - bootstrapRun = Effect.promise(async () => { - reloading.resolve() - await releaseReload.promise - }) + yield* setBootstrap( + Effect.gen(function* () { + yield* Deferred.succeed(reloading, undefined) + yield* Deferred.await(releaseReload) + }), + ) const reload = yield* store.reload({ directory: dir }).pipe(Effect.forkScoped) - yield* Effect.promise(() => reloading.promise) + yield* Deferred.await(reloading) const staleDispose = yield* store.dispose(first).pipe(Effect.forkScoped) - releaseReload.resolve() + yield* Deferred.succeed(releaseReload, undefined) const second = yield* Fiber.join(reload) yield* Fiber.join(staleDispose) @@ -178,23 +202,25 @@ describe("InstanceStore", () => { Effect.gen(function* () { const dir = yield* tmpdirScoped({ git: true }) const store = yield* InstanceStore.Service - const disposing = Promise.withResolvers() - const releaseDispose = Promise.withResolvers() + const disposing = yield* Deferred.make() + const releaseDispose = yield* Deferred.make<() => void>() const disposed: Array = [] - const off = registerDisposer(async (directory) => { + yield* registerDisposerScoped((directory) => { disposed.push(directory) - disposing.resolve() - await releaseDispose.promise + Deferred.doneUnsafe(disposing, Effect.void) + return new Promise((resolve) => { + Deferred.doneUnsafe(releaseDispose, Effect.succeed(resolve)) + }) }) - yield* Effect.addFinalizer(() => Effect.sync(off)) yield* store.load({ directory: dir }) const first = yield* store.disposeAll().pipe(Effect.forkScoped) - yield* Effect.promise(() => disposing.promise) + yield* Deferred.await(disposing) + const release = yield* Deferred.await(releaseDispose) const second = yield* store.disposeAll().pipe(Effect.forkScoped) expect(disposed).toEqual([dir]) - releaseDispose.resolve() + yield* Effect.sync(release) yield* Effect.all([Fiber.join(first), Fiber.join(second)]) expect(disposed).toEqual([dir]) }), @@ -206,10 +232,9 @@ describe("InstanceStore", () => { const dir2 = yield* tmpdirScoped({ git: true }) const store = yield* InstanceStore.Service const disposed: Array = [] - const off = registerDisposer(async (directory) => { + yield* registerDisposerScoped(async (directory) => { disposed.push(directory) }) - yield* Effect.addFinalizer(() => Effect.sync(off)) yield* store.load({ directory: dir1 }) yield* store.disposeAll() @@ -221,20 +246,20 @@ describe("InstanceStore", () => { }), ) - it.live("provides legacy Promise callers with instance ALS", () => - Effect.gen(function* () { - const dir = yield* tmpdirScoped({ git: true }) + it.instance( + "provides legacy Promise callers with instance ALS", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const ctx = yield* InstanceRef + if (!ctx) throw new Error("InstanceRef not provided") - const directory = yield* Effect.promise(() => - WithInstance.provide({ - directory: dir, - fn: () => Instance.directory, - }), - ) + const directory = yield* Effect.promise(() => Promise.resolve(Instance.restore(ctx, () => Instance.directory))) - expect(directory).toBe(dir) - expect(() => Instance.current).toThrow() - }), + expect(directory).toBe(test.directory) + expect(() => Instance.current).toThrow() + }), + { git: true }, ) // kilocode_change - InstanceStore.boot wraps bootstrap.run in the Instance ALS so diff --git a/packages/opencode/test/project/migrate-global.test.ts b/packages/opencode/test/project/migrate-global.test.ts index c476c108b45..6efd670c5c9 100644 --- a/packages/opencode/test/project/migrate-global.test.ts +++ b/packages/opencode/test/project/migrate-global.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, test } from "bun:test" +import { describe, expect } from "bun:test" import { Project } from "@/project/project" import { Database } from "@/storage/db" import { eq } from "drizzle-orm" @@ -8,19 +8,14 @@ import { ProjectID } from "../../src/project/schema" import { SessionID } from "../../src/session/schema" import * as Log from "@opencode-ai/core/util/log" import { $ } from "bun" -import { tmpdir } from "../fixture/fixture" -import { Effect } from "effect" +import { tmpdirScoped } from "../fixture/fixture" +import { Effect, Layer } from "effect" +import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" +import { testEffect } from "../lib/effect" -Log.init({ print: false }) +void Log.init({ print: false }) -function run(fn: (svc: Project.Interface) => Effect.Effect) { - return Effect.runPromise( - Effect.gen(function* () { - const svc = yield* Project.Service - return yield* fn(svc) - }).pipe(Effect.provide(Project.defaultLayer)), - ) -} +const it = testEffect(Layer.mergeAll(Project.defaultLayer, CrossSpawnSpawner.defaultLayer)) function legacySessionID() { // Global-session migration covers persisted IDs from before prefixed session IDs. @@ -63,91 +58,102 @@ function ensureGlobal() { } describe("migrateFromGlobal", () => { - test("migrates global sessions on first project creation", async () => { - // 1. Start with git init but no commits — creates "global" project row - await using tmp = await tmpdir() - await $`git init`.cwd(tmp.path).quiet() - await $`git config user.name "Test"`.cwd(tmp.path).quiet() - await $`git config user.email "test@opencode.test"`.cwd(tmp.path).quiet() - await $`git config commit.gpgsign false`.cwd(tmp.path).quiet() - const { project: pre } = await run((svc) => svc.fromDirectory(tmp.path)) - expect(pre.id).toBe(ProjectID.global) - - // 2. Seed a session under "global" with matching directory - const id = legacySessionID() - seed({ id, dir: tmp.path, project: ProjectID.global }) - - // 3. Make a commit so the project gets a real ID - await $`git commit --allow-empty -m "root"`.cwd(tmp.path).quiet() - - const { project: real } = await run((svc) => svc.fromDirectory(tmp.path)) - expect(real.id).not.toBe(ProjectID.global) - - // 4. The session should have been migrated to the real project ID - const row = Database.use((db) => db.select().from(SessionTable).where(eq(SessionTable.id, id)).get()) - expect(row).toBeDefined() - expect(row!.project_id).toBe(real.id) - }) - - test("migrates global sessions even when project row already exists", async () => { - // 1. Create a repo with a commit — real project ID created immediately - await using tmp = await tmpdir({ git: true }) - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) - expect(project.id).not.toBe(ProjectID.global) - - // 2. Ensure "global" project row exists (as it would from a prior no-git session) - ensureGlobal() - - // 3. Seed a session under "global" with matching directory. - // This simulates a session created before git init that wasn't - // present when the real project row was first created. - const id = legacySessionID() - seed({ id, dir: tmp.path, project: ProjectID.global }) - - // 4. Call fromDirectory again — project row already exists, - // so the current code skips migration entirely. This is the bug. - await run((svc) => svc.fromDirectory(tmp.path)) - - const row = Database.use((db) => db.select().from(SessionTable).where(eq(SessionTable.id, id)).get()) - expect(row).toBeDefined() - expect(row!.project_id).toBe(project.id) - }) - - test("does not claim sessions with empty directory", async () => { - await using tmp = await tmpdir({ git: true }) - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) - expect(project.id).not.toBe(ProjectID.global) - - ensureGlobal() - - // Legacy sessions may lack a directory value. - // Without a matching origin directory, they should remain global. - const id = legacySessionID() - seed({ id, dir: "", project: ProjectID.global }) - - await run((svc) => svc.fromDirectory(tmp.path)) - - const row = Database.use((db) => db.select().from(SessionTable).where(eq(SessionTable.id, id)).get()) - expect(row).toBeDefined() - expect(row!.project_id).toBe(ProjectID.global) - }) - - test("does not steal sessions from unrelated directories", async () => { - await using tmp = await tmpdir({ git: true }) - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) - expect(project.id).not.toBe(ProjectID.global) - - ensureGlobal() - - // Seed a session under "global" but for a DIFFERENT directory - const id = legacySessionID() - seed({ id, dir: "/some/other/dir", project: ProjectID.global }) - - await run((svc) => svc.fromDirectory(tmp.path)) - - const row = Database.use((db) => db.select().from(SessionTable).where(eq(SessionTable.id, id)).get()) - expect(row).toBeDefined() - // Should remain under "global" — not stolen - expect(row!.project_id).toBe(ProjectID.global) - }) + it.live("migrates global sessions on first project creation", () => + Effect.gen(function* () { + // 1. Start with git init but no commits — creates "global" project row + const tmp = yield* tmpdirScoped() + yield* Effect.promise(() => $`git init`.cwd(tmp).quiet()) + yield* Effect.promise(() => $`git config user.name "Test"`.cwd(tmp).quiet()) + yield* Effect.promise(() => $`git config user.email "test@opencode.test"`.cwd(tmp).quiet()) + yield* Effect.promise(() => $`git config commit.gpgsign false`.cwd(tmp).quiet()) + const projects = yield* Project.Service + const { project: pre } = yield* projects.fromDirectory(tmp) + expect(pre.id).toBe(ProjectID.global) + + // 2. Seed a session under "global" with matching directory + const id = legacySessionID() + yield* Effect.sync(() => seed({ id, dir: tmp, project: ProjectID.global })) + + // 3. Make a commit so the project gets a real ID + yield* Effect.promise(() => $`git commit --allow-empty -m "root"`.cwd(tmp).quiet()) + + const { project: real } = yield* projects.fromDirectory(tmp) + expect(real.id).not.toBe(ProjectID.global) + + // 4. The session should have been migrated to the real project ID + const row = Database.use((db) => db.select().from(SessionTable).where(eq(SessionTable.id, id)).get()) + expect(row).toBeDefined() + expect(row!.project_id).toBe(real.id) + }), + ) + + it.live("migrates global sessions even when project row already exists", () => + Effect.gen(function* () { + // 1. Create a repo with a commit — real project ID created immediately + const tmp = yield* tmpdirScoped({ git: true }) + const projects = yield* Project.Service + const { project } = yield* projects.fromDirectory(tmp) + expect(project.id).not.toBe(ProjectID.global) + + // 2. Ensure "global" project row exists (as it would from a prior no-git session) + yield* Effect.sync(() => ensureGlobal()) + + // 3. Seed a session under "global" with matching directory. + // This simulates a session created before git init that wasn't + // present when the real project row was first created. + const id = legacySessionID() + yield* Effect.sync(() => seed({ id, dir: tmp, project: ProjectID.global })) + + // 4. Call fromDirectory again — project row already exists, + // so the current code skips migration entirely. This is the bug. + yield* projects.fromDirectory(tmp) + + const row = Database.use((db) => db.select().from(SessionTable).where(eq(SessionTable.id, id)).get()) + expect(row).toBeDefined() + expect(row!.project_id).toBe(project.id) + }), + ) + + it.live("does not claim sessions with empty directory", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + const projects = yield* Project.Service + const { project } = yield* projects.fromDirectory(tmp) + expect(project.id).not.toBe(ProjectID.global) + + yield* Effect.sync(() => ensureGlobal()) + + // Legacy sessions may lack a directory value. + // Without a matching origin directory, they should remain global. + const id = legacySessionID() + yield* Effect.sync(() => seed({ id, dir: "", project: ProjectID.global })) + + yield* projects.fromDirectory(tmp) + + const row = Database.use((db) => db.select().from(SessionTable).where(eq(SessionTable.id, id)).get()) + expect(row).toBeDefined() + expect(row!.project_id).toBe(ProjectID.global) + }), + ) + + it.live("does not steal sessions from unrelated directories", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + const projects = yield* Project.Service + const { project } = yield* projects.fromDirectory(tmp) + expect(project.id).not.toBe(ProjectID.global) + + yield* Effect.sync(() => ensureGlobal()) + + // Seed a session under "global" but for a DIFFERENT directory + const id = legacySessionID() + yield* Effect.sync(() => seed({ id, dir: "/some/other/dir", project: ProjectID.global })) + + yield* projects.fromDirectory(tmp) + const row = Database.use((db) => db.select().from(SessionTable).where(eq(SessionTable.id, id)).get()) + expect(row).toBeDefined() + // Should remain under "global" — not stolen + expect(row!.project_id).toBe(ProjectID.global) + }), + ) }) diff --git a/packages/opencode/test/project/project.test.ts b/packages/opencode/test/project/project.test.ts index e6ad0b91f99..f4b0e22fcbe 100644 --- a/packages/opencode/test/project/project.test.ts +++ b/packages/opencode/test/project/project.test.ts @@ -4,26 +4,29 @@ import { Project } from "@/project/project" import * as Log from "@opencode-ai/core/util/log" import { $ } from "bun" import path from "path" -import { tmpdir } from "../fixture/fixture" +import { tmpdirScoped } from "../fixture/fixture" import { GlobalBus } from "../../src/bus/global" import { ProjectID } from "../../src/project/schema" -import { Effect, Layer, Stream } from "effect" +import { Cause, Effect, Exit, Layer, Stream } from "effect" import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process" import { NodePath } from "@effect/platform-node" import { AppFileSystem } from "@opencode-ai/core/filesystem" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" +import { testEffect } from "../lib/effect" +import { RuntimeFlags } from "@/effect/runtime-flags" void Log.init({ print: false }) const encoder = new TextEncoder() -function run(fn: (svc: Project.Interface) => Effect.Effect, layer = Project.defaultLayer) { - return Effect.runPromise( - Effect.gen(function* () { - const svc = yield* Project.Service - return yield* fn(svc) - }).pipe(Effect.provide(layer)), - ) +const layer = Layer.mergeAll(Project.defaultLayer, CrossSpawnSpawner.defaultLayer) +const it = testEffect(layer) + +function run(fn: (svc: Project.Interface) => Effect.Effect) { + return Effect.gen(function* () { + const svc = yield* Project.Service + return yield* fn(svc) + }) } /** @@ -67,403 +70,500 @@ function projectLayerWithFailure(failArg: string) { Layer.provide(Bus.defaultLayer), Layer.provide(AppFileSystem.defaultLayer), Layer.provide(NodePath.layer), + Layer.provide(RuntimeFlags.defaultLayer), ) } -describe("Project.fromDirectory", () => { - test("should handle git repository with no commits", async () => { - await using tmp = await tmpdir() - await $`git init`.cwd(tmp.path).quiet() +function projectLayerWithRuntimeFlags(flags: Parameters[0]) { + return Project.layer.pipe( + Layer.provide(Bus.defaultLayer), + Layer.provide(AppFileSystem.defaultLayer), + Layer.provide(NodePath.layer), + Layer.provide(RuntimeFlags.layer(flags)), + ) +} - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) +const failureIt = (failArg: string) => + testEffect(Layer.mergeAll(projectLayerWithFailure(failArg), CrossSpawnSpawner.defaultLayer)) - expect(project).toBeDefined() - expect(project.id).toBe(ProjectID.global) - expect(project.vcs).toBe("git") - expect(project.worktree).toBe(tmp.path) +const iconDiscoveryIt = testEffect( + Layer.provideMerge(projectLayerWithRuntimeFlags({ experimentalIconDiscovery: true }), CrossSpawnSpawner.defaultLayer), +) - const opencodeFile = path.join(tmp.path, ".git", "opencode") - expect(await Bun.file(opencodeFile).exists()).toBe(false) +function waitForProjectIcon(id: ProjectID, attempts = 50): Effect.Effect { + return Effect.gen(function* () { + const project = Project.get(id) + if (project?.icon?.url) return project + if (attempts <= 0) throw new Error(`Project icon was not discovered: ${id}`) + yield* Effect.sleep("10 millis") + return yield* waitForProjectIcon(id, attempts - 1) }) +} - test("should handle git repository with commits", async () => { - await using tmp = await tmpdir({ git: true }) +describe("Project.fromDirectory", () => { + it.live("should handle git repository with no commits", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped() + yield* Effect.promise(() => $`git init`.cwd(tmp).quiet()) - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) + const { project } = yield* run((svc) => svc.fromDirectory(tmp)) - expect(project).toBeDefined() - expect(project.id).not.toBe(ProjectID.global) - expect(project.vcs).toBe("git") - expect(project.worktree).toBe(tmp.path) + expect(project).toBeDefined() + expect(project.id).toBe(ProjectID.global) + expect(project.vcs).toBe("git") + expect(project.worktree).toBe(tmp) - const kiloFile = path.join(tmp.path, ".git", "kilo") - expect(await Bun.file(kiloFile).exists()).toBe(true) - }) + const kiloFile = path.join(tmp, ".git", "kilo") + expect(yield* Effect.promise(() => Bun.file(kiloFile).exists())).toBe(false) + }), + ) - test("returns global for non-git directory", async () => { - await using tmp = await tmpdir() - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) - expect(project.id).toBe(ProjectID.global) - }) + it.live("should handle git repository with commits", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) - test("derives stable project ID from root commit", async () => { - await using tmp = await tmpdir({ git: true }) - const { project: a } = await run((svc) => svc.fromDirectory(tmp.path)) - const { project: b } = await run((svc) => svc.fromDirectory(tmp.path)) - expect(b.id).toBe(a.id) - }) + const { project } = yield* run((svc) => svc.fromDirectory(tmp)) + + expect(project).toBeDefined() + expect(project.id).not.toBe(ProjectID.global) + expect(project.vcs).toBe("git") + expect(project.worktree).toBe(tmp) + + const kiloFile = path.join(tmp, ".git", "kilo") + expect(yield* Effect.promise(() => Bun.file(kiloFile).exists())).toBe(true) + }), + ) + + it.live("returns global for non-git directory", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped() + const { project } = yield* run((svc) => svc.fromDirectory(tmp)) + expect(project.id).toBe(ProjectID.global) + }), + ) + + it.live("derives stable project ID from root commit", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + const { project: a } = yield* run((svc) => svc.fromDirectory(tmp)) + const { project: b } = yield* run((svc) => svc.fromDirectory(tmp)) + expect(b.id).toBe(a.id) + }), + ) }) describe("Project.fromDirectory git failure paths", () => { - test("keeps vcs when rev-list exits non-zero (no commits)", async () => { - await using tmp = await tmpdir() - await $`git init`.cwd(tmp.path).quiet() - - // rev-list fails because HEAD doesn't exist yet — this is the natural scenario - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) - expect(project.vcs).toBe("git") - expect(project.id).toBe(ProjectID.global) - expect(project.worktree).toBe(tmp.path) - }) + it.live("keeps vcs when rev-list exits non-zero (no commits)", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped() + yield* Effect.promise(() => $`git init`.cwd(tmp).quiet()) + + // rev-list fails because HEAD doesn't exist yet: this is the natural scenario. + const { project } = yield* run((svc) => svc.fromDirectory(tmp)) + expect(project.vcs).toBe("git") + expect(project.id).toBe(ProjectID.global) + expect(project.worktree).toBe(tmp) + }), + ) - test("handles show-toplevel failure gracefully", async () => { - await using tmp = await tmpdir({ git: true }) - const layer = projectLayerWithFailure("--show-toplevel") + failureIt("--show-toplevel").live("handles show-toplevel failure gracefully", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) - const { project, sandbox } = await run((svc) => svc.fromDirectory(tmp.path), layer) - expect(project.worktree).toBe(tmp.path) - expect(sandbox).toBe(tmp.path) - }) + const { project, sandbox } = yield* run((svc) => svc.fromDirectory(tmp)) + expect(project.worktree).toBe(tmp) + expect(sandbox).toBe(tmp) + }), + ) - test("handles git-common-dir failure gracefully", async () => { - await using tmp = await tmpdir({ git: true }) - const layer = projectLayerWithFailure("--git-common-dir") + failureIt("--git-common-dir").live("handles git-common-dir failure gracefully", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) - const { project, sandbox } = await run((svc) => svc.fromDirectory(tmp.path), layer) - expect(project.worktree).toBe(tmp.path) - expect(sandbox).toBe(tmp.path) - }) + const { project, sandbox } = yield* run((svc) => svc.fromDirectory(tmp)) + expect(project.worktree).toBe(tmp) + expect(sandbox).toBe(tmp) + }), + ) }) describe("Project.fromDirectory with worktrees", () => { - test("should set worktree to root when called from root", async () => { - await using tmp = await tmpdir({ git: true }) - - const { project, sandbox } = await run((svc) => svc.fromDirectory(tmp.path)) + it.live("should set worktree to root when called from root", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) - expect(project.worktree).toBe(tmp.path) - expect(sandbox).toBe(tmp.path) - expect(project.sandboxes).not.toContain(tmp.path) - }) + const { project, sandbox } = yield* run((svc) => svc.fromDirectory(tmp)) - test("should set worktree to root when called from a worktree", async () => { - await using tmp = await tmpdir({ git: true }) + expect(project.worktree).toBe(tmp) + expect(sandbox).toBe(tmp) + expect(project.sandboxes).not.toContain(tmp) + }), + ) - const worktreePath = path.join(tmp.path, "..", path.basename(tmp.path) + "-worktree") - try { - await $`git worktree add ${worktreePath} -b test-branch-${Date.now()}`.cwd(tmp.path).quiet() + it.live("should set worktree to root when called from a worktree", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + + const worktreePath = path.join(tmp, "..", path.basename(tmp) + "-worktree") + yield* Effect.addFinalizer(() => + Effect.promise(() => + $`git worktree remove ${worktreePath}` + .cwd(tmp) + .quiet() + .catch(() => {}), + ), + ) + yield* Effect.promise(() => $`git worktree add ${worktreePath} -b test-branch-${Date.now()}`.cwd(tmp).quiet()) - const { project, sandbox } = await run((svc) => svc.fromDirectory(worktreePath)) + const { project, sandbox } = yield* run((svc) => svc.fromDirectory(worktreePath)) - expect(project.worktree).toBe(tmp.path) + expect(project.worktree).toBe(tmp) expect(sandbox).toBe(worktreePath) expect(project.sandboxes).toContain(worktreePath) - expect(project.sandboxes).not.toContain(tmp.path) - } finally { - await $`git worktree remove ${worktreePath}` - .cwd(tmp.path) - .quiet() - .catch(() => {}) - } - }) - - test("worktree should share project ID with main repo", async () => { - await using tmp = await tmpdir({ git: true }) - - const { project: main } = await run((svc) => svc.fromDirectory(tmp.path)) + expect(project.sandboxes).not.toContain(tmp) + }), + ) - const worktreePath = path.join(tmp.path, "..", path.basename(tmp.path) + "-wt-shared") - try { - await $`git worktree add ${worktreePath} -b shared-${Date.now()}`.cwd(tmp.path).quiet() + it.live("worktree should share project ID with main repo", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + + const { project: main } = yield* run((svc) => svc.fromDirectory(tmp)) + + const worktreePath = path.join(tmp, "..", path.basename(tmp) + "-wt-shared") + yield* Effect.addFinalizer(() => + Effect.promise(() => + $`git worktree remove ${worktreePath}` + .cwd(tmp) + .quiet() + .catch(() => {}), + ), + ) + yield* Effect.promise(() => $`git worktree add ${worktreePath} -b shared-${Date.now()}`.cwd(tmp).quiet()) - const { project: wt } = await run((svc) => svc.fromDirectory(worktreePath)) + const { project: wt } = yield* run((svc) => svc.fromDirectory(worktreePath)) expect(wt.id).toBe(main.id) // Cache should live in the common .git dir, not the worktree's .git file - const cache = path.join(tmp.path, ".git", "kilo") - const exists = await Bun.file(cache).exists() + const cache = path.join(tmp, ".git", "kilo") + const exists = yield* Effect.promise(() => Bun.file(cache).exists()) expect(exists).toBe(true) - } finally { - await $`git worktree remove ${worktreePath}` - .cwd(tmp.path) - .quiet() - .catch(() => {}) - } - }) + }), + ) - test("separate clones of the same repo should share project ID", async () => { - await using tmp = await tmpdir({ git: true }) + it.live("separate clones of the same repo should share project ID", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) - // Create a bare remote, push, then clone into a second directory - const bare = tmp.path + "-bare" - const clone = tmp.path + "-clone" - try { - await $`git clone --bare ${tmp.path} ${bare}`.quiet() - await $`git clone ${bare} ${clone}`.quiet() + // Create a bare remote, push, then clone into a second directory + const bare = tmp + "-bare" + const clone = tmp + "-clone" + yield* Effect.addFinalizer(() => + Effect.promise(() => $`rm -rf ${bare} ${clone}`.quiet().nothrow()).pipe(Effect.ignore), + ) + yield* Effect.promise(() => $`git clone --bare ${tmp} ${bare}`.quiet()) + yield* Effect.promise(() => $`git clone ${bare} ${clone}`.quiet()) - const { project: a } = await run((svc) => svc.fromDirectory(tmp.path)) - const { project: b } = await run((svc) => svc.fromDirectory(clone)) + const { project: a } = yield* run((svc) => svc.fromDirectory(tmp)) + const { project: b } = yield* run((svc) => svc.fromDirectory(clone)) expect(b.id).toBe(a.id) - } finally { - await $`rm -rf ${bare} ${clone}`.quiet().nothrow() - } - }) - - test("should accumulate multiple worktrees in sandboxes", async () => { - await using tmp = await tmpdir({ git: true }) + }), + ) - const worktree1 = path.join(tmp.path, "..", path.basename(tmp.path) + "-wt1") - const worktree2 = path.join(tmp.path, "..", path.basename(tmp.path) + "-wt2") - try { - await $`git worktree add ${worktree1} -b branch-${Date.now()}`.cwd(tmp.path).quiet() - await $`git worktree add ${worktree2} -b branch-${Date.now() + 1}`.cwd(tmp.path).quiet() + it.live("should accumulate multiple worktrees in sandboxes", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + + const worktree1 = path.join(tmp, "..", path.basename(tmp) + "-wt1") + const worktree2 = path.join(tmp, "..", path.basename(tmp) + "-wt2") + yield* Effect.addFinalizer(() => + Effect.gen(function* () { + yield* Effect.promise(() => + $`git worktree remove ${worktree1}` + .cwd(tmp) + .quiet() + .catch(() => {}), + ) + yield* Effect.promise(() => + $`git worktree remove ${worktree2}` + .cwd(tmp) + .quiet() + .catch(() => {}), + ) + }), + ) + yield* Effect.promise(() => $`git worktree add ${worktree1} -b branch-${Date.now()}`.cwd(tmp).quiet()) + yield* Effect.promise(() => $`git worktree add ${worktree2} -b branch-${Date.now() + 1}`.cwd(tmp).quiet()) - await run((svc) => svc.fromDirectory(worktree1)) - const { project } = await run((svc) => svc.fromDirectory(worktree2)) + yield* run((svc) => svc.fromDirectory(worktree1)) + const { project } = yield* run((svc) => svc.fromDirectory(worktree2)) - expect(project.worktree).toBe(tmp.path) + expect(project.worktree).toBe(tmp) expect(project.sandboxes).toContain(worktree1) expect(project.sandboxes).toContain(worktree2) - expect(project.sandboxes).not.toContain(tmp.path) - } finally { - await $`git worktree remove ${worktree1}` - .cwd(tmp.path) - .quiet() - .catch(() => {}) - await $`git worktree remove ${worktree2}` - .cwd(tmp.path) - .quiet() - .catch(() => {}) - } - }) + expect(project.sandboxes).not.toContain(tmp) + }), + ) }) describe("Project.discover", () => { - test("should discover favicon.png in root", async () => { - await using tmp = await tmpdir({ git: true }) - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) + iconDiscoveryIt.live("discovers favicon from fromDirectory when enabled", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + const pngData = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]) + yield* Effect.promise(() => Bun.write(path.join(tmp, "favicon.png"), pngData)) - const pngData = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]) - await Bun.write(path.join(tmp.path, "favicon.png"), pngData) + const { project } = yield* run((svc) => svc.fromDirectory(tmp)) + const updated = yield* waitForProjectIcon(project.id) - await run((svc) => svc.discover(project)) + expect(updated.icon?.url).toStartWith("data:") + expect(updated.icon?.url).toContain("base64") + }), + ) - const updated = Project.get(project.id) - expect(updated).toBeDefined() - expect(updated!.icon).toBeDefined() - expect(updated!.icon?.url).toStartWith("data:") - expect(updated!.icon?.url).toContain("base64") - expect(updated!.icon?.color).toBeUndefined() - }) + it.live("should discover favicon.png in root", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + const { project } = yield* run((svc) => svc.fromDirectory(tmp)) - test("should not discover non-image files", async () => { - await using tmp = await tmpdir({ git: true }) - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) + const pngData = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]) + yield* Effect.promise(() => Bun.write(path.join(tmp, "favicon.png"), pngData)) - await Bun.write(path.join(tmp.path, "favicon.txt"), "not an image") + yield* run((svc) => svc.discover(project)) - await run((svc) => svc.discover(project)) + const updated = Project.get(project.id) + expect(updated).toBeDefined() + expect(updated!.icon).toBeDefined() + expect(updated!.icon?.url).toStartWith("data:") + expect(updated!.icon?.url).toContain("base64") + expect(updated!.icon?.color).toBeUndefined() + }), + ) - const updated = Project.get(project.id) - expect(updated).toBeDefined() - expect(updated!.icon).toBeUndefined() - }) + it.live("should not discover non-image files", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + const { project } = yield* run((svc) => svc.fromDirectory(tmp)) - test("should not discover favicon when override is set", async () => { - await using tmp = await tmpdir({ git: true }) - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) + yield* Effect.promise(() => Bun.write(path.join(tmp, "favicon.txt"), "not an image")) - await run((svc) => - svc.update({ - projectID: project.id, - icon: { override: "data:image/png;base64,override" }, - }), - ) + yield* run((svc) => svc.discover(project)) - const updatedProject = await run((svc) => svc.get(project.id)) - if (!updatedProject) throw new Error("Project not found") + const updated = Project.get(project.id) + expect(updated).toBeDefined() + expect(updated!.icon).toBeUndefined() + }), + ) - const pngData = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]) - await Bun.write(path.join(tmp.path, "favicon.png"), pngData) + it.live("should not discover favicon when override is set", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + const { project } = yield* run((svc) => svc.fromDirectory(tmp)) - await run((svc) => svc.discover(updatedProject)) + yield* run((svc) => + svc.update({ + projectID: project.id, + icon: { override: "data:image/png;base64,override" }, + }), + ) - const updated = Project.get(project.id) - expect(updated).toBeDefined() - expect(updated!.icon?.override).toBe("data:image/png;base64,override") - expect(updated!.icon?.url).toBeUndefined() - }) + const updatedProject = yield* run((svc) => svc.get(project.id)) + if (!updatedProject) throw new Error("Project not found") + + const pngData = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]) + yield* Effect.promise(() => Bun.write(path.join(tmp, "favicon.png"), pngData)) + + yield* run((svc) => svc.discover(updatedProject)) + + const updated = Project.get(project.id) + expect(updated).toBeDefined() + expect(updated!.icon?.override).toBe("data:image/png;base64,override") + expect(updated!.icon?.url).toBeUndefined() + }), + ) }) describe("Project.update", () => { - test("should update name", async () => { - await using tmp = await tmpdir({ git: true }) - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) + it.live("should update name", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + const { project } = yield* run((svc) => svc.fromDirectory(tmp)) - const updated = await run((svc) => - svc.update({ - projectID: project.id, - name: "New Project Name", - }), - ) + const updated = yield* run((svc) => + svc.update({ + projectID: project.id, + name: "New Project Name", + }), + ) - expect(updated.name).toBe("New Project Name") + expect(updated.name).toBe("New Project Name") - const fromDb = Project.get(project.id) - expect(fromDb?.name).toBe("New Project Name") - }) + const fromDb = Project.get(project.id) + expect(fromDb?.name).toBe("New Project Name") + }), + ) - test("should update icon url", async () => { - await using tmp = await tmpdir({ git: true }) - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) + it.live("should update icon url", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + const { project } = yield* run((svc) => svc.fromDirectory(tmp)) - const updated = await run((svc) => - svc.update({ - projectID: project.id, - icon: { url: "https://example.com/icon.png" }, - }), - ) + const updated = yield* run((svc) => + svc.update({ + projectID: project.id, + icon: { url: "https://example.com/icon.png" }, + }), + ) - expect(updated.icon?.url).toBe("https://example.com/icon.png") + expect(updated.icon?.url).toBe("https://example.com/icon.png") - const fromDb = Project.get(project.id) - expect(fromDb?.icon?.url).toBe("https://example.com/icon.png") - }) + const fromDb = Project.get(project.id) + expect(fromDb?.icon?.url).toBe("https://example.com/icon.png") + }), + ) - test("should update icon color", async () => { - await using tmp = await tmpdir({ git: true }) - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) + it.live("should update icon color", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + const { project } = yield* run((svc) => svc.fromDirectory(tmp)) - const updated = await run((svc) => - svc.update({ - projectID: project.id, - icon: { color: "#ff0000" }, - }), - ) + const updated = yield* run((svc) => + svc.update({ + projectID: project.id, + icon: { color: "#ff0000" }, + }), + ) - expect(updated.icon?.color).toBe("#ff0000") + expect(updated.icon?.color).toBe("#ff0000") - const fromDb = Project.get(project.id) - expect(fromDb?.icon?.color).toBe("#ff0000") - }) + const fromDb = Project.get(project.id) + expect(fromDb?.icon?.color).toBe("#ff0000") + }), + ) - test("should update icon override", async () => { - await using tmp = await tmpdir({ git: true }) - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) + it.live("should update icon override", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + const { project } = yield* run((svc) => svc.fromDirectory(tmp)) - const updated = await run((svc) => - svc.update({ - projectID: project.id, - icon: { override: "data:image/png;base64,abc123" }, - }), - ) + const updated = yield* run((svc) => + svc.update({ + projectID: project.id, + icon: { override: "data:image/png;base64,abc123" }, + }), + ) - expect(updated.icon?.override).toBe("data:image/png;base64,abc123") + expect(updated.icon?.override).toBe("data:image/png;base64,abc123") - const fromDb = Project.get(project.id) - expect(fromDb?.icon?.override).toBe("data:image/png;base64,abc123") - }) + const fromDb = Project.get(project.id) + expect(fromDb?.icon?.override).toBe("data:image/png;base64,abc123") + }), + ) - test("should update commands", async () => { - await using tmp = await tmpdir({ git: true }) - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) + it.live("should update commands", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + const { project } = yield* run((svc) => svc.fromDirectory(tmp)) - const updated = await run((svc) => - svc.update({ - projectID: project.id, - commands: { start: "npm run dev" }, - }), - ) + const updated = yield* run((svc) => + svc.update({ + projectID: project.id, + commands: { start: "npm run dev" }, + }), + ) - expect(updated.commands?.start).toBe("npm run dev") + expect(updated.commands?.start).toBe("npm run dev") - const fromDb = Project.get(project.id) - expect(fromDb?.commands?.start).toBe("npm run dev") - }) + const fromDb = Project.get(project.id) + expect(fromDb?.commands?.start).toBe("npm run dev") + }), + ) - test("should throw error when project not found", async () => { - await expect( - run((svc) => + it.live("should throw error when project not found", () => + Effect.gen(function* () { + const exit = yield* run((svc) => svc.update({ projectID: ProjectID.make("nonexistent-project-id"), name: "Should Fail", }), - ), - ).rejects.toThrow("Project not found: nonexistent-project-id") - }) + ).pipe(Effect.exit) + expect(Exit.isFailure(exit)).toBe(true) + if (Exit.isFailure(exit)) { + const error = Cause.squash(exit.cause) + expect(error instanceof Error ? error.message : String(error)).toContain( + "Project not found: nonexistent-project-id", + ) + } + }), + ) - test("should emit GlobalBus event on update", async () => { - await using tmp = await tmpdir({ git: true }) - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) + it.live("should emit GlobalBus event on update", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + const { project } = yield* run((svc) => svc.fromDirectory(tmp)) - let eventPayload: any = null - const on = (data: any) => { - eventPayload = data - } - GlobalBus.on("event", on) + let eventPayload: any = null + const on = (data: any) => { + eventPayload = data + } + GlobalBus.on("event", on) + yield* Effect.addFinalizer(() => Effect.sync(() => GlobalBus.off("event", on))) - try { - await run((svc) => svc.update({ projectID: project.id, name: "Updated Name" })) + yield* run((svc) => svc.update({ projectID: project.id, name: "Updated Name" })) expect(eventPayload).not.toBeNull() expect(eventPayload.payload.type).toBe("project.updated") expect(eventPayload.payload.properties.name).toBe("Updated Name") - } finally { - GlobalBus.off("event", on) - } - }) + }), + ) - test("should update multiple fields at once", async () => { - await using tmp = await tmpdir({ git: true }) - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) - - const updated = await run((svc) => - svc.update({ - projectID: project.id, - name: "Multi Update", - icon: { url: "https://example.com/favicon.ico", override: "data:image/png;base64,abc123", color: "#00ff00" }, - commands: { start: "make start" }, - }), - ) - - expect(updated.name).toBe("Multi Update") - expect(updated.icon?.url).toBe("https://example.com/favicon.ico") - expect(updated.icon?.override).toBe("data:image/png;base64,abc123") - expect(updated.icon?.color).toBe("#00ff00") - expect(updated.commands?.start).toBe("make start") - }) + it.live("should update multiple fields at once", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + const { project } = yield* run((svc) => svc.fromDirectory(tmp)) + + const updated = yield* run((svc) => + svc.update({ + projectID: project.id, + name: "Multi Update", + icon: { url: "https://example.com/favicon.ico", override: "data:image/png;base64,abc123", color: "#00ff00" }, + commands: { start: "make start" }, + }), + ) + + expect(updated.name).toBe("Multi Update") + expect(updated.icon?.url).toBe("https://example.com/favicon.ico") + expect(updated.icon?.override).toBe("data:image/png;base64,abc123") + expect(updated.icon?.color).toBe("#00ff00") + expect(updated.commands?.start).toBe("make start") + }), + ) }) describe("Project.list and Project.get", () => { - test("list returns all projects", async () => { - await using tmp = await tmpdir({ git: true }) - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) + it.live("list returns all projects", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + const { project } = yield* run((svc) => svc.fromDirectory(tmp)) - const all = Project.list() - expect(all.length).toBeGreaterThan(0) - expect(all.find((p) => p.id === project.id)).toBeDefined() - }) + const all = Project.list() + expect(all.length).toBeGreaterThan(0) + expect(all.find((p) => p.id === project.id)).toBeDefined() + }), + ) - test("get returns project by id", async () => { - await using tmp = await tmpdir({ git: true }) - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) + it.live("get returns project by id", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + const { project } = yield* run((svc) => svc.fromDirectory(tmp)) - const found = Project.get(project.id) - expect(found).toBeDefined() - expect(found!.id).toBe(project.id) - }) + const found = Project.get(project.id) + expect(found).toBeDefined() + expect(found!.id).toBe(project.id) + }), + ) test("get returns undefined for unknown id", () => { const found = Project.get(ProjectID.make("nonexistent")) @@ -472,134 +572,143 @@ describe("Project.list and Project.get", () => { }) describe("Project.setInitialized", () => { - test("sets time_initialized on project", async () => { - await using tmp = await tmpdir({ git: true }) - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) + it.live("sets time_initialized on project", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + const { project } = yield* run((svc) => svc.fromDirectory(tmp)) - expect(project.time.initialized).toBeUndefined() + expect(project.time.initialized).toBeUndefined() - Project.setInitialized(project.id) + Project.setInitialized(project.id) - const updated = Project.get(project.id) - expect(updated?.time.initialized).toBeDefined() - }) + const updated = Project.get(project.id) + expect(updated?.time.initialized).toBeDefined() + }), + ) }) describe("Project.addSandbox and Project.removeSandbox", () => { - test("addSandbox adds directory and removeSandbox removes it", async () => { - await using tmp = await tmpdir({ git: true }) - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) - const sandboxDir = path.join(tmp.path, "sandbox-test") + it.live("addSandbox adds directory and removeSandbox removes it", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + const { project } = yield* run((svc) => svc.fromDirectory(tmp)) + const sandboxDir = path.join(tmp, "sandbox-test") - await run((svc) => svc.addSandbox(project.id, sandboxDir)) + yield* run((svc) => svc.addSandbox(project.id, sandboxDir)) - let found = Project.get(project.id) - expect(found?.sandboxes).toContain(sandboxDir) + let found = Project.get(project.id) + expect(found?.sandboxes).toContain(sandboxDir) - await run((svc) => svc.removeSandbox(project.id, sandboxDir)) + yield* run((svc) => svc.removeSandbox(project.id, sandboxDir)) - found = Project.get(project.id) - expect(found?.sandboxes).not.toContain(sandboxDir) - }) + found = Project.get(project.id) + expect(found?.sandboxes).not.toContain(sandboxDir) + }), + ) - test("addSandbox emits GlobalBus event", async () => { - await using tmp = await tmpdir({ git: true }) - const { project } = await run((svc) => svc.fromDirectory(tmp.path)) - const sandboxDir = path.join(tmp.path, "sandbox-event") + it.live("addSandbox emits GlobalBus event", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + const { project } = yield* run((svc) => svc.fromDirectory(tmp)) + const sandboxDir = path.join(tmp, "sandbox-event") - const events: any[] = [] - const on = (evt: any) => events.push(evt) - GlobalBus.on("event", on) + const events: any[] = [] + const on = (evt: any) => events.push(evt) + GlobalBus.on("event", on) + yield* Effect.addFinalizer(() => Effect.sync(() => GlobalBus.off("event", on))) - await run((svc) => svc.addSandbox(project.id, sandboxDir)) + yield* run((svc) => svc.addSandbox(project.id, sandboxDir)) - GlobalBus.off("event", on) - expect(events.some((e) => e.payload.type === Project.Event.Updated.type)).toBe(true) - }) + expect(events.some((e) => e.payload.type === Project.Event.Updated.type)).toBe(true) + }), + ) }) describe("Project.fromDirectory with bare repos", () => { - test("worktree from bare repo should cache in bare repo, not parent", async () => { - await using tmp = await tmpdir({ git: true }) + it.live("worktree from bare repo should cache in bare repo, not parent", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) - const parentDir = path.dirname(tmp.path) - const barePath = path.join(parentDir, `bare-${Date.now()}.git`) - const worktreePath = path.join(parentDir, `worktree-${Date.now()}`) + const parentDir = path.dirname(tmp) + const barePath = path.join(parentDir, `bare-${Date.now()}.git`) + const worktreePath = path.join(parentDir, `worktree-${Date.now()}`) + yield* Effect.addFinalizer(() => + Effect.promise(() => $`rm -rf ${barePath} ${worktreePath}`.quiet().nothrow()).pipe(Effect.ignore), + ) - try { - await $`git clone --bare ${tmp.path} ${barePath}`.quiet() - await $`git worktree add ${worktreePath} HEAD`.cwd(barePath).quiet() + yield* Effect.promise(() => $`git clone --bare ${tmp} ${barePath}`.quiet()) + yield* Effect.promise(() => $`git worktree add ${worktreePath} HEAD`.cwd(barePath).quiet()) - const { project } = await run((svc) => svc.fromDirectory(worktreePath)) + const { project } = yield* run((svc) => svc.fromDirectory(worktreePath)) expect(project.id).not.toBe(ProjectID.global) expect(project.worktree).toBe(barePath) - const correctCache = path.join(barePath, "kilo") // kilocode_change - const wrongCache = path.join(parentDir, ".git", "kilo") // kilocode_change - - expect(await Bun.file(correctCache).exists()).toBe(true) - expect(await Bun.file(wrongCache).exists()).toBe(false) - } finally { - await $`rm -rf ${barePath} ${worktreePath}`.quiet().nothrow() - } - }) + const correctCache = path.join(barePath, "kilo") + const wrongCache = path.join(parentDir, ".git", "kilo") - test("different bare repos under same parent should not share project ID", async () => { - await using tmp1 = await tmpdir({ git: true }) - await using tmp2 = await tmpdir({ git: true }) + expect(yield* Effect.promise(() => Bun.file(correctCache).exists())).toBe(true) + expect(yield* Effect.promise(() => Bun.file(wrongCache).exists())).toBe(false) + }), + ) - const parentDir = path.dirname(tmp1.path) - const bareA = path.join(parentDir, `bare-a-${Date.now()}.git`) - const bareB = path.join(parentDir, `bare-b-${Date.now()}.git`) - const worktreeA = path.join(parentDir, `wt-a-${Date.now()}`) - const worktreeB = path.join(parentDir, `wt-b-${Date.now()}`) + it.live("different bare repos under same parent should not share project ID", () => + Effect.gen(function* () { + const tmp1 = yield* tmpdirScoped({ git: true }) + const tmp2 = yield* tmpdirScoped({ git: true }) + + const parentDir = path.dirname(tmp1) + const bareA = path.join(parentDir, `bare-a-${Date.now()}.git`) + const bareB = path.join(parentDir, `bare-b-${Date.now()}.git`) + const worktreeA = path.join(parentDir, `wt-a-${Date.now()}`) + const worktreeB = path.join(parentDir, `wt-b-${Date.now()}`) + yield* Effect.addFinalizer(() => + Effect.promise(() => $`rm -rf ${bareA} ${bareB} ${worktreeA} ${worktreeB}`.quiet().nothrow()).pipe( + Effect.ignore, + ), + ) - try { - await $`git clone --bare ${tmp1.path} ${bareA}`.quiet() - await $`git clone --bare ${tmp2.path} ${bareB}`.quiet() - await $`git worktree add ${worktreeA} HEAD`.cwd(bareA).quiet() - await $`git worktree add ${worktreeB} HEAD`.cwd(bareB).quiet() + yield* Effect.promise(() => $`git clone --bare ${tmp1} ${bareA}`.quiet()) + yield* Effect.promise(() => $`git clone --bare ${tmp2} ${bareB}`.quiet()) + yield* Effect.promise(() => $`git worktree add ${worktreeA} HEAD`.cwd(bareA).quiet()) + yield* Effect.promise(() => $`git worktree add ${worktreeB} HEAD`.cwd(bareB).quiet()) - const { project: projA } = await run((svc) => svc.fromDirectory(worktreeA)) - const { project: projB } = await run((svc) => svc.fromDirectory(worktreeB)) + const { project: projA } = yield* run((svc) => svc.fromDirectory(worktreeA)) + const { project: projB } = yield* run((svc) => svc.fromDirectory(worktreeB)) expect(projA.id).not.toBe(projB.id) - // kilocode_change start const cacheA = path.join(bareA, "kilo") const cacheB = path.join(bareB, "kilo") const wrongCache = path.join(parentDir, ".git", "kilo") - // kilocode_change end - - expect(await Bun.file(cacheA).exists()).toBe(true) - expect(await Bun.file(cacheB).exists()).toBe(true) - expect(await Bun.file(wrongCache).exists()).toBe(false) - } finally { - await $`rm -rf ${bareA} ${bareB} ${worktreeA} ${worktreeB}`.quiet().nothrow() - } - }) - test("bare repo without .git suffix is still detected via core.bare", async () => { - await using tmp = await tmpdir({ git: true }) + expect(yield* Effect.promise(() => Bun.file(cacheA).exists())).toBe(true) + expect(yield* Effect.promise(() => Bun.file(cacheB).exists())).toBe(true) + expect(yield* Effect.promise(() => Bun.file(wrongCache).exists())).toBe(false) + }), + ) - const parentDir = path.dirname(tmp.path) - const barePath = path.join(parentDir, `bare-no-suffix-${Date.now()}`) - const worktreePath = path.join(parentDir, `worktree-${Date.now()}`) + it.live("bare repo without .git suffix is still detected via core.bare", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) - try { - await $`git clone --bare ${tmp.path} ${barePath}`.quiet() - await $`git worktree add ${worktreePath} HEAD`.cwd(barePath).quiet() + const parentDir = path.dirname(tmp) + const barePath = path.join(parentDir, `bare-no-suffix-${Date.now()}`) + const worktreePath = path.join(parentDir, `worktree-${Date.now()}`) + yield* Effect.addFinalizer(() => + Effect.promise(() => $`rm -rf ${barePath} ${worktreePath}`.quiet().nothrow()).pipe(Effect.ignore), + ) + + yield* Effect.promise(() => $`git clone --bare ${tmp} ${barePath}`.quiet()) + yield* Effect.promise(() => $`git worktree add ${worktreePath} HEAD`.cwd(barePath).quiet()) - const { project } = await run((svc) => svc.fromDirectory(worktreePath)) + const { project } = yield* run((svc) => svc.fromDirectory(worktreePath)) expect(project.id).not.toBe(ProjectID.global) expect(project.worktree).toBe(barePath) - const correctCache = path.join(barePath, "kilo") // kilocode_change - expect(await Bun.file(correctCache).exists()).toBe(true) - } finally { - await $`rm -rf ${barePath} ${worktreePath}`.quiet().nothrow() - } - }) + const correctCache = path.join(barePath, "kilo") + expect(yield* Effect.promise(() => Bun.file(correctCache).exists())).toBe(true) + }), + ) }) diff --git a/packages/opencode/test/project/vcs.test.ts b/packages/opencode/test/project/vcs.test.ts index 82eacfb6df8..b1d637302df 100644 --- a/packages/opencode/test/project/vcs.test.ts +++ b/packages/opencode/test/project/vcs.test.ts @@ -1,161 +1,150 @@ -import { $ } from "bun" -import { afterEach, describe, expect, test } from "bun:test" +import { afterEach, describe, expect } from "bun:test" +import { AppFileSystem } from "@opencode-ai/core/filesystem" import { parsePatch } from "diff" -import { Effect } from "effect" +import { Deferred, Effect, Layer } from "effect" +import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import fs from "fs/promises" import path from "path" -import { disposeAllInstances, tmpdir } from "../fixture/fixture" -import { AppRuntime } from "../../src/effect/app-runtime" +import { disposeAllInstances, provideInstance, TestInstance, tmpdirScoped } from "../fixture/fixture" +import { Bus } from "../../src/bus" import { FileWatcher } from "../../src/file/watcher" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" -import { GlobalBus } from "../../src/bus/global" +import { Git } from "../../src/git" import { Vcs } from "@/project/vcs" - -// Skip in CI — native @parcel/watcher binding needed -const describeVcs = FileWatcher.hasNativeBinding() && !process.env.CI ? describe : describe.skip +import { testEffect } from "../lib/effect" // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- -async function withVcs(directory: string, body: () => Promise) { - return WithInstance.provide({ - directory, - fn: async () => { - await AppRuntime.runPromise( - Effect.gen(function* () { - const watcher = yield* FileWatcher.Service - const vcs = yield* Vcs.Service - yield* watcher.init() - yield* vcs.init() - }), - ) - await Bun.sleep(500) - await body() - }, - }) -} - -function withVcsOnly(directory: string, body: () => Promise) { - return WithInstance.provide({ - directory, - fn: async () => { - await AppRuntime.runPromise( - Effect.gen(function* () { - const vcs = yield* Vcs.Service - yield* vcs.init() - }), - ) - await body() - }, - }) -} - -type BranchEvent = { directory?: string; payload: { type: string; properties: { branch?: string } } } const weird = process.platform === "win32" ? "space file.txt" : "tab\tfile.txt" -/** Wait for a Vcs.Event.BranchUpdated event on GlobalBus, with retry polling as fallback */ -function nextBranchUpdate(directory: string, timeout = 10_000) { - return new Promise((resolve, reject) => { - let settled = false - - const timer = setTimeout(() => { - if (settled) return - settled = true - GlobalBus.off("event", on) - reject(new Error("timed out waiting for BranchUpdated event")) - }, timeout) - - function on(evt: BranchEvent) { - if (evt.directory !== directory) return - if (evt.payload.type !== Vcs.Event.BranchUpdated.type) return - if (settled) return - settled = true - clearTimeout(timer) - GlobalBus.off("event", on) - resolve(evt.payload.properties.branch) - } - - GlobalBus.on("event", on) +const layer = Layer.mergeAll( + Vcs.layer.pipe(Layer.provideMerge(Git.defaultLayer), Layer.provideMerge(Bus.layer)), + CrossSpawnSpawner.defaultLayer, + AppFileSystem.defaultLayer, +) +const it = testEffect(layer) + +const git = Effect.fn("VcsTest.git")(function* (cwd: string, args: string[]) { + const result = yield* Git.Service.use((git) => git.run(args, { cwd })) + if (result.exitCode !== 0) throw new Error(`git ${args.join(" ")} failed: ${result.stderr.toString("utf8")}`) +}) + +const write = Effect.fn("VcsTest.write")(function* (file: string, content: string) { + yield* AppFileSystem.Service.use((fs) => fs.writeWithDirs(file, content)) +}) + +const remove = Effect.fn("VcsTest.remove")(function* (file: string) { + yield* AppFileSystem.Service.use((fs) => fs.remove(file)) +}) + +const symlink = (target: string, file: string) => Effect.promise(() => fs.symlink(target, file)) + +const init = Effect.fn("VcsTest.init")(function* () { + const vcs = yield* Vcs.Service + yield* vcs.init() + return vcs +}) + +const nextBranchUpdate = Effect.fn("VcsTest.nextBranchUpdate")(function* () { + const bus = yield* Bus.Service + const updated = yield* Deferred.make() + + const off = yield* bus.subscribeCallback(Vcs.Event.BranchUpdated, (evt) => { + Effect.runSync(Deferred.succeed(updated, evt.properties.branch)) }) -} + yield* Effect.addFinalizer(() => Effect.sync(off)) + + return updated +}) + +const publishHeadChangeUntil = Effect.fn("VcsTest.publishHeadChangeUntil")(function* ( + pending: Deferred.Deferred, + head: string, +) { + const bus = yield* Bus.Service + for (let i = 0; i < 50; i++) { + yield* bus.publish(FileWatcher.Event.Updated, { file: head, event: "change" }) + if (yield* Deferred.isDone(pending)) return + yield* Effect.sleep("10 millis") + } +}) // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- -describeVcs("Vcs", () => { +describe("Vcs", () => { afterEach(async () => { await disposeAllInstances() }) - test("branch() returns current branch name", async () => { - await using tmp = await tmpdir({ git: true }) + it.instance( + "branch() returns current branch name", + () => + Effect.gen(function* () { + const vcs = yield* init() + const branch = yield* vcs.branch() - await withVcs(tmp.path, async () => { - const branch = await AppRuntime.runPromise( - Effect.gen(function* () { - const vcs = yield* Vcs.Service - return yield* vcs.branch() - }), - ) - expect(branch).toBeDefined() - expect(typeof branch).toBe("string") - }) - }) + expect(branch).toBeDefined() + expect(typeof branch).toBe("string") + }), + { git: true }, + ) - test("branch() returns undefined for non-git directories", async () => { - await using tmp = await tmpdir() + it.instance("branch() returns undefined for non-git directories", () => + Effect.gen(function* () { + const vcs = yield* init() + const branch = yield* vcs.branch() - await withVcs(tmp.path, async () => { - const branch = await AppRuntime.runPromise( - Effect.gen(function* () { - const vcs = yield* Vcs.Service - return yield* vcs.branch() - }), - ) expect(branch).toBeUndefined() - }) - }) - - test("publishes BranchUpdated when .git/HEAD changes", async () => { - await using tmp = await tmpdir({ git: true }) - const branch = `test-${Math.random().toString(36).slice(2)}` - await $`git branch ${branch}`.cwd(tmp.path).quiet() - - await withVcs(tmp.path, async () => { - const pending = nextBranchUpdate(tmp.path) - - const head = path.join(tmp.path, ".git", "HEAD") - await fs.writeFile(head, `ref: refs/heads/${branch}\n`) - - const updated = await pending - expect(updated).toBe(branch) - }) - }) - - test("branch() reflects the new branch after HEAD change", async () => { - await using tmp = await tmpdir({ git: true }) - const branch = `test-${Math.random().toString(36).slice(2)}` - await $`git branch ${branch}`.cwd(tmp.path).quiet() - - await withVcs(tmp.path, async () => { - const pending = nextBranchUpdate(tmp.path) - - const head = path.join(tmp.path, ".git", "HEAD") - await fs.writeFile(head, `ref: refs/heads/${branch}\n`) - - await pending - const current = await AppRuntime.runPromise( - Effect.gen(function* () { - const vcs = yield* Vcs.Service - return yield* vcs.branch() - }), - ) - expect(current).toBe(branch) - }) - }) + }), + ) + + it.instance( + "publishes BranchUpdated when .git/HEAD changes", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const branch = `test-${Math.random().toString(36).slice(2)}` + yield* git(test.directory, ["branch", branch]) + + const vcs = yield* init() + yield* vcs.branch() + const pending = yield* nextBranchUpdate() + + const head = path.join(test.directory, ".git", "HEAD") + yield* write(head, `ref: refs/heads/${branch}\n`) + yield* publishHeadChangeUntil(pending, head) + + const updated = yield* Deferred.await(pending).pipe(Effect.timeout("2 seconds")) + expect(updated).toBe(branch) + }), + { git: true }, + ) + + it.instance( + "branch() reflects the new branch after HEAD change", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const branch = `test-${Math.random().toString(36).slice(2)}` + yield* git(test.directory, ["branch", branch]) + + const vcs = yield* init() + yield* vcs.branch() + const pending = yield* nextBranchUpdate() + + const head = path.join(test.directory, ".git", "HEAD") + yield* write(head, `ref: refs/heads/${branch}\n`) + yield* publishHeadChangeUntil(pending, head) + yield* Deferred.await(pending).pipe(Effect.timeout("2 seconds")) + + const current = yield* vcs.branch() + expect(current).toBe(branch) + }), + { git: true }, + ) }) describe("Vcs diff", () => { @@ -163,177 +152,176 @@ describe("Vcs diff", () => { await disposeAllInstances() }) - test("defaultBranch() falls back to main", async () => { - await using tmp = await tmpdir({ git: true }) - await $`git branch -M main`.cwd(tmp.path).quiet() - - await withVcsOnly(tmp.path, async () => { - const branch = await AppRuntime.runPromise( - Effect.gen(function* () { - const vcs = yield* Vcs.Service - return yield* vcs.defaultBranch() - }), - ) - expect(branch).toBe("main") - }) - }) - - test("defaultBranch() uses init.defaultBranch when available", async () => { - await using tmp = await tmpdir({ git: true }) - await $`git branch -M trunk`.cwd(tmp.path).quiet() - await $`git config init.defaultBranch trunk`.cwd(tmp.path).quiet() - - await withVcsOnly(tmp.path, async () => { - const branch = await AppRuntime.runPromise( - Effect.gen(function* () { - const vcs = yield* Vcs.Service - return yield* vcs.defaultBranch() - }), - ) - expect(branch).toBe("trunk") - }) - }) + it.instance( + "defaultBranch() falls back to main", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* git(test.directory, ["branch", "-M", "main"]) + + const vcs = yield* init() + const branch = yield* vcs.defaultBranch() + + expect(branch).toBe("main") + }), + { git: true }, + ) + + it.instance( + "defaultBranch() uses init.defaultBranch when available", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* git(test.directory, ["branch", "-M", "trunk"]) + yield* git(test.directory, ["config", "init.defaultBranch", "trunk"]) + + const vcs = yield* init() + const branch = yield* vcs.defaultBranch() + + expect(branch).toBe("trunk") + }), + { git: true }, + ) + + it.live("detects current branch from the active worktree", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ git: true }) + const wt = yield* tmpdirScoped() + yield* git(tmp, ["branch", "-M", "main"]) + const dir = path.join(wt, "feature") + yield* git(tmp, ["worktree", "add", "-b", "feature/test", dir, "HEAD"]) + + const [branch, base] = yield* Effect.gen(function* () { + const vcs = yield* init() + return yield* Effect.all([vcs.branch(), vcs.defaultBranch()], { concurrency: 2 }) + }).pipe(provideInstance(dir)) - test("detects current branch from the active worktree", async () => { - await using tmp = await tmpdir({ git: true }) - await using wt = await tmpdir() - await $`git branch -M main`.cwd(tmp.path).quiet() - const dir = path.join(wt.path, "feature") - await $`git worktree add -b feature/test ${dir} HEAD`.cwd(tmp.path).quiet() - - await withVcsOnly(dir, async () => { - const [branch, base] = await AppRuntime.runPromise( - Effect.gen(function* () { - const vcs = yield* Vcs.Service - return yield* Effect.all([vcs.branch(), vcs.defaultBranch()], { concurrency: 2 }) - }), - ) + expect(branch).toBeDefined() expect(branch).toBe("feature/test") expect(base).toBe("main") - }) - }) - - test("diff('git') returns uncommitted changes", async () => { - await using tmp = await tmpdir({ git: true }) - await fs.writeFile(path.join(tmp.path, "file.txt"), "original\n", "utf-8") - await $`git add .`.cwd(tmp.path).quiet() - await $`git commit --no-gpg-sign -m "add file"`.cwd(tmp.path).quiet() - await fs.writeFile(path.join(tmp.path, "file.txt"), "changed\n", "utf-8") - - await withVcsOnly(tmp.path, async () => { - const diff = await AppRuntime.runPromise( - Effect.gen(function* () { - const vcs = yield* Vcs.Service - return yield* vcs.diff("git") - }), - ) - expect(diff).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - file: "file.txt", - status: "modified", - }), - ]), - ) - expect(diff.find((item) => item.file === "file.txt")?.patch).toContain("diff --git") - }) - }) - - test("diff('git') handles special filenames", async () => { - await using tmp = await tmpdir({ git: true }) - await fs.writeFile(path.join(tmp.path, weird), "hello\n", "utf-8") - - await withVcsOnly(tmp.path, async () => { - const diff = await AppRuntime.runPromise( - Effect.gen(function* () { - const vcs = yield* Vcs.Service - return yield* vcs.diff("git") - }), - ) - expect(diff).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - file: weird, - status: "added", - }), - ]), - ) - }) - }) - - test("diff('git') keeps batched patches aligned for type changes", async () => { - if (process.platform === "win32") return - - await using tmp = await tmpdir({ git: true }) - await fs.writeFile(path.join(tmp.path, "a.txt"), "old\n", "utf-8") - await fs.writeFile(path.join(tmp.path, "b.txt"), "old\n", "utf-8") - await $`git add .`.cwd(tmp.path).quiet() - await $`git commit --no-gpg-sign -m "add files"`.cwd(tmp.path).quiet() - await fs.unlink(path.join(tmp.path, "a.txt")) - await fs.symlink("target", path.join(tmp.path, "a.txt")) - await fs.writeFile(path.join(tmp.path, "b.txt"), "new\n", "utf-8") - - await withVcsOnly(tmp.path, async () => { - const diff = await AppRuntime.runPromise( - Effect.gen(function* () { - const vcs = yield* Vcs.Service - return yield* vcs.diff("git") - }), - ) - const a = diff.find((item) => item.file === "a.txt") - const b = diff.find((item) => item.file === "b.txt") - - expect(a?.patch).toContain("deleted file mode") - expect(a?.patch).toContain("new file mode") - expect(b?.patch).toContain("+new") - }) - }) - - test("diff('git') keeps carriage returns inside patch hunks", async () => { - await using tmp = await tmpdir({ git: true }) - await fs.writeFile(path.join(tmp.path, "file.txt"), "keep\nsame\rdiff --git inside\ndelete\n", "utf-8") - await $`git add .`.cwd(tmp.path).quiet() - await $`git commit --no-gpg-sign -m "add file"`.cwd(tmp.path).quiet() - await fs.writeFile(path.join(tmp.path, "file.txt"), "keep\nadd\nsame\rdiff --git inside\n", "utf-8") - - await withVcsOnly(tmp.path, async () => { - const diff = await AppRuntime.runPromise( - Effect.gen(function* () { - const vcs = yield* Vcs.Service - return yield* vcs.diff("git") - }), - ) - const file = diff.find((item) => item.file === "file.txt") - - expect(file?.patch).toContain(" same\rdiff --git inside") - expect(file?.patch).toContain("-delete") - expect(() => parsePatch(file?.patch ?? "")).not.toThrow() - }) - }, 20_000) - - test("diff('branch') returns changes against default branch", async () => { - await using tmp = await tmpdir({ git: true }) - await $`git branch -M main`.cwd(tmp.path).quiet() - await $`git checkout -b feature/test`.cwd(tmp.path).quiet() - await fs.writeFile(path.join(tmp.path, "branch.txt"), "hello\n", "utf-8") - await $`git add .`.cwd(tmp.path).quiet() - await $`git commit --no-gpg-sign -m "branch file"`.cwd(tmp.path).quiet() - - await withVcsOnly(tmp.path, async () => { - const diff = await AppRuntime.runPromise( - Effect.gen(function* () { - const vcs = yield* Vcs.Service - return yield* vcs.diff("branch") - }), - ) - expect(diff).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - file: "branch.txt", - status: "added", - }), - ]), - ) - }) - }) + }), + ) + + it.instance( + "diff('git') returns uncommitted changes", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* write(path.join(test.directory, "file.txt"), "original\n") + yield* git(test.directory, ["add", "."]) + yield* git(test.directory, ["commit", "--no-gpg-sign", "-m", "add file"]) + yield* write(path.join(test.directory, "file.txt"), "changed\n") + + const vcs = yield* init() + const diff = yield* vcs.diff("git") + + expect(diff).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + file: "file.txt", + status: "modified", + }), + ]), + ) + expect(diff.find((item) => item.file === "file.txt")?.patch).toContain("diff --git") + }), + { git: true }, + ) + + it.instance( + "diff('git') handles special filenames", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* write(path.join(test.directory, weird), "hello\n") + + const vcs = yield* init() + const diff = yield* vcs.diff("git") + + expect(diff).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + file: weird, + status: "added", + }), + ]), + ) + }), + { git: true }, + ) + + it.instance( + "diff('git') keeps batched patches aligned for type changes", + () => + Effect.gen(function* () { + if (process.platform === "win32") return + + const test = yield* TestInstance + yield* write(path.join(test.directory, "a.txt"), "old\n") + yield* write(path.join(test.directory, "b.txt"), "old\n") + yield* git(test.directory, ["add", "."]) + yield* git(test.directory, ["commit", "--no-gpg-sign", "-m", "add files"]) + yield* remove(path.join(test.directory, "a.txt")) + yield* symlink("target", path.join(test.directory, "a.txt")) + yield* write(path.join(test.directory, "b.txt"), "new\n") + + const vcs = yield* init() + const diff = yield* vcs.diff("git") + const a = diff.find((item) => item.file === "a.txt") + const b = diff.find((item) => item.file === "b.txt") + + expect(a?.patch).toContain("deleted file mode") + expect(a?.patch).toContain("new file mode") + expect(b?.patch).toContain("+new") + }), + { git: true }, + ) + + it.instance( + "diff('git') keeps carriage returns inside patch hunks", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* write(path.join(test.directory, "file.txt"), "keep\nsame\rdiff --git inside\ndelete\n") + yield* git(test.directory, ["add", "."]) + yield* git(test.directory, ["commit", "--no-gpg-sign", "-m", "add file"]) + yield* write(path.join(test.directory, "file.txt"), "keep\nadd\nsame\rdiff --git inside\n") + + const vcs = yield* init() + const diff = yield* vcs.diff("git") + const file = diff.find((item) => item.file === "file.txt") + + expect(file?.patch).toContain(" same\rdiff --git inside") + expect(file?.patch).toContain("-delete") + expect(() => parsePatch(file?.patch ?? "")).not.toThrow() + }), + { git: true }, + 20_000, + ) + + it.instance( + "diff('branch') returns changes against default branch", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* git(test.directory, ["branch", "-M", "main"]) + yield* git(test.directory, ["checkout", "-b", "feature/test"]) + yield* write(path.join(test.directory, "branch.txt"), "hello\n") + yield* git(test.directory, ["add", "."]) + yield* git(test.directory, ["commit", "--no-gpg-sign", "-m", "branch file"]) + + const vcs = yield* init() + const diff = yield* vcs.diff("branch") + + expect(diff).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + file: "branch.txt", + status: "added", + }), + ]), + ) + }), + { git: true }, + ) }) diff --git a/packages/opencode/test/project/worktree.test.ts b/packages/opencode/test/project/worktree.test.ts index 4cd2265d678..308e2f957b4 100644 --- a/packages/opencode/test/project/worktree.test.ts +++ b/packages/opencode/test/project/worktree.test.ts @@ -1,257 +1,308 @@ -import { $ } from "bun" import { afterEach, describe, expect } from "bun:test" -import * as fs from "fs/promises" import path from "path" -import { Cause, Effect, Exit, Layer } from "effect" +import { AppFileSystem } from "@opencode-ai/core/filesystem" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" +import { Cause, Deferred, Effect, Exit, Fiber, Layer } from "effect" +import { GlobalBus, type GlobalEvent } from "../../src/bus/global" +import { Git } from "../../src/git" import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" import { InstanceRuntime } from "../../src/project/instance-runtime" import { Worktree } from "../../src/worktree" -import { disposeAllInstances, provideInstance, provideTmpdirInstance } from "../fixture/fixture" +import { disposeAllInstances, provideInstance, TestInstance } from "../fixture/fixture" import { testEffect } from "../lib/effect" -const it = testEffect(Layer.mergeAll(Worktree.defaultLayer, CrossSpawnSpawner.defaultLayer)) -const wintest = process.platform !== "win32" ? it.live : it.live.skip +const it = testEffect( + Layer.mergeAll(Worktree.defaultLayer, AppFileSystem.defaultLayer, CrossSpawnSpawner.defaultLayer, Git.defaultLayer), +) +const wintest = process.platform !== "win32" ? it.instance : it.instance.skip function normalize(input: string) { return input.replace(/\\/g, "/").toLowerCase() } -async function waitReady() { - const { GlobalBus } = await import("../../src/bus/global") +const waitReady = Effect.fn("WorktreeTest.waitReady")(function* () { + const ready = yield* Deferred.make<{ name: string; branch?: string }>() + const on = (evt: GlobalEvent) => { + if (evt.payload.type !== Worktree.Event.Ready.type) return + Deferred.doneUnsafe(ready, Effect.succeed(evt.payload.properties)) + } + + GlobalBus.on("event", on) + yield* Effect.addFinalizer(() => Effect.sync(() => GlobalBus.off("event", on))) + + return yield* Deferred.await(ready).pipe( + Effect.timeoutOrElse({ + duration: "10 seconds", + orElse: () => Effect.fail(new Error("timed out waiting for worktree.ready")), + }), + ) +}) - return await new Promise<{ name: string; branch: string }>((resolve, reject) => { - const timer = setTimeout(() => { - GlobalBus.off("event", on) - reject(new Error("timed out waiting for worktree.ready")) - }, 10_000) +const removeCreatedWorktree = (directory: string) => + Effect.gen(function* () { + const svc = yield* Worktree.Service + const ctx = yield* Effect.sync(() => Instance.current).pipe(provideInstance(directory)) + yield* Effect.promise(() => InstanceRuntime.disposeInstance(ctx)) + const ok = yield* svc.remove({ directory }) + if (!ok) return yield* Effect.fail(new Error(`failed to remove worktree ${directory}`)) + }) - function on(evt: { directory?: string; payload: { type: string; properties: { name: string; branch: string } } }) { - if (evt.payload.type !== Worktree.Event.Ready.type) return - clearTimeout(timer) - GlobalBus.off("event", on) - resolve(evt.payload.properties) - } +const withCreatedWorktree = ( + input: Parameters[0], + use: (created: { info: Worktree.Info; ready: { name: string; branch?: string } }) => Effect.Effect, +) => + Effect.acquireUseRelease( + Effect.gen(function* () { + const svc = yield* Worktree.Service + const ready = yield* waitReady().pipe(Effect.forkScoped) + const info = yield* svc.create(input) + const props = yield* Fiber.join(ready) + return { info, ready: props } + }), + use, + ({ info }) => removeCreatedWorktree(info.directory), + ) + +const git = Effect.fn("WorktreeTest.git")(function* (cwd: string, args: string[]) { + const service = yield* Git.Service + const result = yield* service.run(args, { cwd }) + if (result.exitCode !== 0) throw new Error(`git ${args.join(" ")} failed: ${result.stderr.toString("utf8")}`) + return result.text() +}) - GlobalBus.on("event", on) - }) -} +const gitResult = Effect.fn("WorktreeTest.gitResult")(function* (cwd: string, args: string[]) { + const service = yield* Git.Service + return yield* service.run(args, { cwd }) +}) describe("Worktree", () => { afterEach(() => disposeAllInstances()) describe("makeWorktreeInfo", () => { - it.live("returns info with name, branch, and directory", () => - provideTmpdirInstance( - () => - Effect.gen(function* () { - const svc = yield* Worktree.Service - const info = yield* svc.makeWorktreeInfo() + it.instance( + "returns info with name, branch, and directory", + () => + Effect.gen(function* () { + const svc = yield* Worktree.Service + const info = yield* svc.makeWorktreeInfo() - expect(info.name).toBeDefined() - expect(typeof info.name).toBe("string") - expect(info.branch).toBe(`opencode/${info.name}`) - expect(info.directory).toContain(info.name) - }), - { git: true }, - ), + expect(info.name).toBeDefined() + expect(typeof info.name).toBe("string") + expect(info.branch).toBe(`opencode/${info.name}`) + expect(info.directory).toContain(info.name) + }), + { git: true }, ) - it.live("uses provided name as base", () => - provideTmpdirInstance( - () => - Effect.gen(function* () { - const svc = yield* Worktree.Service - const info = yield* svc.makeWorktreeInfo("my-feature") + it.instance( + "uses provided name as base", + () => + Effect.gen(function* () { + const svc = yield* Worktree.Service + const info = yield* svc.makeWorktreeInfo({ name: "my-feature" }) - expect(info.name).toBe("my-feature") - expect(info.branch).toBe("opencode/my-feature") - }), - { git: true }, - ), + expect(info.name).toBe("my-feature") + expect(info.branch).toBe("opencode/my-feature") + }), + { git: true }, ) - it.live("slugifies the provided name", () => - provideTmpdirInstance( - () => - Effect.gen(function* () { - const svc = yield* Worktree.Service - const info = yield* svc.makeWorktreeInfo("My Feature Branch!") + it.instance( + "slugifies the provided name", + () => + Effect.gen(function* () { + const svc = yield* Worktree.Service + const info = yield* svc.makeWorktreeInfo({ name: "My Feature Branch!" }) - expect(info.name).toBe("my-feature-branch") - }), - { git: true }, - ), + expect(info.name).toBe("my-feature-branch") + }), + { git: true }, + ) + + it.instance( + "omits branch for detached info", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const svc = yield* Worktree.Service + yield* git(test.directory, ["branch", "opencode/my-feature"]) + + const info = yield* svc.makeWorktreeInfo({ name: "my-feature", detached: true }) + + expect(info.name).toBe("my-feature") + expect(info.branch).toBeUndefined() + }), + { git: true }, + ) + + it.instance("fails with NotGitError for non-git directories", () => + Effect.gen(function* () { + const svc = yield* Worktree.Service + const exit = yield* Effect.exit(svc.makeWorktreeInfo()) + + expect(Exit.isFailure(exit)).toBe(true) + if (Exit.isFailure(exit)) { + const error = Cause.squash(exit.cause) + expect(error).toBeInstanceOf(Worktree.NotGitError) + if (error instanceof Worktree.NotGitError) expect(error._tag).toBe("WorktreeNotGitError") + } + }), ) - it.live("throws NotGitError for non-git directories", () => - provideTmpdirInstance(() => + wintest( + "creates detached git worktree when info has no branch", + () => Effect.gen(function* () { + const test = yield* TestInstance const svc = yield* Worktree.Service - const exit = yield* Effect.exit(svc.makeWorktreeInfo()) + const info = yield* svc.makeWorktreeInfo({ name: "detached-test", detached: true }) + const ready = yield* waitReady().pipe(Effect.forkScoped) + yield* svc.createFromInfo(info) - expect(Exit.isFailure(exit)).toBe(true) - if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Worktree.NotGitError) + const list = yield* git(test.directory, ["worktree", "list", "--porcelain"]) + const normalizedList = normalize(list) + const normalizedDir = normalize(info.directory) + expect(normalizedList).toContain(normalizedDir) + + const branch = yield* gitResult(info.directory, ["symbolic-ref", "-q", "--short", "HEAD"]) + expect(branch.exitCode).not.toBe(0) + + const props = yield* Fiber.join(ready) + expect(props.name).toBe(info.name) + expect(props.branch).toBeUndefined() + + yield* svc.remove({ directory: info.directory }) }), - ), + { git: true }, ) }) describe("create + remove lifecycle", () => { - it.live("create returns worktree info and remove cleans up", () => - provideTmpdirInstance( - () => + it.instance( + "create returns worktree info and remove cleans up", + () => + withCreatedWorktree(undefined, ({ info }) => Effect.gen(function* () { - const svc = yield* Worktree.Service - const info = yield* svc.create() - expect(info.name).toBeDefined() - expect(info.branch).toStartWith("opencode/") + expect(info.branch ?? "").toStartWith("opencode/") expect(info.directory).toBeDefined() - - yield* Effect.promise(() => Bun.sleep(1000)) - - const ok = yield* svc.remove({ directory: info.directory }) - expect(ok).toBe(true) }), - { git: true }, - ), + ), + { git: true }, ) - it.live("create returns after setup and fires Event.Ready after bootstrap", () => - provideTmpdirInstance( - (dir) => + it.instance( + "create returns after setup and fires Event.Ready after bootstrap", + () => + withCreatedWorktree(undefined, ({ info, ready }) => Effect.gen(function* () { const svc = yield* Worktree.Service - const ready = waitReady() - const info = yield* svc.create() expect(info.name).toBeDefined() - expect(info.branch).toStartWith("opencode/") - - const text = yield* Effect.promise(() => $`git worktree list --porcelain`.cwd(dir).quiet().text()) - const next = yield* Effect.promise(() => fs.realpath(info.directory).catch(() => info.directory)) - expect(normalize(text)).toContain(normalize(next)) - - const props = yield* Effect.promise(() => ready) - expect(props.name).toBe(info.name) - expect(props.branch).toBe(info.branch) - - yield* Effect.promise(() => - WithInstance.provide({ - directory: info.directory, - fn: () => InstanceRuntime.disposeInstance(Instance.current), - }), - ) - yield* Effect.promise(() => Bun.sleep(100)) - yield* svc.remove({ directory: info.directory }) + expect(info.branch ?? "").toStartWith("opencode/") + + expect(ready.name).toBe(info.name) + expect(ready.branch).toBe(info.branch) + + const list = yield* svc.list() + expect(list).toContainEqual(expect.objectContaining({ name: info.name, branch: info.branch })) }), - { git: true }, - ), + ), + { git: true }, ) - it.live("create with custom name", () => - provideTmpdirInstance( - () => + it.instance( + "create with custom name", + () => + withCreatedWorktree({ name: "test-workspace" }, ({ info }) => Effect.gen(function* () { - const svc = yield* Worktree.Service - const ready = waitReady() - const info = yield* svc.create({ name: "test-workspace" }) - expect(info.name).toBe("test-workspace") expect(info.branch).toBe("opencode/test-workspace") - - yield* Effect.promise(() => ready) - yield* Effect.promise(() => - WithInstance.provide({ - directory: info.directory, - fn: () => InstanceRuntime.disposeInstance(Instance.current), - }), - ) - yield* Effect.promise(() => Bun.sleep(100)) - yield* svc.remove({ directory: info.directory }) }), - { git: true }, - ), + ), + { git: true }, ) }) describe("createFromInfo", () => { - wintest("creates git worktree and boots asynchronously", () => - provideTmpdirInstance( - (dir) => - Effect.gen(function* () { - const svc = yield* Worktree.Service - const info = yield* svc.makeWorktreeInfo("from-info-test") - const ready = waitReady() - yield* svc.createFromInfo(info) + wintest( + "creates git worktree and boots asynchronously", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const svc = yield* Worktree.Service + const info = yield* svc.makeWorktreeInfo({ name: "from-info-test" }) + const ready = yield* waitReady().pipe(Effect.forkScoped) + yield* svc.createFromInfo(info) - const list = yield* Effect.promise(() => $`git worktree list --porcelain`.cwd(dir).quiet().text()) - const normalizedList = list.replace(/\\/g, "/") - const normalizedDir = info.directory.replace(/\\/g, "/") - expect(normalizedList).toContain(normalizedDir) + const list = yield* git(test.directory, ["worktree", "list", "--porcelain"]) + const normalizedList = list.replace(/\\/g, "/") + const normalizedDir = info.directory.replace(/\\/g, "/") + expect(normalizedList).toContain(normalizedDir) - yield* Effect.promise(() => ready) - yield* svc.remove({ directory: info.directory }) - }), - { git: true }, - ), + yield* Fiber.join(ready) + yield* removeCreatedWorktree(info.directory) + }), + { git: true }, ) }) describe("list", () => { - it.live("uses parent folder name when worktree basename matches the primary worktree", () => - provideTmpdirInstance( - (dir) => - Effect.gen(function* () { - const svc = yield* Worktree.Service - const parent = path.join(path.dirname(dir), `${path.basename(dir)}-parent`) - const target = path.join(parent, path.basename(dir)) - const branch = `same-basename-list-${Date.now()}` + it.instance( + "uses parent folder name when worktree basename matches the primary worktree", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const fs = yield* AppFileSystem.Service + const svc = yield* Worktree.Service + const parent = path.join(path.dirname(test.directory), `${path.basename(test.directory)}-parent`) + const target = path.join(parent, path.basename(test.directory)) + const branch = `same-basename-list-${Date.now()}` - yield* Effect.promise(() => fs.mkdir(parent, { recursive: true })) - yield* Effect.promise(() => $`git worktree add -b ${branch} ${target}`.cwd(dir).quiet()) + yield* fs.ensureDir(parent) + yield* git(test.directory, ["worktree", "add", "-b", branch, target]) - const list = yield* svc.list() - const directory = process.platform === "win32" ? path.resolve(target).toLowerCase() : path.resolve(target) + const list = yield* svc.list() + const directory = yield* fs.realPath(target).pipe(Effect.catch(() => Effect.succeed(target))) - expect(list).toContainEqual({ - name: path.basename(parent), - branch, - directory, - }) + expect(list.map((item) => ({ ...item, directory: normalize(item.directory) }))).toContainEqual({ + name: path.basename(parent), + branch, + directory: normalize(directory), + }) - yield* svc.remove({ directory: target }) - }), - { git: true }, - ), + yield* svc.remove({ directory: target }) + }), + { git: true }, ) }) describe("remove edge cases", () => { - it.live("remove non-existent directory succeeds silently", () => - provideTmpdirInstance( - (dir) => - Effect.gen(function* () { - const svc = yield* Worktree.Service - const ok = yield* svc.remove({ directory: path.join(dir, "does-not-exist") }) - expect(ok).toBe(true) - }), - { git: true }, - ), - ) - - it.live("throws NotGitError for non-git directories", () => - provideTmpdirInstance(() => + it.instance( + "remove non-existent directory succeeds silently", + () => Effect.gen(function* () { + const test = yield* TestInstance const svc = yield* Worktree.Service - const exit = yield* Effect.exit(svc.remove({ directory: "/tmp/fake" })) - - expect(Exit.isFailure(exit)).toBe(true) - if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Worktree.NotGitError) + const ok = yield* svc.remove({ directory: path.join(test.directory, "does-not-exist") }) + expect(ok).toBe(true) }), - ), + { git: true }, + ) + + it.instance("fails with NotGitError for non-git directories", () => + Effect.gen(function* () { + const test = yield* TestInstance + const svc = yield* Worktree.Service + const exit = yield* Effect.exit(svc.remove({ directory: path.join(test.directory, "fake") })) + + expect(Exit.isFailure(exit)).toBe(true) + if (Exit.isFailure(exit)) { + const error = Cause.squash(exit.cause) + expect(error).toBeInstanceOf(Worktree.NotGitError) + if (error instanceof Worktree.NotGitError) expect(error._tag).toBe("WorktreeNotGitError") + } + }), ) }) }) diff --git a/packages/opencode/test/provider/digitalocean.test.ts b/packages/opencode/test/provider/digitalocean.test.ts new file mode 100644 index 00000000000..d74ccb33491 --- /dev/null +++ b/packages/opencode/test/provider/digitalocean.test.ts @@ -0,0 +1,122 @@ +import { expect } from "bun:test" +import { Provider } from "../../src/provider/provider" +import { ProviderID } from "../../src/provider/schema" +import { Effect } from "effect" +import { testEffect } from "../lib/effect" + +const DIGITALOCEAN = ProviderID.make("digitalocean") +const it = testEffect(Provider.defaultLayer) + +const withEnv = (values: Record, effect: Effect.Effect) => + Effect.acquireUseRelease( + Effect.sync(() => { + const previous = Object.fromEntries(Object.keys(values).map((key) => [key, process.env[key]] as const)) + Object.assign(process.env, values) + return previous + }), + () => effect, + (previous) => + Effect.sync(() => { + for (const [key, value] of Object.entries(previous)) { + if (value === undefined) delete process.env[key] + else process.env[key] = value + } + }), + ) + +const withAuth = (metadata: Record | undefined, effect: Effect.Effect) => + withEnv( + { + KILO_AUTH_CONTENT: JSON.stringify({ + digitalocean: { + type: "api", + key: "sk_do_test", + ...(metadata ? { metadata } : {}), + }, + }), + }, + effect, + ) + +it.instance( + "digitalocean provider autoloads from DIGITALOCEAN_ACCESS_TOKEN", + () => + withEnv( + { DIGITALOCEAN_ACCESS_TOKEN: "test-token" }, + Effect.gen(function* () { + const provider = yield* Provider.Service + const providers = yield* provider.list() + expect(providers[DIGITALOCEAN]).toBeDefined() + expect(providers[DIGITALOCEAN].source).toBe("env") + const baseModel = Object.values(providers[DIGITALOCEAN].models)[0] + expect(baseModel.api.url).toBe("https://inference.do-ai.run/v1") + expect(baseModel.api.npm).toBe("@ai-sdk/openai-compatible") + const routerEntries = Object.keys(providers[DIGITALOCEAN].models).filter((id) => id.startsWith("router:")) + expect(routerEntries.length).toBe(0) + }), + ), + { config: {} }, +) + +it.instance( + "digitalocean provider.models surfaces cached routers from auth metadata", + () => + withAuth( + { + routers: JSON.stringify([ + { name: "my-router", uuid: "11f1499a-aaaa-bbbb-cccc-4e013e2ddde4" }, + { name: "other-router", uuid: "22f1499a-aaaa-bbbb-cccc-4e013e2ddde4" }, + ]), + routers_fetched_at: String(Date.now()), + oauth_access: "doo_v1_test", + oauth_expires: String(Date.now() + 60 * 60 * 1000), + }, + Effect.gen(function* () { + const provider = yield* Provider.Service + const providers = yield* provider.list() + const models = providers[DIGITALOCEAN].models + expect(models["router:my-router"]).toBeDefined() + expect(models["router:my-router"].api.id).toBe("router:my-router") + expect(models["router:my-router"].api.url).toBe("https://inference.do-ai.run/v1") + expect(models["router:my-router"].api.npm).toBe("@ai-sdk/openai-compatible") + expect(models["router:other-router"]).toBeDefined() + }), + ), + { config: {} }, +) + +it.instance( + "digitalocean provider.models skips refresh when oauth bearer is expired", + () => + withAuth( + { + routers: JSON.stringify([{ name: "stale-router", uuid: "stale" }]), + routers_fetched_at: "0", + oauth_access: "doo_v1_expired", + oauth_expires: "1", + }, + Effect.gen(function* () { + const provider = yield* Provider.Service + const providers = yield* provider.list() + const models = providers[DIGITALOCEAN].models + expect(models["router:stale-router"]).toBeDefined() + }), + ), + { config: {} }, +) + +it.instance( + "digitalocean provider.models passes through base models when no auth metadata", + () => + withEnv( + { DIGITALOCEAN_ACCESS_TOKEN: "test-token" }, + Effect.gen(function* () { + const provider = yield* Provider.Service + const providers = yield* provider.list() + const models = providers[DIGITALOCEAN].models + expect(Object.keys(models).length).toBeGreaterThan(0) + expect(Object.keys(models).filter((id) => id.startsWith("router:")).length).toBe(0) + }), + ), + { config: {} }, +) diff --git a/packages/opencode/test/provider/model-status.test.ts b/packages/opencode/test/provider/model-status.test.ts index e6fa645e714..96f50a1b542 100644 --- a/packages/opencode/test/provider/model-status.test.ts +++ b/packages/opencode/test/provider/model-status.test.ts @@ -2,7 +2,7 @@ import { describe, expect, test } from "bun:test" import { Schema } from "effect" import { ConfigProvider } from "@/config/provider" import { CatalogModelStatus, ModelStatus } from "@/provider/model-status" -import { ModelsDev } from "@/provider/models" +import { ModelsDev } from "@opencode-ai/core/models" import { Provider } from "@/provider/provider" describe("provider model status schemas", () => { diff --git a/packages/opencode/test/provider/provider.test.ts b/packages/opencode/test/provider/provider.test.ts index bd90ac271c2..9cf1af1bb85 100644 --- a/packages/opencode/test/provider/provider.test.ts +++ b/packages/opencode/test/provider/provider.test.ts @@ -8,17 +8,34 @@ import { Instance } from "../../src/project/instance" import { InstanceRuntime } from "../../src/project/instance-runtime" import { WithInstance } from "../../src/project/with-instance" import { Plugin } from "../../src/plugin/index" -import { ModelsDev } from "@/provider/models" +import { ModelsDev } from "@opencode-ai/core/models" import { Provider } from "@/provider/provider" import { ProviderID, ModelID } from "../../src/provider/schema" import { Filesystem } from "@/util/filesystem" import { Env } from "../../src/env" -import { Effect } from "effect" +import { Effect, Layer } from "effect" import { AppRuntime } from "../../src/effect/app-runtime" import { makeRuntime } from "../../src/effect/run-service" +import { testEffect } from "../lib/effect" +import { AppFileSystem } from "@opencode-ai/core/filesystem" +import { Config } from "@/config/config" +import { Auth } from "@/auth" +import { RuntimeFlags } from "@/effect/runtime-flags" const env = makeRuntime(Env.Service, Env.defaultLayer) const set = (k: string, v: string) => env.runSync((svc) => svc.set(k, v)) +const remove = (k: string) => env.runSync((svc) => svc.remove(k)) + +const providerLayer = (flags: Partial = {}) => + Provider.layer.pipe( + Layer.provide(AppFileSystem.defaultLayer), + Layer.provide(Env.defaultLayer), + Layer.provide(Config.defaultLayer), + Layer.provide(Auth.defaultLayer), + Layer.provide(Plugin.defaultLayer), + Layer.provide(ModelsDev.defaultLayer), + Layer.provide(RuntimeFlags.layer(flags)), + ) async function run(fn: (provider: Provider.Interface) => Effect.Effect) { return AppRuntime.runPromise( @@ -57,15 +74,13 @@ async function defaultModel() { return run((provider) => provider.defaultModel()) } -// kilocode_change start - upstream #24416 fixture adapted for @kilocode/plugin async function markPluginDependenciesReady(dir: string) { await mkdir(path.join(dir, "node_modules"), { recursive: true }) await Bun.write( path.join(dir, "package-lock.json"), - JSON.stringify({ packages: { "": { dependencies: { "@kilocode/plugin": "0.0.0" } } } }), + JSON.stringify({ packages: { "": { dependencies: { "@opencode-ai/plugin": "0.0.0" } } } }), ) } -// kilocode_change end function paid(providers: Awaited>) { const item = providers[ProviderID.make("opencode")] @@ -73,13 +88,38 @@ function paid(providers: Awaited>) { return Object.values(item.models).filter((model) => model.cost.input > 0).length } +const it = testEffect(Provider.defaultLayer) +const experimentalModels = testEffect(providerLayer({ enableExperimentalModels: true })) + +const alphaProviderConfig = { + provider: { + "custom-provider": { + name: "Custom Provider", + npm: "@ai-sdk/openai-compatible", + api: "https://api.custom.com/v1", + models: { + "active-model": { + name: "Active Model", + }, + "alpha-model": { + name: "Alpha Model", + status: "alpha" as const, + }, + }, + options: { + apiKey: "custom-key", + }, + }, + }, +} + test("provider loaded from env variable", async () => { await using tmp = await tmpdir({ init: async (dir) => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", }), ) }, @@ -98,67 +138,13 @@ test("provider loaded from env variable", async () => { }) }) -// kilocode_change start -test("provider OAuth auth overrides inherited env variable", async () => { - const authPath = path.join(Global.Path.data, "auth.json") - const prev = await Filesystem.readText(authPath).catch(() => undefined) - - try { - await Filesystem.write( - authPath, - JSON.stringify({ - openai: { - type: "oauth", - refresh: "test-refresh-token", - access: "test-access-token", - expires: Date.now() + 60_000, - }, - }), - ) - - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", - }), - ) - }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - set("OPENAI_API_KEY", "test-openai-key") - const providers = await list() - const provider = providers[ProviderID.openai] - expect(provider).toBeDefined() - if (!provider) throw new Error("Expected OpenAI provider") - expect(provider.source).toBe("custom") - expect(provider.key).toBeUndefined() - expect(Object.values(provider.models).every((model) => model.cost.input === 0 && model.cost.output === 0)).toBe( - true, - ) - }, - }) - } finally { - if (prev !== undefined) { - await Filesystem.write(authPath, prev) - } - if (prev === undefined) { - await unlink(authPath).catch(() => undefined) - } - } -}) -// kilocode_change end - test("provider loaded from config with apiKey option", async () => { await using tmp = await tmpdir({ init: async (dir) => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { anthropic: { options: { @@ -185,7 +171,7 @@ test("disabled_providers excludes provider", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", disabled_providers: ["anthropic"], }), ) @@ -207,7 +193,7 @@ test("enabled_providers restricts to only listed providers", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", enabled_providers: ["anthropic"], }), ) @@ -231,7 +217,7 @@ test("model whitelist filters models for provider", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { anthropic: { whitelist: ["claude-sonnet-4-20250514"], @@ -260,7 +246,7 @@ test("model blacklist excludes specific models", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { anthropic: { blacklist: ["claude-sonnet-4-20250514"], @@ -288,7 +274,7 @@ test("custom model alias via config", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { anthropic: { models: { @@ -321,7 +307,7 @@ test("custom provider with npm package", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { "custom-provider": { name: "Custom Provider", @@ -358,6 +344,26 @@ test("custom provider with npm package", async () => { }) }) +it.instance( + "filters alpha provider models by default", + Effect.gen(function* () { + const providers = yield* Provider.Service.use((provider) => provider.list()) + expect(providers[ProviderID.make("custom-provider")].models["active-model"]).toBeDefined() + expect(providers[ProviderID.make("custom-provider")].models["alpha-model"]).toBeUndefined() + }), + { config: alphaProviderConfig }, +) + +experimentalModels.instance( + "includes alpha provider models when experimental models are enabled", + Effect.gen(function* () { + const providers = yield* Provider.Service.use((provider) => provider.list()) + expect(providers[ProviderID.make("custom-provider")].models["active-model"]).toBeDefined() + expect(providers[ProviderID.make("custom-provider")].models["alpha-model"]).toBeDefined() + }), + { config: alphaProviderConfig }, +) + test("custom DeepSeek openai-compatible model defaults interleaved reasoning field", async () => { await using tmp = await tmpdir({ init: async (dir) => { @@ -425,7 +431,7 @@ test("env variable takes precedence, config merges options", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { anthropic: { options: { @@ -457,7 +463,7 @@ test("getModel returns model for valid provider/model", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", }), ) }, @@ -482,7 +488,7 @@ test("getModel throws ModelNotFoundError for invalid model", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", }), ) }, @@ -502,7 +508,7 @@ test("getModel throws ModelNotFoundError for invalid provider", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", }), ) }, @@ -533,7 +539,7 @@ test("defaultModel returns first available model when no config set", async () = await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", }), ) }, @@ -555,7 +561,7 @@ test("defaultModel respects config model setting", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", model: "anthropic/claude-sonnet-4-20250514", }), ) @@ -572,144 +578,116 @@ test("defaultModel respects config model setting", async () => { }) }) -test("provider with baseURL from config", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", - provider: { - "custom-openai": { - name: "Custom OpenAI", - npm: "@ai-sdk/openai-compatible", - env: [], - models: { - "gpt-4": { - name: "GPT-4", - tool_call: true, - limit: { context: 128000, output: 4096 }, - }, - }, - options: { - apiKey: "test-key", - baseURL: "https://custom.openai.com/v1", - }, +it.instance( + "provider with baseURL from config", + Effect.gen(function* () { + const providers = yield* Provider.Service.use((provider) => provider.list()) + expect(providers[ProviderID.make("custom-openai")]).toBeDefined() + expect(providers[ProviderID.make("custom-openai")].options.baseURL).toBe("https://custom.openai.com/v1") + }), + { + config: { + provider: { + "custom-openai": { + name: "Custom OpenAI", + npm: "@ai-sdk/openai-compatible", + env: [], + models: { + "gpt-4": { + name: "GPT-4", + tool_call: true, + limit: { context: 128000, output: 4096 }, }, }, - }), - ) - }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const providers = await list() - expect(providers[ProviderID.make("custom-openai")]).toBeDefined() - expect(providers[ProviderID.make("custom-openai")].options.baseURL).toBe("https://custom.openai.com/v1") + options: { + apiKey: "test-key", + baseURL: "https://custom.openai.com/v1", + }, + }, + }, }, - }) -}) - -test("model cost defaults to zero when not specified", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", - provider: { - "test-provider": { - name: "Test Provider", - npm: "@ai-sdk/openai-compatible", - env: [], - models: { - "test-model": { - name: "Test Model", - tool_call: true, - limit: { context: 128000, output: 4096 }, - }, - }, - options: { - apiKey: "test-key", - }, + }, +) + +it.instance( + "model cost defaults to zero when not specified", + Effect.gen(function* () { + const providers = yield* Provider.Service.use((provider) => provider.list()) + const model = providers[ProviderID.make("test-provider")].models["test-model"] + expect(model.cost.input).toBe(0) + expect(model.cost.output).toBe(0) + expect(model.cost.cache.read).toBe(0) + expect(model.cost.cache.write).toBe(0) + }), + { + config: { + provider: { + "test-provider": { + name: "Test Provider", + npm: "@ai-sdk/openai-compatible", + env: [], + models: { + "test-model": { + name: "Test Model", + tool_call: true, + limit: { context: 128000, output: 4096 }, }, }, - }), - ) - }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const providers = await list() - const model = providers[ProviderID.make("test-provider")].models["test-model"] - expect(model.cost.input).toBe(0) - expect(model.cost.output).toBe(0) - expect(model.cost.cache.read).toBe(0) - expect(model.cost.cache.write).toBe(0) + options: { + apiKey: "test-key", + }, + }, + }, }, - }) -}) - -test("model options are merged from existing model", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", - provider: { - anthropic: { - models: { - "claude-sonnet-4-20250514": { - options: { - customOption: "custom-value", - }, - }, + }, +) + +it.instance( + "model options are merged from existing model", + Effect.gen(function* () { + const providers = yield* Provider.Service.use((provider) => provider.list()) + const model = providers[ProviderID.anthropic].models["claude-sonnet-4-20250514"] + expect(model.options.customOption).toBe("custom-value") + }), + { + config: { + provider: { + anthropic: { + options: { + apiKey: "test-api-key", + }, + models: { + "claude-sonnet-4-20250514": { + options: { + customOption: "custom-value", }, }, }, - }), - ) - }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - set("ANTHROPIC_API_KEY", "test-api-key") - const providers = await list() - const model = providers[ProviderID.anthropic].models["claude-sonnet-4-20250514"] - expect(model.options.customOption).toBe("custom-value") + }, + }, }, - }) -}) - -test("provider removed when all models filtered out", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", - provider: { - anthropic: { - whitelist: ["nonexistent-model"], - }, + }, +) + +it.instance( + "provider removed when all models filtered out", + Effect.gen(function* () { + const providers = yield* Provider.Service.use((provider) => provider.list()) + expect(providers[ProviderID.anthropic]).toBeUndefined() + }), + { + config: { + provider: { + anthropic: { + options: { + apiKey: "test-api-key", }, - }), - ) - }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - set("ANTHROPIC_API_KEY", "test-api-key") - const providers = await list() - expect(providers[ProviderID.anthropic]).toBeUndefined() + whitelist: ["nonexistent-model"], + }, + }, }, - }) -}) + }, +) test("closest finds model by partial match", async () => { await using tmp = await tmpdir({ @@ -717,7 +695,7 @@ test("closest finds model by partial match", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", }), ) }, @@ -740,7 +718,7 @@ test("closest returns undefined for nonexistent provider", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", }), ) }, @@ -760,7 +738,7 @@ test("getModel uses realIdByKey for aliased models", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { anthropic: { models: { @@ -796,7 +774,7 @@ test("provider api field sets model api.url", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { "custom-api": { name: "Custom API", @@ -835,7 +813,7 @@ test("explicit baseURL overrides api field", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { "custom-api": { name: "Custom API", @@ -874,7 +852,7 @@ test("model inherits properties from existing database model", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { anthropic: { models: { @@ -908,7 +886,7 @@ test("disabled_providers prevents loading even with env var", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", disabled_providers: ["openai"], }), ) @@ -930,7 +908,7 @@ test("enabled_providers with empty array allows no providers", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", enabled_providers: [], }), ) @@ -953,7 +931,7 @@ test("whitelist and blacklist can be combined", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { anthropic: { whitelist: ["claude-sonnet-4-20250514", "claude-opus-4-20250514"], @@ -984,7 +962,7 @@ test("model modalities default correctly", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { "test-provider": { name: "Test", @@ -1021,7 +999,7 @@ test("model with custom cost values", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { "test-provider": { name: "Test", @@ -1066,7 +1044,7 @@ test("getSmallModel returns appropriate small model", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", }), ) }, @@ -1088,7 +1066,7 @@ test("getSmallModel respects config small_model override", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", small_model: "anthropic/claude-sonnet-4-20250514", }), ) @@ -1106,6 +1084,27 @@ test("getSmallModel respects config small_model override", async () => { }) }) +test("getSmallModel ignores invalid config small_model", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ + $schema: "https://opencode.ai/config.json", + small_model: "anthropic/not-a-real-model", + }), + ) + }, + }) + await WithInstance.provide({ + directory: tmp.path, + fn: async () => { + set("ANTHROPIC_API_KEY", "test-api-key") + expect(await getSmallModel(ProviderID.anthropic)).toBeUndefined() + }, + }) +}) + test("provider.sort prioritizes preferred models", () => { const models = [ { id: "random-model", name: "Random" }, @@ -1127,7 +1126,7 @@ test("multiple providers can be configured simultaneously", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { anthropic: { options: { timeout: 30000 }, @@ -1160,7 +1159,7 @@ test("provider with custom npm package", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { "local-llm": { name: "Local LLM", @@ -1202,7 +1201,7 @@ test("model alias name defaults to alias key when id differs", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { anthropic: { models: { @@ -1233,7 +1232,7 @@ test("provider with multiple env var options only includes apiKey when single en await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { "multi-env": { name: "Multi Env Provider", @@ -1273,7 +1272,7 @@ test("provider with single env var includes apiKey automatically", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { "single-env": { name: "Single Env Provider", @@ -1313,7 +1312,7 @@ test("model cost overrides existing cost values", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { anthropic: { models: { @@ -1348,7 +1347,7 @@ test("completely new provider not in database can be configured", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { "brand-new-provider": { name: "Brand New", @@ -1398,7 +1397,7 @@ test("disabled_providers and enabled_providers interaction", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", // enabled_providers takes precedence - only these are considered enabled_providers: ["anthropic", "openai"], // Then disabled_providers filters from the enabled set @@ -1430,7 +1429,7 @@ test("model with tool_call false", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { "no-tools": { name: "No Tools Provider", @@ -1465,7 +1464,7 @@ test("model defaults tool_call to true when not specified", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { "default-tools": { name: "Default Tools Provider", @@ -1500,7 +1499,7 @@ test("model headers are preserved", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { "headers-provider": { name: "Headers Provider", @@ -1543,7 +1542,7 @@ test("provider env fallback - second env var used if first missing", async () => await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { "fallback-env": { name: "Fallback Env Provider", @@ -1581,7 +1580,7 @@ test("getModel returns consistent results", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", }), ) }, @@ -1605,7 +1604,7 @@ test("provider name defaults to id when not in database", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { "my-custom-id": { // no name specified @@ -1640,7 +1639,7 @@ test("ModelNotFoundError includes suggestions for typos", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", }), ) }, @@ -1653,8 +1652,8 @@ test("ModelNotFoundError includes suggestions for typos", async () => { await getModel(ProviderID.anthropic, ModelID.make("claude-sonet-4")) // typo: sonet instead of sonnet expect(true).toBe(false) // Should not reach here } catch (e: any) { - expect(e.data.suggestions).toBeDefined() - expect(e.data.suggestions.length).toBeGreaterThan(0) + expect(e.suggestions).toBeDefined() + expect(e.suggestions.length).toBeGreaterThan(0) } }, }) @@ -1666,7 +1665,7 @@ test("ModelNotFoundError for provider includes suggestions", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", }), ) }, @@ -1679,8 +1678,34 @@ test("ModelNotFoundError for provider includes suggestions", async () => { await getModel(ProviderID.make("antropic"), ModelID.make("claude-sonnet-4")) // typo: antropic expect(true).toBe(false) // Should not reach here } catch (e: any) { - expect(e.data.suggestions).toBeDefined() - expect(e.data.suggestions).toContain("anthropic") + expect(e.suggestions).toBeDefined() + expect(e.suggestions).toContain("anthropic") + } + }, + }) +}) + +test("ModelNotFoundError suggests catalog models for unloaded providers", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ + $schema: "https://opencode.ai/config.json", + }), + ) + }, + }) + await WithInstance.provide({ + directory: tmp.path, + fn: async () => { + remove("OPENCODE_API_KEY") + try { + await getModel(ProviderID.opencode, ModelID.make("claude-haiku-fake-model")) + throw new Error("expected model lookup to fail") + } catch (e) { + if (!Provider.ModelNotFoundError.isInstance(e)) throw e + expect(e.suggestions).toContain("claude-haiku-4-5") } }, }) @@ -1692,7 +1717,7 @@ test("getProvider returns undefined for nonexistent provider", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", }), ) }, @@ -1712,7 +1737,7 @@ test("getProvider returns provider info", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", }), ) }, @@ -1734,7 +1759,7 @@ test("closest returns undefined when no partial match found", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", }), ) }, @@ -1755,7 +1780,7 @@ test("closest checks multiple query terms in order", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", }), ) }, @@ -1778,7 +1803,7 @@ test("model limit defaults to zero when not specified", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { "no-limit": { name: "No Limit Provider", @@ -1815,7 +1840,7 @@ test("provider options are deeply merged", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { anthropic: { options: { @@ -1844,13 +1869,107 @@ test("provider options are deeply merged", async () => { }) }) +test("hosted nvidia provider adds billing origin header", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ + $schema: "https://opencode.ai/config.json", + provider: { + nvidia: { + options: { + apiKey: "test-api-key", + }, + }, + }, + }), + ) + }, + }) + await WithInstance.provide({ + directory: tmp.path, + fn: async () => { + const providers = await list() + expect(providers[ProviderID.make("nvidia")].options.headers).toEqual({ + "HTTP-Referer": "https://kilo.ai/", + "X-Title": "Kilo Code", + "X-BILLING-INVOKE-ORIGIN": "KiloCode", + }) + }, + }) +}) + +test("custom nvidia baseURL adds billing origin header", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ + $schema: "https://opencode.ai/config.json", + provider: { + nvidia: { + options: { + apiKey: "test-api-key", + baseURL: "http://localhost:8000/v1", + }, + }, + }, + }), + ) + }, + }) + await WithInstance.provide({ + directory: tmp.path, + fn: async () => { + const providers = await list() + expect(providers[ProviderID.make("nvidia")].options.headers).toEqual({ + "HTTP-Referer": "https://kilo.ai/", + "X-Title": "Kilo Code", + "X-BILLING-INVOKE-ORIGIN": "KiloCode", + }) + }, + }) +}) + +test("explicit nvidia billing origin header is preserved", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ + $schema: "https://opencode.ai/config.json", + provider: { + nvidia: { + options: { + apiKey: "test-api-key", + baseURL: "http://localhost:8000/v1", + headers: { + "X-BILLING-INVOKE-ORIGIN": "CustomOrigin", + }, + }, + }, + }, + }), + ) + }, + }) + await WithInstance.provide({ + directory: tmp.path, + fn: async () => { + const providers = await list() + expect(providers[ProviderID.make("nvidia")].options.headers["X-BILLING-INVOKE-ORIGIN"]).toBe("CustomOrigin") + }, + }) +}) + test("custom model inherits npm package from models.dev provider config", async () => { await using tmp = await tmpdir({ init: async (dir) => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { openai: { models: { @@ -1884,7 +2003,7 @@ test("custom model inherits api.url from models.dev provider", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { openrouter: { models: { @@ -2025,7 +2144,7 @@ test("model variants are generated for reasoning models", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", }), ) }, @@ -2050,7 +2169,7 @@ test("model variants can be disabled via config", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { anthropic: { models: { @@ -2086,7 +2205,7 @@ test("model variants can be customized via config", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { anthropic: { models: { @@ -2125,7 +2244,7 @@ test("disabled key is stripped from variant config", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { anthropic: { models: { @@ -2163,7 +2282,7 @@ test("all variants can be disabled via config", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { anthropic: { models: { @@ -2198,7 +2317,7 @@ test("variant config merges with generated variants", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { anthropic: { models: { @@ -2236,7 +2355,7 @@ test("variants filtered in second pass for database models", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { openai: { models: { @@ -2272,7 +2391,7 @@ test("custom model with variants enabled and disabled", async () => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ - $schema: "https://app.kilo.ai/config.json", + $schema: "https://opencode.ai/config.json", provider: { "custom-reasoning": { name: "Custom Reasoning Provider", @@ -2470,13 +2589,11 @@ test("cloudflare-ai-gateway forwards config metadata options", async () => { test("plugin config providers persist after instance dispose", async () => { await using tmp = await tmpdir({ init: async (dir) => { - // kilocode_change start - upstream #24416 avoids real plugin dependency installs const configDir = path.join(dir, ".opencode") const root = path.join(configDir, "plugin") await mkdir(root, { recursive: true }) await markPluginDependenciesReady(configDir) await markPluginDependenciesReady(Global.Path.config) - // kilocode_change end await Bun.write( path.join(root, "demo-provider.ts"), [ @@ -2521,12 +2638,10 @@ test("plugin config providers persist after instance dispose", async () => { expect(first[ProviderID.make("demo")]).toBeDefined() expect(first[ProviderID.make("demo")].models[ModelID.make("chat")]).toBeDefined() - // kilocode_change start await WithInstance.provide({ directory: tmp.path, fn: () => InstanceRuntime.disposeInstance(Instance.current), }) - // kilocode_change end const second = await WithInstance.provide({ directory: tmp.path, @@ -2534,7 +2649,7 @@ test("plugin config providers persist after instance dispose", async () => { }) expect(second[ProviderID.make("demo")]).toBeDefined() expect(second[ProviderID.make("demo")].models[ModelID.make("chat")]).toBeDefined() -}) +}, 60_000) test("plugin config enabled and disabled providers are honored", async () => { await using tmp = await tmpdir({ diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index 5f538f9366f..2bdd3c8df50 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -310,6 +310,75 @@ describe("ProviderTransform.options - gpt-5 textVerbosity", () => { }) }) +describe("ProviderTransform.options - gpt-5 reasoningEffort", () => { + const sessionID = "test-session-123" + + const createModel = (apiId: string) => + ({ + id: `azure/${apiId}`, + providerID: "azure", + api: { + id: apiId, + url: "https://azure.com", + npm: "@ai-sdk/azure", + }, + name: apiId, + capabilities: { + temperature: true, + reasoning: true, + attachment: true, + toolcall: true, + input: { + text: true, + audio: false, + image: true, + video: false, + pdf: false, + }, + output: { + text: true, + audio: false, + image: false, + video: false, + pdf: false, + }, + interleaved: false, + }, + cost: { + input: 0.03, + output: 0.06, + cache: { read: 0.001, write: 0.002 }, + }, + limit: { + context: 128000, + output: 4096, + }, + status: "active", + options: {}, + headers: {}, + }) as any + + test("gpt-5-chat should NOT set reasoningEffort", () => { + const result = ProviderTransform.options({ + model: createModel("gpt-5-chat"), + sessionID, + providerOptions: {}, + }) + + expect(result.reasoningEffort).toBeUndefined() + }) + + test("gpt-5.5 should NOT set reasoningEffort", () => { + const result = ProviderTransform.options({ + model: createModel("gpt-5.5"), + sessionID, + providerOptions: {}, + }) + + expect(result.reasoningEffort).toBeUndefined() + }) +}) + describe("ProviderTransform.options - gateway", () => { const sessionID = "test-session-123" @@ -3882,8 +3951,8 @@ describe("ProviderTransform.variants", () => { }) describe("ProviderTransform.smallOptions - gpt-5 chat/search", () => { - const createModel = (apiId: string) => - ({ + const createModel = (apiId: string) => { + const model = { id: `openai/${apiId}`, providerID: "openai", api: { @@ -3891,13 +3960,43 @@ describe("ProviderTransform.smallOptions - gpt-5 chat/search", () => { url: "https://api.openai.com", npm: "@ai-sdk/openai", }, - }) as any + capabilities: { reasoning: true }, + limit: { output: 64_000 }, + release_date: "2026-01-01", + } as any + model.variants = ProviderTransform.variants(model) + return model + } for (const testCase of [ { id: "gpt-5-chat-latest", options: { store: false } }, - { id: "gpt-5.1-chat-latest", options: { store: false, reasoningEffort: "medium" } }, - { id: "gpt-5.2-chat-latest", options: { store: false, reasoningEffort: "medium" } }, - { id: "gpt-5-search-api", options: { store: false } }, + { + id: "gpt-5.1-chat-latest", + options: { + store: false, + reasoningEffort: "medium", + reasoningSummary: "auto", + include: ["reasoning.encrypted_content"], + }, + }, + { + id: "gpt-5.2-chat-latest", + options: { + store: false, + reasoningEffort: "medium", + reasoningSummary: "auto", + include: ["reasoning.encrypted_content"], + }, + }, + { + id: "gpt-5-search-api", + options: { + store: false, + reasoningEffort: "none", + reasoningSummary: "auto", + include: ["reasoning.encrypted_content"], + }, + }, ]) { test(`${testCase.id} returns only supported small options`, () => { expect(ProviderTransform.smallOptions(createModel(testCase.id))).toEqual(testCase.options) @@ -3906,8 +4005,8 @@ describe("ProviderTransform.smallOptions - gpt-5 chat/search", () => { }) describe("ProviderTransform.smallOptions - google thinking controls", () => { - const createGoogleModel = (apiId: string) => - ({ + const createGoogleModel = (apiId: string) => { + const model = { id: `google/${apiId}`, providerID: "google", api: { @@ -3915,20 +4014,44 @@ describe("ProviderTransform.smallOptions - google thinking controls", () => { url: "https://generativelanguage.googleapis.com", npm: "@ai-sdk/google", }, - }) as any + capabilities: { reasoning: true }, + limit: { output: 64_000 }, + } as any + model.variants = ProviderTransform.variants(model) + return model + } for (const testCase of [ - { id: "gemini-3-pro-preview", options: { thinkingConfig: { thinkingLevel: "low" } } }, - { id: "gemini-3-flash-preview", options: { thinkingConfig: { thinkingLevel: "minimal" } } }, - { id: "gemini-3.1-flash-image-preview", options: { thinkingConfig: { thinkingLevel: "minimal" } } }, - { id: "gemini-3-pro-image-preview", options: { thinkingConfig: { thinkingLevel: "high" } } }, - { id: "gemini-2.5-pro", options: { thinkingConfig: { thinkingBudget: 128 } } }, - { id: "gemini-2.5-flash", options: { thinkingConfig: { thinkingBudget: 0 } } }, + { id: "gemini-3-pro-preview", options: { thinkingConfig: { includeThoughts: true, thinkingLevel: "low" } } }, + { id: "gemini-3-flash-preview", options: { thinkingConfig: { includeThoughts: true, thinkingLevel: "minimal" } } }, + { + id: "gemini-3.1-flash-image-preview", + options: { thinkingConfig: { includeThoughts: true, thinkingLevel: "minimal" } }, + }, + { id: "gemini-3-pro-image-preview", options: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } } }, + { id: "gemini-2.5-pro", options: { thinkingConfig: { includeThoughts: true, thinkingBudget: 16000 } } }, + { id: "gemini-2.5-flash", options: { thinkingConfig: { includeThoughts: true, thinkingBudget: 16000 } } }, ]) { test(`${testCase.id} returns supported small thinking options`, () => { expect(ProviderTransform.smallOptions(createGoogleModel(testCase.id))).toEqual(testCase.options) }) } + + test("uses the first configured variant when available", () => { + expect( + ProviderTransform.smallOptions({ + ...createGoogleModel("gemini-2.5-pro"), + variants: { + high: { thinkingConfig: { includeThoughts: true, thinkingBudget: 16000 } }, + max: { thinkingConfig: { includeThoughts: true, thinkingBudget: 32768 } }, + }, + }), + ).toEqual({ thinkingConfig: { includeThoughts: true, thinkingBudget: 16000 } }) + }) + + test("does not synthesize thinking options when variants are empty", () => { + expect(ProviderTransform.smallOptions({ ...createGoogleModel("gemini-2.5-pro"), variants: {} })).toEqual({}) + }) }) describe("ProviderTransform.providerOptions - ai-gateway-provider", () => { diff --git a/packages/opencode/test/pty/pty-output-isolation.test.ts b/packages/opencode/test/pty/pty-output-isolation.test.ts index cf0b7fa5e55..0fa710f02ab 100644 --- a/packages/opencode/test/pty/pty-output-isolation.test.ts +++ b/packages/opencode/test/pty/pty-output-isolation.test.ts @@ -1,148 +1,162 @@ -import { describe, expect, test } from "bun:test" -import { AppRuntime } from "../../src/effect/app-runtime" -import { Effect } from "effect" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" +import { describe, expect } from "bun:test" +import { Bus } from "../../src/bus" +import { Config } from "../../src/config/config" +import { Plugin } from "../../src/plugin" import { Pty } from "../../src/pty" -import { tmpdir } from "../fixture/fixture" -import { setTimeout as sleep } from "node:timers/promises" +import { Duration, Effect, Layer, Queue } from "effect" +import { testEffect } from "../lib/effect" + +type Socket = Parameters[1] + +const it = testEffect( + Pty.layer.pipe( + Layer.provideMerge(Bus.layer), + Layer.provideMerge(Config.defaultLayer), + Layer.provideMerge(Plugin.defaultLayer), + ), +) +const ptyTest = process.platform === "win32" ? it.instance.skip : it.instance + +const createPty = Effect.fn("PtyOutputIsolationTest.createPty")(function* (input: Pty.CreateInput) { + const pty = yield* Pty.Service + return yield* Effect.acquireRelease(pty.create(input), (info) => pty.remove(info.id).pipe(Effect.ignore)) +}) + +const decodeOutput = (data: string | Uint8Array | ArrayBuffer) => + typeof data === "string" + ? data + : Buffer.from(data instanceof Uint8Array ? data : new Uint8Array(data)).toString("utf8") + +const makeSocket = Effect.fn("PtyOutputIsolationTest.makeSocket")(function* (data: unknown) { + const output = yield* Queue.unbounded() + const chunks: string[] = [] + const socket: Socket = { + readyState: 1, + data, + send: (data) => { + const text = decodeOutput(data) + chunks.push(text) + Queue.offerUnsafe(output, text) + }, + close: () => { + // no-op (simulate abrupt drop) + }, + } + + return { socket, output, chunks } +}) + +const waitForOutput = (output: Queue.Queue, text: string, duration: Duration.Input = "5 seconds") => + Effect.gen(function* () { + let received = "" + while (!received.includes(text)) { + received += yield* Queue.take(output) + } + return received + }).pipe( + Effect.timeoutOrElse({ + duration, + orElse: () => Effect.fail(new Error(`timeout waiting for output containing ${JSON.stringify(text)}`)), + }), + ) + +const waitForLeakedOutput = (output: Queue.Queue, text: string) => + Effect.gen(function* () { + let received = "" + while (!received.includes(text)) { + received += yield* Queue.take(output) + } + return received + }).pipe( + Effect.timeoutOrElse({ + duration: "100 millis", + orElse: () => Effect.succeed(undefined), + }), + ) describe("pty", () => { - test("does not leak output when websocket objects are reused", async () => { - await using dir = await tmpdir({ git: true }) - - await WithInstance.provide({ - directory: dir.path, - fn: () => - AppRuntime.runPromise( - Effect.gen(function* () { - const pty = yield* Pty.Service - const a = yield* pty.create({ command: "cat", title: "a" }) - const b = yield* pty.create({ command: "cat", title: "b" }) - try { - const outA: string[] = [] - const outB: string[] = [] - - const ws = { - readyState: 1, - data: { events: { connection: "a" } }, - send: (data: unknown) => { - outA.push(typeof data === "string" ? data : Buffer.from(data as Uint8Array).toString("utf8")) - }, - close: () => { - // no-op (simulate abrupt drop) - }, - } - - yield* pty.connect(a.id, ws as any) - - ws.data = { events: { connection: "b" } } - ws.send = (data: unknown) => { - outB.push(typeof data === "string" ? data : Buffer.from(data as Uint8Array).toString("utf8")) - } - yield* pty.connect(b.id, ws as any) - - outA.length = 0 - outB.length = 0 - - yield* pty.write(a.id, "AAA\n") - yield* Effect.promise(() => sleep(100)) - - expect(outB.join("")).not.toContain("AAA") - } finally { - yield* pty.remove(a.id) - yield* pty.remove(b.id) - } - }), - ), - }) - }) - - test("does not leak output when Bun recycles websocket objects before re-connect", async () => { - await using dir = await tmpdir({ git: true }) - - await WithInstance.provide({ - directory: dir.path, - fn: () => - AppRuntime.runPromise( - Effect.gen(function* () { - const pty = yield* Pty.Service - const a = yield* pty.create({ command: "cat", title: "a" }) - try { - const outA: string[] = [] - const outB: string[] = [] - - const ws = { - readyState: 1, - data: { events: { connection: "a" } }, - send: (data: unknown) => { - outA.push(typeof data === "string" ? data : Buffer.from(data as Uint8Array).toString("utf8")) - }, - close: () => { - // no-op (simulate abrupt drop) - }, - } - - yield* pty.connect(a.id, ws as any) - outA.length = 0 - - ws.data = { events: { connection: "b" } } - ws.send = (data: unknown) => { - outB.push(typeof data === "string" ? data : Buffer.from(data as Uint8Array).toString("utf8")) - } - - yield* pty.write(a.id, "AAA\n") - yield* Effect.promise(() => sleep(100)) - - expect(outB.join("")).not.toContain("AAA") - } finally { - yield* pty.remove(a.id) - } - }), - ), - }) - }) - - test("treats in-place socket data mutation as the same connection", async () => { - if (process.platform === "win32") return // kilocode_change - ConPTY does not echo writes the way Unix PTYs do, so the positive assertion below is unreachable on Windows - await using dir = await tmpdir({ git: true }) - - await WithInstance.provide({ - directory: dir.path, - fn: () => - AppRuntime.runPromise( - Effect.gen(function* () { - const pty = yield* Pty.Service - const a = yield* pty.create({ command: "cat", title: "a" }) - try { - const out: string[] = [] - - const ctx = { connId: 1 } - const ws = { - readyState: 1, - data: ctx, - send: (data: unknown) => { - out.push(typeof data === "string" ? data : Buffer.from(data as Uint8Array).toString("utf8")) - }, - close: () => { - // no-op - }, - } - - yield* pty.connect(a.id, ws as any) - out.length = 0 - - ctx.connId = 2 - - yield* pty.write(a.id, "AAA\n") - yield* Effect.promise(() => sleep(100)) - - expect(out.join("")).toContain("AAA") - } finally { - yield* pty.remove(a.id) - } - }), - ), - }) - }) + ptyTest( + "does not leak output when websocket objects are reused", + () => + Effect.gen(function* () { + const pty = yield* Pty.Service + const a = yield* createPty({ command: "cat", title: "a" }) + const b = yield* createPty({ command: "cat", title: "b" }) + const connectionA = yield* makeSocket({ events: { connection: "a" } }) + const connectionB = { events: { connection: "b" } } + + yield* pty.connect(a.id, connectionA.socket) + + const outBQueue = yield* Queue.unbounded() + const outB: string[] = [] + connectionA.socket.data = connectionB + connectionA.socket.send = (data) => { + const text = decodeOutput(data) + outB.push(text) + Queue.offerUnsafe(outBQueue, text) + } + yield* pty.connect(b.id, connectionA.socket) + + connectionA.chunks.length = 0 + outB.length = 0 + + yield* pty.write(a.id, "AAA\n") + const verifyA = yield* makeSocket({ events: { connection: "verify-a" } }) + yield* pty.connect(a.id, verifyA.socket) + yield* waitForOutput(verifyA.output, "AAA") + + expect(outB.join("")).not.toContain("AAA") + expect(yield* waitForLeakedOutput(outBQueue, "AAA")).toBeUndefined() + }), + { git: true }, + ) + + ptyTest( + "does not leak output when Bun recycles websocket objects before re-connect", + () => + Effect.gen(function* () { + const pty = yield* Pty.Service + const a = yield* createPty({ command: "cat", title: "a" }) + const outA = yield* makeSocket({ events: { connection: "a" } }) + const outB = yield* Queue.unbounded() + + yield* pty.connect(a.id, outA.socket) + outA.chunks.length = 0 + + const connectionB = { events: { connection: "b" } } + outA.socket.data = connectionB + outA.socket.send = (data) => { + Queue.offerUnsafe(outB, decodeOutput(data)) + } + + yield* pty.write(a.id, "AAA\n") + const verifyA = yield* makeSocket({ events: { connection: "verify-a" } }) + yield* pty.connect(a.id, verifyA.socket) + yield* waitForOutput(verifyA.output, "AAA") + + expect(yield* waitForLeakedOutput(outB, "AAA")).toBeUndefined() + }), + { git: true }, + ) + + ptyTest( + "treats in-place socket data mutation as the same connection", + () => + Effect.gen(function* () { + const pty = yield* Pty.Service + const a = yield* createPty({ command: "cat", title: "a" }) + const ctx = { connId: 1 } + const out = yield* makeSocket(ctx) + + yield* pty.connect(a.id, out.socket) + out.chunks.length = 0 + + ctx.connId = 2 + + yield* pty.write(a.id, "AAA\n") + + expect(yield* waitForOutput(out.output, "AAA")).toContain("AAA") + }), + { git: true }, + ) }) diff --git a/packages/opencode/test/pty/pty-session.test.ts b/packages/opencode/test/pty/pty-session.test.ts index 8c5d804b730..12784baf315 100644 --- a/packages/opencode/test/pty/pty-session.test.ts +++ b/packages/opencode/test/pty/pty-session.test.ts @@ -1,103 +1,100 @@ -import { describe, expect, test } from "bun:test" -import { AppRuntime } from "../../src/effect/app-runtime" +import { describe, expect } from "bun:test" import { Bus } from "../../src/bus" -import { Effect } from "effect" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" +import { Config } from "../../src/config/config" +import { Plugin } from "../../src/plugin" import { Pty } from "../../src/pty" import type { PtyID } from "../../src/pty/schema" -import { tmpdir } from "../fixture/fixture" -import { setTimeout as sleep } from "node:timers/promises" +import { Effect, Layer, Queue } from "effect" +import { testEffect } from "../lib/effect" -const wait = async (fn: () => boolean, ms = 5000) => { - const end = Date.now() + ms - while (Date.now() < end) { - if (fn()) return - await sleep(25) - } - throw new Error("timeout waiting for pty events") -} - -const pick = (log: Array<{ type: "created" | "exited" | "deleted"; id: PtyID }>, id: PtyID) => { - return log.filter((evt) => evt.id === id).map((evt) => evt.type) -} - -describe("pty", () => { - test("publishes created, exited, deleted in order for a short-lived process", async () => { - if (process.platform === "win32") return +type PtyEvent = { type: "created" | "exited" | "deleted"; id: PtyID } - await using dir = await tmpdir({ git: true }) +const it = testEffect( + Pty.layer.pipe( + Layer.provideMerge(Bus.layer), + Layer.provideMerge(Config.defaultLayer), + Layer.provideMerge(Plugin.defaultLayer), + ), +) +const ptyTest = process.platform === "win32" ? it.instance.skip : it.instance - await WithInstance.provide({ - directory: dir.path, - fn: () => - AppRuntime.runPromise( - Effect.gen(function* () { - const pty = yield* Pty.Service - const log: Array<{ type: "created" | "exited" | "deleted"; id: PtyID }> = [] - const off = [ - Bus.subscribe(Pty.Event.Created, (evt) => log.push({ type: "created", id: evt.properties.info.id })), - Bus.subscribe(Pty.Event.Exited, (evt) => log.push({ type: "exited", id: evt.properties.id })), - Bus.subscribe(Pty.Event.Deleted, (evt) => log.push({ type: "deleted", id: evt.properties.id })), - ] +const subscribePtyEvents = Effect.fn("PtySessionTest.subscribePtyEvents")(function* () { + const bus = yield* Bus.Service + const events = yield* Queue.unbounded() - let id: PtyID | undefined - try { - const info = yield* pty.create({ - command: "/usr/bin/env", - args: ["sh", "-c", "sleep 0.1"], - title: "sleep", - }) - id = info.id + const subscribe = (effect: Effect.Effect<() => void, never, A>) => + Effect.acquireRelease(effect, (off) => Effect.sync(off)) - yield* Effect.promise(() => wait(() => pick(log, id!).includes("exited"))) + yield* subscribe( + bus.subscribeCallback(Pty.Event.Created, (evt) => { + Queue.offerUnsafe(events, { type: "created", id: evt.properties.info.id }) + }), + ) + yield* subscribe( + bus.subscribeCallback(Pty.Event.Exited, (evt) => { + Queue.offerUnsafe(events, { type: "exited", id: evt.properties.id }) + }), + ) + yield* subscribe( + bus.subscribeCallback(Pty.Event.Deleted, (evt) => { + Queue.offerUnsafe(events, { type: "deleted", id: evt.properties.id }) + }), + ) - yield* pty.remove(id) - yield* Effect.promise(() => wait(() => pick(log, id!).length >= 3)) - expect(pick(log, id!)).toEqual(["created", "exited", "deleted"]) - } finally { - off.forEach((x) => x()) - if (id) yield* pty.remove(id) - } - }), - ), - }) - }) + return events +}) - test("publishes created, exited, deleted in order for /bin/sh + remove", async () => { - if (process.platform === "win32") return +const createPty = Effect.fn("PtySessionTest.createPty")(function* (input: Pty.CreateInput) { + const pty = yield* Pty.Service + return yield* Effect.acquireRelease(pty.create(input), (info) => pty.remove(info.id).pipe(Effect.ignore)) +}) - await using dir = await tmpdir({ git: true }) +const waitForEvents = (events: Queue.Queue, id: PtyID, count: number) => { + return Effect.gen(function* () { + const picked: Array = [] + while (picked.length < count) { + const evt = yield* Queue.take(events) + if (evt.id === id) picked.push(evt.type) + } + return picked + }).pipe( + Effect.timeoutOrElse({ + duration: "5 seconds", + orElse: () => Effect.fail(new Error("timeout waiting for pty events")), + }), + ) +} - await WithInstance.provide({ - directory: dir.path, - fn: () => - AppRuntime.runPromise( - Effect.gen(function* () { - const pty = yield* Pty.Service - const log: Array<{ type: "created" | "exited" | "deleted"; id: PtyID }> = [] - const off = [ - Bus.subscribe(Pty.Event.Created, (evt) => log.push({ type: "created", id: evt.properties.info.id })), - Bus.subscribe(Pty.Event.Exited, (evt) => log.push({ type: "exited", id: evt.properties.id })), - Bus.subscribe(Pty.Event.Deleted, (evt) => log.push({ type: "deleted", id: evt.properties.id })), - ] +describe("pty", () => { + ptyTest( + "publishes created, exited, deleted in order for a short-lived process", + () => + Effect.gen(function* () { + const events = yield* subscribePtyEvents() + const info = yield* createPty({ + command: "/usr/bin/env", + args: ["sh", "-c", "sleep 0.1"], + title: "sleep", + }) - let id: PtyID | undefined - try { - const info = yield* pty.create({ command: "/bin/sh", title: "sh" }) - id = info.id + expect(yield* waitForEvents(events, info.id, 3)).toEqual(["created", "exited", "deleted"]) + }), + { git: true }, + ) - yield* Effect.promise(() => sleep(100)) + ptyTest( + "publishes created, exited, deleted in order for /bin/sh + remove", + () => + Effect.gen(function* () { + const pty = yield* Pty.Service + const events = yield* subscribePtyEvents() + const info = yield* createPty({ command: "/bin/sh", title: "sh" }) - yield* pty.remove(id) - yield* Effect.promise(() => wait(() => pick(log, id!).length >= 3)) - expect(pick(log, id!)).toEqual(["created", "exited", "deleted"]) - } finally { - off.forEach((x) => x()) - if (id) yield* pty.remove(id) - } - }), - ), - }) - }) + expect(yield* waitForEvents(events, info.id, 1)).toEqual(["created"]) + yield* pty.write(info.id, "exit\n") + expect(yield* waitForEvents(events, info.id, 2)).toEqual(["exited", "deleted"]) + yield* pty.remove(info.id) + }), + { git: true }, + ) }) diff --git a/packages/opencode/test/pty/pty-shell.test.ts b/packages/opencode/test/pty/pty-shell.test.ts index 00e965d25ed..e8132dec767 100644 --- a/packages/opencode/test/pty/pty-shell.test.ts +++ b/packages/opencode/test/pty/pty-shell.test.ts @@ -1,39 +1,35 @@ -import { describe, expect, test } from "bun:test" -import { AppRuntime } from "../../src/effect/app-runtime" +import { describe, expect } from "bun:test" import { Effect } from "effect" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" import { Pty } from "../../src/pty" import { Shell } from "../../src/shell/shell" -import { tmpdir } from "../fixture/fixture" +import { testEffect } from "../lib/effect" Shell.preferred.reset() +const it = testEffect(Pty.defaultLayer) + +const createPty = (input: Pty.CreateInput) => + Effect.acquireRelease( + Effect.gen(function* () { + const pty = yield* Pty.Service + const info = yield* pty.create(input) + return { pty, info } + }), + ({ pty, info }) => pty.remove(info.id).pipe(Effect.ignore), + ).pipe(Effect.map(({ info }) => info)) + describe("pty shell args", () => { if (process.platform !== "win32") return const ps = Bun.which("pwsh") || Bun.which("powershell") if (ps) { - test( + it.instance( "does not add login args to pwsh", - async () => { - await using dir = await tmpdir() - await WithInstance.provide({ - directory: dir.path, - fn: () => - AppRuntime.runPromise( - Effect.gen(function* () { - const pty = yield* Pty.Service - const info = yield* pty.create({ command: ps, title: "pwsh" }) - try { - expect(info.args).toEqual([]) - } finally { - yield* pty.remove(info.id) - } - }), - ), - }) - }, + () => + Effect.gen(function* () { + const info = yield* createPty({ command: ps, title: "pwsh" }) + expect(info.args).toEqual([]) + }), { timeout: 30000 }, ) } @@ -44,62 +40,36 @@ describe("pty shell args", () => { return Shell.gitbash() })() if (bash) { - test( + it.instance( "adds login args to bash", - async () => { - await using dir = await tmpdir() - await WithInstance.provide({ - directory: dir.path, - fn: () => - AppRuntime.runPromise( - Effect.gen(function* () { - const pty = yield* Pty.Service - const info = yield* pty.create({ command: bash, title: "bash" }) - try { - expect(info.args).toEqual(["-l"]) - } finally { - yield* pty.remove(info.id) - } - }), - ), - }) - }, + () => + Effect.gen(function* () { + const info = yield* createPty({ command: bash, title: "bash" }) + expect(info.args).toEqual(["-l"]) + }), { timeout: 30000 }, ) } }) describe("pty configured shell", () => { - test( + const configured = process.platform === "win32" ? Bun.which("pwsh") || Bun.which("powershell") : Bun.which("bash") + + it.instance( "uses configured shell for default PTY command", - async () => { - const configured = process.platform === "win32" ? Bun.which("pwsh") || Bun.which("powershell") : Bun.which("bash") - if (!configured) return + () => + Effect.gen(function* () { + if (!configured) return - await using dir = await tmpdir({ - config: { shell: Shell.name(configured) }, - }) - await WithInstance.provide({ - directory: dir.path, - fn: () => - AppRuntime.runPromise( - Effect.gen(function* () { - const pty = yield* Pty.Service - const info = yield* pty.create({ title: "configured" }) - try { - if (process.platform === "win32") { - expect(info.command.toLowerCase()).toBe(configured.toLowerCase()) - } else { - expect(info.command).toBe(configured) - } - expect(info.args).toEqual(process.platform === "win32" ? [] : ["-l"]) - } finally { - yield* pty.remove(info.id) - } - }), - ), - }) - }, + const info = yield* createPty({ title: "configured" }) + if (process.platform === "win32") { + expect(info.command.toLowerCase()).toBe(configured.toLowerCase()) + } else { + expect(info.command).toBe(configured) + } + expect(info.args).toEqual(process.platform === "win32" ? [] : ["-l"]) + }), + configured ? { config: { shell: Shell.name(configured) } } : undefined, { timeout: 30000 }, ) }) diff --git a/packages/opencode/test/question/question.test.ts b/packages/opencode/test/question/question.test.ts index 67bf62e5ddb..37a1d534f5c 100644 --- a/packages/opencode/test/question/question.test.ts +++ b/packages/opencode/test/question/question.test.ts @@ -1,16 +1,18 @@ import { afterEach, expect } from "bun:test" // kilocode_change - blocking behavior now uses the scoped service test helper -import { Cause, Effect, Exit, Fiber, Layer } from "effect" +import { Cause, Effect, Exit, Fiber, Layer, Queue } from "effect" import { Question } from "../../src/question" import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" import { InstanceRuntime } from "../../src/project/instance-runtime" import { QuestionID } from "../../src/question/schema" import { disposeAllInstances, provideInstance, reloadTestInstance, tmpdirScoped } from "../fixture/fixture" // kilocode_change - blocking coverage no longer uses the Promise facade fixture import { SessionID } from "../../src/session/schema" import { testEffect } from "../lib/effect" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" +import { Bus } from "../../src/bus" -const it = testEffect(Layer.mergeAll(Question.defaultLayer, CrossSpawnSpawner.defaultLayer)) +const it = testEffect( + Layer.mergeAll(Question.layer.pipe(Layer.provideMerge(Bus.layer)), CrossSpawnSpawner.defaultLayer), +) const askEffect = Effect.fn("QuestionTest.ask")(function* (input: { sessionID: SessionID @@ -46,15 +48,19 @@ const rejectAll = Effect.gen(function* () { yield* Effect.forEach(yield* listEffect, (req) => rejectEffect(req.id), { discard: true }) }) -const waitForPending = (count: number) => - Effect.gen(function* () { - for (let i = 0; i < 100; i++) { - const pending = yield* listEffect - if (pending.length === count) return pending - yield* Effect.sleep("10 millis") - } - return yield* Effect.fail(new Error(`timed out waiting for ${count} pending question request(s)`)) - }) +const waitForPending = Effect.fn("QuestionTest.waitForPending")(function* (count: number) { + const question = yield* Question.Service + const bus = yield* Bus.Service + const asked = yield* Queue.unbounded() + const off = yield* bus.subscribeCallback(Question.Event.Asked, () => Queue.offerUnsafe(asked, undefined)) + yield* Effect.addFinalizer(() => Effect.sync(off)) + + for (;;) { + const pending = yield* question.list() + if (pending.length === count) return pending + yield* Queue.take(asked).pipe(Effect.timeout("2 seconds")) + } +}) it.instance( "ask - remains pending until answered", @@ -448,9 +454,8 @@ it.live("pending question rejects on instance dispose", () => }).pipe(provideInstance(dir), Effect.forkScoped) expect(yield* waitForPending(1).pipe(provideInstance(dir))).toHaveLength(1) - yield* Effect.promise(() => - WithInstance.provide({ directory: dir, fn: () => InstanceRuntime.disposeInstance(Instance.current) }), - ) + const ctx = yield* Effect.sync(() => Instance.current).pipe(provideInstance(dir)) + yield* Effect.promise(() => InstanceRuntime.disposeInstance(ctx)) const exit = yield* Fiber.await(fiber) expect(Exit.isFailure(exit)).toBe(true) diff --git a/packages/opencode/test/reference/reference.test.ts b/packages/opencode/test/reference/reference.test.ts index 43427e4e66c..1c72ba4af92 100644 --- a/packages/opencode/test/reference/reference.test.ts +++ b/packages/opencode/test/reference/reference.test.ts @@ -3,8 +3,9 @@ import path from "path" import { Effect, Layer } from "effect" import { AppFileSystem } from "@opencode-ai/core/filesystem" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" -import { Flag } from "@opencode-ai/core/flag/flag" import { Global } from "@opencode-ai/core/global" +import { Config } from "../../src/config/config" +import { RuntimeFlags } from "../../src/effect/runtime-flags" import { Git } from "../../src/git" import { Reference } from "../../src/reference/reference" import { disposeAllInstances, provideTmpdirInstance, tmpdirScoped } from "../fixture/fixture" @@ -14,23 +15,25 @@ afterEach(async () => { await disposeAllInstances() }) +const referenceLayer = (flags: Partial = {}) => + Reference.layer.pipe( + Layer.provide(Config.defaultLayer), + Layer.provide(AppFileSystem.defaultLayer), + Layer.provide(Git.defaultLayer), + Layer.provide(RuntimeFlags.layer(flags)), + ) + const it = testEffect( - Layer.mergeAll(AppFileSystem.defaultLayer, CrossSpawnSpawner.defaultLayer, Git.defaultLayer, Reference.defaultLayer), + Layer.mergeAll(AppFileSystem.defaultLayer, CrossSpawnSpawner.defaultLayer, Git.defaultLayer, referenceLayer()), +) +const scout = testEffect( + Layer.mergeAll( + AppFileSystem.defaultLayer, + CrossSpawnSpawner.defaultLayer, + Git.defaultLayer, + referenceLayer({ experimentalScout: true }), + ), ) - -const experimentalScout = (self: Effect.Effect) => - Effect.acquireUseRelease( - Effect.sync(() => { - const previous = Flag.KILO_EXPERIMENTAL_SCOUT - Flag.KILO_EXPERIMENTAL_SCOUT = true - return previous - }), - () => self, - (previous) => - Effect.sync(() => { - Flag.KILO_EXPERIMENTAL_SCOUT = previous - }), - ) const githubBase = (url: string, self: Effect.Effect) => Effect.acquireUseRelease( @@ -127,118 +130,114 @@ describe("reference", () => { }), ) - it.live("materializes configured git references during init", () => - experimentalScout( - provideTmpdirInstance( - (_dir) => - Effect.gen(function* () { - const fs = yield* AppFileSystem.Service - const cache = path.join(Global.Path.repos, "github.com", "opencode-reference-test", "repo") - yield* fs.remove(cache, { recursive: true }).pipe(Effect.ignore) - yield* Effect.addFinalizer(() => fs.remove(cache, { recursive: true }).pipe(Effect.ignore)) - - const source = yield* tmpdirScoped({ git: true }) - const remoteRoot = yield* tmpdirScoped() - const remoteDir = path.join(remoteRoot, "opencode-reference-test") - const remoteRepo = path.join(remoteDir, "repo.git") - - yield* Effect.promise(() => Bun.write(path.join(source, "README.md"), "configured\n")) - yield* git(source, ["add", "."]) - yield* git(source, ["commit", "-m", "add readme"]) - yield* fs.makeDirectory(remoteDir, { recursive: true }).pipe(Effect.orDie) - yield* git(remoteRoot, ["clone", "--bare", source, remoteRepo]) - - const reference = yield* Reference.Service - yield* githubBase( - `file://${remoteRoot}/`, - Effect.gen(function* () { - yield* reference.init() - yield* waitForContent(fs, path.join(cache, "README.md"), "configured\n") - }), - ) - - expect(yield* fs.existsSafe(path.join(cache, ".git"))).toBe(true) - expect(yield* fs.readFileString(path.join(cache, "README.md"))).toBe("configured\n") - - const resolved = yield* reference.get("docs") - expect(resolved?.kind).toBe("git") - if (resolved?.kind === "git") expect(resolved.path).toBe(cache) - }), - { - config: { - reference: { - docs: "opencode-reference-test/repo", - }, + scout.live("materializes configured git references during init", () => + provideTmpdirInstance( + (_dir) => + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const cache = path.join(Global.Path.repos, "github.com", "opencode-reference-test", "repo") + yield* fs.remove(cache, { recursive: true }).pipe(Effect.ignore) + yield* Effect.addFinalizer(() => fs.remove(cache, { recursive: true }).pipe(Effect.ignore)) + + const source = yield* tmpdirScoped({ git: true }) + const remoteRoot = yield* tmpdirScoped() + const remoteDir = path.join(remoteRoot, "opencode-reference-test") + const remoteRepo = path.join(remoteDir, "repo.git") + + yield* Effect.promise(() => Bun.write(path.join(source, "README.md"), "configured\n")) + yield* git(source, ["add", "."]) + yield* git(source, ["commit", "-m", "add readme"]) + yield* fs.makeDirectory(remoteDir, { recursive: true }).pipe(Effect.orDie) + yield* git(remoteRoot, ["clone", "--bare", source, remoteRepo]) + + const reference = yield* Reference.Service + yield* githubBase( + `file://${remoteRoot}/`, + Effect.gen(function* () { + yield* reference.init() + yield* waitForContent(fs, path.join(cache, "README.md"), "configured\n") + }), + ) + + expect(yield* fs.existsSafe(path.join(cache, ".git"))).toBe(true) + expect(yield* fs.readFileString(path.join(cache, "README.md"))).toBe("configured\n") + + const resolved = yield* reference.get("docs") + expect(resolved?.kind).toBe("git") + if (resolved?.kind === "git") expect(resolved.path).toBe(cache) + }), + { + config: { + reference: { + docs: "opencode-reference-test/repo", }, }, - ), + }, ), ) - it.live("refreshes configured git references on new instance init", () => - experimentalScout( - Effect.gen(function* () { - const fs = yield* AppFileSystem.Service - const cache = path.join(Global.Path.repos, "github.com", "opencode-reference-refresh", "repo") - yield* fs.remove(cache, { recursive: true }).pipe(Effect.ignore) - yield* Effect.addFinalizer(() => fs.remove(cache, { recursive: true }).pipe(Effect.ignore)) - - const source = yield* tmpdirScoped({ git: true }) - const remoteRoot = yield* tmpdirScoped() - const remoteDir = path.join(remoteRoot, "opencode-reference-refresh") - const remoteRepo = path.join(remoteDir, "repo.git") - - yield* Effect.promise(() => Bun.write(path.join(source, "README.md"), "v1\n")) - yield* git(source, ["add", "."]) - yield* git(source, ["commit", "-m", "add readme"]) - yield* fs.makeDirectory(remoteDir, { recursive: true }).pipe(Effect.orDie) - yield* git(remoteRoot, ["clone", "--bare", source, remoteRepo]) - - yield* githubBase( - `file://${remoteRoot}/`, - provideTmpdirInstance( - (_dir) => - Effect.gen(function* () { - const reference = yield* Reference.Service - yield* reference.init() - yield* waitForContent(fs, path.join(cache, "README.md"), "v1\n") - }), - { - config: { - reference: { - docs: "opencode-reference-refresh/repo", - }, + scout.live("refreshes configured git references on new instance init", () => + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const cache = path.join(Global.Path.repos, "github.com", "opencode-reference-refresh", "repo") + yield* fs.remove(cache, { recursive: true }).pipe(Effect.ignore) + yield* Effect.addFinalizer(() => fs.remove(cache, { recursive: true }).pipe(Effect.ignore)) + + const source = yield* tmpdirScoped({ git: true }) + const remoteRoot = yield* tmpdirScoped() + const remoteDir = path.join(remoteRoot, "opencode-reference-refresh") + const remoteRepo = path.join(remoteDir, "repo.git") + + yield* Effect.promise(() => Bun.write(path.join(source, "README.md"), "v1\n")) + yield* git(source, ["add", "."]) + yield* git(source, ["commit", "-m", "add readme"]) + yield* fs.makeDirectory(remoteDir, { recursive: true }).pipe(Effect.orDie) + yield* git(remoteRoot, ["clone", "--bare", source, remoteRepo]) + + yield* githubBase( + `file://${remoteRoot}/`, + provideTmpdirInstance( + (_dir) => + Effect.gen(function* () { + const reference = yield* Reference.Service + yield* reference.init() + yield* waitForContent(fs, path.join(cache, "README.md"), "v1\n") + }), + { + config: { + reference: { + docs: "opencode-reference-refresh/repo", }, }, - ), - ) - - const branch = yield* git(source, ["branch", "--show-current"]) - yield* git(source, ["remote", "add", "origin", remoteRepo]) - yield* Effect.promise(() => Bun.write(path.join(source, "README.md"), "v2\n")) - yield* git(source, ["add", "."]) - yield* git(source, ["commit", "-m", "update readme"]) - yield* git(source, ["push", "origin", `${branch}:${branch}`]) - - yield* githubBase( - `file://${remoteRoot}/`, - provideTmpdirInstance( - (_dir) => - Effect.gen(function* () { - const reference = yield* Reference.Service - yield* reference.init() - yield* waitForContent(fs, path.join(cache, "README.md"), "v2\n") - }), - { - config: { - reference: { - docs: "opencode-reference-refresh/repo", - }, + }, + ), + ) + + const branch = yield* git(source, ["branch", "--show-current"]) + yield* git(source, ["remote", "add", "origin", remoteRepo]) + yield* Effect.promise(() => Bun.write(path.join(source, "README.md"), "v2\n")) + yield* git(source, ["add", "."]) + yield* git(source, ["commit", "-m", "update readme"]) + yield* git(source, ["push", "origin", `${branch}:${branch}`]) + + yield* githubBase( + `file://${remoteRoot}/`, + provideTmpdirInstance( + (_dir) => + Effect.gen(function* () { + const reference = yield* Reference.Service + yield* reference.init() + yield* waitForContent(fs, path.join(cache, "README.md"), "v2\n") + }), + { + config: { + reference: { + docs: "opencode-reference-refresh/repo", }, }, - ), - ) - }), - ), + }, + ), + ) + }), ) }) diff --git a/packages/opencode/test/server/global-bus.ts b/packages/opencode/test/server/global-bus.ts index c8d0f92191f..24e5cf77b5a 100644 --- a/packages/opencode/test/server/global-bus.ts +++ b/packages/opencode/test/server/global-bus.ts @@ -29,6 +29,3 @@ export function waitGlobalBusEvent(input: { ), ) } - -export const waitGlobalBusEventPromise = (input: Parameters[0]) => - Effect.runPromise(waitGlobalBusEvent(input)) diff --git a/packages/opencode/test/server/global-session-list.test.ts b/packages/opencode/test/server/global-session-list.test.ts index 1aca6f90efd..0fdba1f6639 100644 --- a/packages/opencode/test/server/global-session-list.test.ts +++ b/packages/opencode/test/server/global-session-list.test.ts @@ -1,164 +1,104 @@ -import { $ } from "bun" -import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from "bun:test" -import { Effect } from "effect" -import path from "path" -import z from "zod" -import { WithInstance } from "../../src/project/with-instance" +import { describe, expect } from "bun:test" +import { Deferred, Effect, Layer } from "effect" import { Project } from "@/project/project" import { Session as SessionNs } from "@/session/session" +import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import * as Log from "@opencode-ai/core/util/log" -import { resetDatabase } from "../fixture/db" -import { tmpdir } from "../fixture/fixture" -import { RemoteSender } from "../../src/kilo-sessions/remote-sender" // kilocode_change - -// kilocode_change start -beforeEach(() => { - spyOn(RemoteSender, "create").mockReturnValue({ handle() {}, dispose() {} }) -}) -// kilocode_change end +import { provideInstance, TestInstance, tmpdirScoped } from "../fixture/fixture" +import { testEffect } from "../lib/effect" void Log.init({ print: false }) -// kilocode_change start -afterEach(async () => { - mock.restore() - await resetDatabase() -}) -// kilocode_change end - -function run(fx: Effect.Effect) { - return Effect.runPromise(fx.pipe(Effect.provide(SessionNs.defaultLayer))) -} - -const svc = { - ...SessionNs, - create(input?: SessionNs.CreateInput) { - return run(SessionNs.Service.use((svc) => svc.create(input))) - }, - setArchived(input: z.output) { - return run(SessionNs.Service.use((svc) => svc.setArchived(input))) - }, -} +const it = testEffect(Layer.mergeAll(SessionNs.defaultLayer, Project.defaultLayer, CrossSpawnSpawner.defaultLayer)) + +const withSession = (input?: Parameters[0]) => + Effect.acquireRelease( + SessionNs.Service.use((session) => session.create(input)), + (created) => SessionNs.Service.use((session) => session.remove(created.id).pipe(Effect.ignore)), + ) describe("session.listGlobal", () => { - test("lists sessions across projects with project metadata", async () => { - await using first = await tmpdir({ git: true }) - await using second = await tmpdir({ git: true }) - - const firstSession = await WithInstance.provide({ - directory: first.path, - fn: async () => svc.create({ title: "first-session" }), - }) - const secondSession = await WithInstance.provide({ - directory: second.path, - fn: async () => svc.create({ title: "second-session" }), - }) - - const sessions = [...svc.listGlobal({ limit: 200 })] - const ids = sessions.map((session) => session.id) - - expect(ids).toContain(firstSession.id) - expect(ids).toContain(secondSession.id) - - const firstProject = Project.get(firstSession.projectID) - const secondProject = Project.get(secondSession.projectID) - - const firstItem = sessions.find((session) => session.id === firstSession.id) - const secondItem = sessions.find((session) => session.id === secondSession.id) - - expect(firstItem?.project?.id).toBe(firstProject?.id) - expect(firstItem?.project?.worktree).toBe(firstProject?.worktree) - expect(secondItem?.project?.id).toBe(secondProject?.id) - expect(secondItem?.project?.worktree).toBe(secondProject?.worktree) - }) - - test("excludes archived sessions by default", async () => { - await using tmp = await tmpdir({ git: true }) - - const archived = await WithInstance.provide({ - directory: tmp.path, - fn: async () => svc.create({ title: "archived-session" }), - }) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => svc.setArchived({ sessionID: archived.id, time: Date.now() }), - }) - - const sessions = [...svc.listGlobal({ limit: 200 })] - const ids = sessions.map((session) => session.id) - - expect(ids).not.toContain(archived.id) - - const allSessions = [...svc.listGlobal({ limit: 200, archived: true })] - const allIds = allSessions.map((session) => session.id) - - expect(allIds).toContain(archived.id) - }) - - test("supports cursor pagination", async () => { - await using tmp = await tmpdir({ git: true }) - - const first = await WithInstance.provide({ - directory: tmp.path, - fn: async () => svc.create({ title: "page-one" }), - }) - await new Promise((resolve) => setTimeout(resolve, 5)) - const second = await WithInstance.provide({ - directory: tmp.path, - fn: async () => svc.create({ title: "page-two" }), - }) - - const page = [...svc.listGlobal({ directory: tmp.path, limit: 1 })] - expect(page.length).toBe(1) - expect(page[0]!.id).toBe(second.id) - - const next = [...svc.listGlobal({ directory: tmp.path, limit: 10, cursor: page[0]!.time.updated })] - const ids = next.map((session) => session.id) - - expect(ids).toContain(first.id) - expect(ids).not.toContain(second.id) - }) - - // kilocode_change start - project-family filter across worktrees (stale .git/kilo project ID) - test("filters by project family across worktrees when project IDs drift", async () => { - await using first = await tmpdir({ git: true }) - await using second = await tmpdir({ git: true }) - const worktree = path.join(first.path, "..", path.basename(first.path) + "-worktree") - - try { - await $`git worktree add ${worktree} -b test-branch-${Date.now()}`.cwd(first.path).quiet() - - // Create worktree session first so it computes its own project ID via rev-list - const branch = await WithInstance.provide({ - directory: worktree, - fn: async () => svc.create({ title: "worktree-session" }), - }) - - // Now write a stale project ID to .git/kilo — this overrides the root's cached ID - await Bun.write(path.join(first.path, ".git", "kilo"), "stale-project-id") - - const root = await WithInstance.provide({ - directory: first.path, - fn: async () => svc.create({ title: "root-session" }), - }) - await Bun.file(path.join(first.path, ".git", "kilo")).delete() - const other = await WithInstance.provide({ - directory: second.path, - fn: async () => svc.create({ title: "other-session" }), - }) - - const sessions = [...svc.listGlobal({ projectID: root.projectID, roots: true, limit: 200 })] - const ids = sessions.map((session) => session.id) - - expect(root.projectID).not.toBe(branch.projectID) - expect(ids).toContain(root.id) - expect(ids).toContain(branch.id) - expect(ids).not.toContain(other.id) - expect(sessions.find((session) => session.id === branch.id)?.directory).toBe(worktree) - } finally { - await $`git worktree remove ${worktree}`.cwd(first.path).quiet().nothrow() - } - }) - // kilocode_change end + it.instance( + "lists sessions across projects with project metadata", + () => + Effect.gen(function* () { + const first = yield* TestInstance + const second = yield* tmpdirScoped({ git: true }) + + const firstSession = yield* withSession({ title: "first-session" }) + const secondSession = yield* withSession({ title: "second-session" }).pipe(provideInstance(second)) + + const sessions = yield* Effect.sync(() => [...SessionNs.listGlobal({ limit: 200 })]) + const ids = sessions.map((session) => session.id) + + expect(ids).toContain(firstSession.id) + expect(ids).toContain(secondSession.id) + + const firstProject = yield* Project.Service.use((project) => project.get(firstSession.projectID)) + const secondProject = yield* Project.Service.use((project) => project.get(secondSession.projectID)) + + const firstItem = sessions.find((session) => session.id === firstSession.id) + const secondItem = sessions.find((session) => session.id === secondSession.id) + + expect(firstItem?.project?.id).toBe(firstProject?.id) + expect(firstItem?.project?.worktree).toBe(firstProject?.worktree) + expect(secondItem?.project?.id).toBe(secondProject?.id) + expect(secondItem?.project?.worktree).toBe(secondProject?.worktree) + expect(first.directory).not.toBe(second) + }), + { git: true }, + ) + + it.instance( + "excludes archived sessions by default", + () => + Effect.gen(function* () { + const archived = yield* withSession({ title: "archived-session" }) + + yield* SessionNs.Service.use((session) => session.setArchived({ sessionID: archived.id, time: Date.now() })) + + const sessions = yield* Effect.sync(() => [...SessionNs.listGlobal({ limit: 200 })]) + const ids = sessions.map((session) => session.id) + + expect(ids).not.toContain(archived.id) + + const allSessions = yield* Effect.sync(() => [...SessionNs.listGlobal({ limit: 200, archived: true })]) + const allIds = allSessions.map((session) => session.id) + + expect(allIds).toContain(archived.id) + }), + { git: true }, + ) + + it.instance( + "supports cursor pagination", + () => + Effect.gen(function* () { + const test = yield* TestInstance + + const first = yield* withSession({ title: "page-one" }) + const ready = yield* Deferred.make() + yield* Deferred.succeed(ready, undefined).pipe(Effect.delay("5 millis"), Effect.forkScoped) + yield* Deferred.await(ready).pipe( + Effect.timeoutOrElse({ + duration: "1 second", + orElse: () => Effect.fail(new Error("timed out waiting between session creates")), + }), + ) + const second = yield* withSession({ title: "page-two" }) + + const page = yield* Effect.sync(() => [...SessionNs.listGlobal({ directory: test.directory, limit: 1 })]) + expect(page.length).toBe(1) + expect(page[0].id).toBe(second.id) + + const next = yield* Effect.sync(() => [ + ...SessionNs.listGlobal({ directory: test.directory, limit: 10, cursor: page[0].time.updated }), + ]) + const ids = next.map((session) => session.id) + + expect(ids).toContain(first.id) + expect(ids).not.toContain(second.id) + }), + { git: true }, + ) }) diff --git a/packages/opencode/test/server/httpapi-config.test.ts b/packages/opencode/test/server/httpapi-config.test.ts index b295a5f29a9..018b4bb773c 100644 --- a/packages/opencode/test/server/httpapi-config.test.ts +++ b/packages/opencode/test/server/httpapi-config.test.ts @@ -1,10 +1,12 @@ -import { afterEach, describe, expect, test } from "bun:test" +import { afterEach, describe, expect } from "bun:test" import path from "path" import { Server } from "../../src/server/server" import * as Log from "@opencode-ai/core/util/log" +import { Effect, Fiber } from "effect" import { resetDatabase } from "../fixture/db" import { disposeAllInstances, tmpdir } from "../fixture/fixture" -import { waitGlobalBusEventPromise } from "./global-bus" +import { it } from "../lib/effect" +import { waitGlobalBusEvent } from "./global-bus" void Log.init({ print: false }) @@ -12,77 +14,100 @@ function app() { return Server.Default().app } -async function waitDisposed(directory: string) { - await waitGlobalBusEventPromise({ +function waitDisposed(directory: string) { + return waitGlobalBusEvent({ message: "timed out waiting for instance disposal", predicate: (event) => event.payload.type === "server.instance.disposed" && event.directory === directory, }) } +const tmpdirEffect = (options: Parameters[0]) => + Effect.acquireRelease( + Effect.promise(() => tmpdir(options)), + (tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()), + ) + afterEach(async () => { await disposeAllInstances() await resetDatabase() }) describe("config HttpApi", () => { - test("serves config update through Hono bridge", async () => { - await using tmp = await tmpdir({ config: { formatter: false, lsp: false } }) - const disposed = waitDisposed(tmp.path) - - const response = await app().request("/config", { - method: "PATCH", - headers: { - "content-type": "application/json", - "x-kilo-directory": tmp.path, - }, - body: JSON.stringify({ username: "patched-user", formatter: false, lsp: false }), - }) + it.live( + "serves config update through the default server app", + Effect.gen(function* () { + const tmp = yield* tmpdirEffect({ config: { formatter: false, lsp: false } }) + const disposed = yield* waitDisposed(tmp.path).pipe(Effect.forkScoped) - expect(response.status).toBe(200) - expect(await response.json()).toMatchObject({ username: "patched-user", formatter: false, lsp: false }) - await disposed - // kilocode_change - fixture wrote opencode.json; KilocodeConfig.updateProjectConfig patches it in place - expect(await Bun.file(path.join(tmp.path, "opencode.json")).json()).toMatchObject({ - username: "patched-user", - formatter: false, - lsp: false, - }) - }) + const response = yield* Effect.promise(() => + Promise.resolve( + app().request("/config", { + method: "PATCH", + headers: { + "content-type": "application/json", + "x-kilo-directory": tmp.path, + }, + body: JSON.stringify({ username: "patched-user", formatter: false, lsp: false }), + }), + ), + ) - test("serves config with active provider model status", async () => { - await using tmp = await tmpdir({ - config: { + expect(response.status).toBe(200) + expect(yield* Effect.promise(() => response.json())).toMatchObject({ + username: "patched-user", formatter: false, lsp: false, - provider: { - omniroute: { - models: { - "gpt-4o": { - status: "active", + }) + yield* Fiber.join(disposed) + expect(yield* Effect.promise(() => Bun.file(path.join(tmp.path, "opencode.json")).json())).toMatchObject({ // kilocode_change + username: "patched-user", + formatter: false, + lsp: false, + }) + }), + ) + + it.live( + "serves config with active provider model status", + Effect.gen(function* () { + const tmp = yield* tmpdirEffect({ + config: { + formatter: false, + lsp: false, + provider: { + omniroute: { + models: { + "gpt-4o": { + status: "active", + }, }, }, }, }, - }, - }) + }) - const response = await app().request("/config", { - headers: { - "x-kilo-directory": tmp.path, - }, - }) + const response = yield* Effect.promise(() => + Promise.resolve( + app().request("/config", { + headers: { + "x-kilo-directory": tmp.path, + }, + }), + ), + ) - expect(response.status).toBe(200) - expect(await response.json()).toMatchObject({ - provider: { - omniroute: { - models: { - "gpt-4o": { - status: "active", + expect(response.status).toBe(200) + expect(yield* Effect.promise(() => response.json())).toMatchObject({ + provider: { + omniroute: { + models: { + "gpt-4o": { + status: "active", + }, }, }, }, - }, - }) - }) + }) + }), + ) }) diff --git a/packages/opencode/test/server/httpapi-cors.test.ts b/packages/opencode/test/server/httpapi-cors.test.ts index a7c3a36fdb2..bdb733767c9 100644 --- a/packages/opencode/test/server/httpapi-cors.test.ts +++ b/packages/opencode/test/server/httpapi-cors.test.ts @@ -6,7 +6,7 @@ import { HttpClient, HttpClientRequest, HttpRouter, HttpServer } from "effect/un import * as Socket from "effect/unstable/socket/Socket" import { Server } from "../../src/server/server" import { InstancePaths } from "../../src/server/routes/instance/httpapi/groups/instance" -import { ExperimentalHttpApiServer } from "../../src/server/routes/instance/httpapi/server" +import { HttpApiApp } from "../../src/server/routes/instance/httpapi/server" import { resetDatabase } from "../fixture/db" import { testEffect } from "../lib/effect" @@ -27,7 +27,7 @@ const testStateLayer = Layer.effectDiscard( ) const servedRoutes: Layer.Layer = HttpRouter.serve( - ExperimentalHttpApiServer.routes, + HttpApiApp.routes, { disableListenLog: true, disableLogger: true }, ) @@ -63,7 +63,7 @@ describe("HttpApi CORS", () => { it.live("adds CORS headers to unauthorized responses", () => Effect.gen(function* () { const handler = HttpRouter.toWebHandler( - ExperimentalHttpApiServer.createRoutes().pipe( + HttpApiApp.createRoutes().pipe( Layer.provide(ConfigProvider.layer(ConfigProvider.fromUnknown({ KILO_SERVER_PASSWORD: "secret" }))), ), { disableLogger: true }, @@ -73,7 +73,7 @@ describe("HttpApi CORS", () => { new Request(new URL("/global/config", "http://localhost"), { headers: { origin: "https://app.opencode.ai" }, }), - ExperimentalHttpApiServer.context, + HttpApiApp.context, ), ) @@ -103,6 +103,20 @@ describe("HttpApi CORS", () => { expect(response.status).toBe(204) expect(response.headers.get("access-control-allow-origin")).toBe("https://custom.example") expect(response.headers.get("access-control-allow-headers")).toBe("authorization") + + const rejected = yield* Effect.promise(() => + fetch(new URL(InstancePaths.path, listener.url), { + method: "OPTIONS", + headers: { + origin: "https://evil.example", + "access-control-request-method": "GET", + "access-control-request-headers": "authorization", + }, + }), + ) + + expect(rejected.status).toBe(204) + expect(rejected.headers.get("access-control-allow-origin")).not.toBe("https://evil.example") }), ) }) diff --git a/packages/opencode/test/server/httpapi-error-middleware.test.ts b/packages/opencode/test/server/httpapi-error-middleware.test.ts new file mode 100644 index 00000000000..15f8aa20260 --- /dev/null +++ b/packages/opencode/test/server/httpapi-error-middleware.test.ts @@ -0,0 +1,71 @@ +import { NodeHttpServer, NodeServices } from "@effect/platform-node" +import { NamedError } from "@opencode-ai/core/util/error" +import { describe, expect } from "bun:test" +import { Effect, Layer } from "effect" +import { HttpClient, HttpClientRequest, HttpRouter } from "effect/unstable/http" +import { errorLayer } from "../../src/server/routes/instance/httpapi/middleware/error" +import { NotFoundError } from "../../src/storage/storage" +import { testEffect } from "../lib/effect" + +const it = testEffect(Layer.mergeAll(NodeHttpServer.layerTest, NodeServices.layer)) + +describe("HttpApi error middleware", () => { + it.live("returns a safe body for unknown 500 defects", () => + Effect.gen(function* () { + yield* HttpRouter.add("GET", "/boom", Effect.die(new Error("secret stack marker"))).pipe( + Layer.provide(errorLayer), + HttpRouter.serve, + Layer.build, + ) + + const response = yield* HttpClientRequest.get("/boom").pipe(HttpClient.execute) + const body = yield* response.json + + expect(response.status).toBe(500) + expect(body).toEqual({ + name: "UnknownError", + data: { message: "Unexpected server error. Check server logs for details." }, + }) + expect(JSON.stringify(body)).not.toContain("secret stack marker") + }), + ) + + it.live("returns a safe body for named defects", () => + Effect.gen(function* () { + yield* HttpRouter.add( + "GET", + "/named", + Effect.die(new NamedError.Unknown({ message: "secret named marker" })), + ).pipe(Layer.provide(errorLayer), HttpRouter.serve, Layer.build) + + const response = yield* HttpClientRequest.get("/named").pipe(HttpClient.execute) + const body = yield* response.json + + expect(response.status).toBe(500) + expect(body).toEqual({ + name: "UnknownError", + data: { message: "Unexpected server error. Check server logs for details." }, + }) + expect(JSON.stringify(body)).not.toContain("secret named marker") + }), + ) + + it.live("does not map storage not-found defects to 404", () => + Effect.gen(function* () { + yield* HttpRouter.add( + "GET", + "/missing", + Effect.die(new NotFoundError({ message: "Resource not found: secret" })), + ).pipe(Layer.provide(errorLayer), HttpRouter.serve, Layer.build) + + const response = yield* HttpClientRequest.get("/missing").pipe(HttpClient.execute) + const body = yield* response.json + + expect(response.status).toBe(500) + expect(body).toEqual({ + name: "UnknownError", + data: { message: "Unexpected server error. Check server logs for details." }, + }) + }), + ) +}) diff --git a/packages/opencode/test/server/httpapi-event.test.ts b/packages/opencode/test/server/httpapi-event.test.ts index 739a08110b0..5ad2926f274 100644 --- a/packages/opencode/test/server/httpapi-event.test.ts +++ b/packages/opencode/test/server/httpapi-event.test.ts @@ -1,10 +1,13 @@ import { afterEach, describe, expect, test } from "bun:test" +import { Bus } from "../../src/bus" import { Instance } from "../../src/project/instance" import { Server } from "../../src/server/server" -import { EventPaths } from "../../src/server/routes/instance/httpapi/event" +import { EventPaths } from "../../src/server/routes/instance/httpapi/groups/event" +import { Event as ServerEvent } from "../../src/server/event" import * as Log from "@opencode-ai/core/util/log" +import { Schema } from "effect" import { resetDatabase } from "../fixture/db" -import { disposeAllInstances, tmpdir } from "../fixture/fixture" +import { disposeAllInstances, reloadTestInstance, tmpdir } from "../fixture/fixture" void Log.init({ print: false }) @@ -12,25 +15,80 @@ function app() { return Server.Default().app } -async function readFirstChunk(response: Response) { +const EventData = Schema.Struct({ + id: Schema.optional(Schema.String), + type: Schema.String, + properties: Schema.Record(Schema.String, Schema.Any), +}) + +async function readChunk(reader: ReadableStreamDefaultReader, delay = 5_000) { + let timeout: ReturnType | undefined + try { + return await Promise.race([ + reader.read(), + new Promise((_, reject) => { + timeout = setTimeout(() => reject(new Error("timed out waiting for event")), delay) + }), + ]) + } finally { + if (timeout) clearTimeout(timeout) + } +} + +async function readFirstEvent(response: Response) { if (!response.body) throw new Error("missing response body") const reader = response.body.getReader() - const result = await Promise.race([ - reader.read(), - new Promise((_, reject) => setTimeout(() => reject(new Error("timed out waiting for event")), 5_000)), - ]) - await reader.cancel() - return new TextDecoder().decode(result.value) + try { + return await readEvent(reader) + } finally { + await reader.cancel() + } } -async function readFirstEvent(response: Response) { - return JSON.parse((await readFirstChunk(response)).replace(/^data: /, "")) as { - id?: string - type: string - properties: Record +async function readEvent(reader: ReadableStreamDefaultReader) { + const result = await readChunk(reader) + if (result.done || !result.value) throw new Error("event stream closed") + return Schema.decodeUnknownSync(EventData)(JSON.parse(new TextDecoder().decode(result.value).replace(/^data: /, ""))) +} + +async function expectOpen(reader: ReadableStreamDefaultReader, delay: number) { + const end = Date.now() + delay + while (true) { + const remaining = end - Date.now() + if (remaining <= 0) return + + let timeout: ReturnType | undefined + const result = await Promise.race([ + reader.read(), + new Promise<"open">((resolve) => { + timeout = setTimeout(() => resolve("open"), remaining) + }), + ]) + if (timeout) clearTimeout(timeout) + if (result === "open") return + if (result.done) throw new Error("event stream closed") } } +async function readUntil( + reader: ReadableStreamDefaultReader, + predicate: (event: Schema.Schema.Type) => boolean, + delay = 5_000, +) { + const end = Date.now() + delay + while (true) { + const event = await readEventWithin(reader, end - Date.now()) + if (predicate(event)) return event + } +} + +async function readEventWithin(reader: ReadableStreamDefaultReader, delay: number) { + if (delay <= 0) throw new Error("timed out waiting for event") + const result = await readChunk(reader, delay) + if (result.done || !result.value) throw new Error("event stream closed") + return Schema.decodeUnknownSync(EventData)(JSON.parse(new TextDecoder().decode(result.value).replace(/^data: /, ""))) +} + afterEach(async () => { await disposeAllInstances() await resetDatabase() @@ -49,11 +107,37 @@ describe("event HttpApi", () => { expect(await readFirstEvent(response)).toMatchObject({ type: "server.connected", properties: {} }) }) - test("serves the initial server connected event", async () => { + test("keeps the event stream open after the initial event", async () => { await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) - const headers = { "x-kilo-directory": tmp.path } - const response = await app().request(EventPaths.event, { headers }) + const response = await app().request(EventPaths.event, { headers: { "x-kilo-directory": tmp.path } }) + if (!response.body) throw new Error("missing response body") - expect(await readFirstEvent(response)).toMatchObject({ type: "server.connected", properties: {} }) + const reader = response.body.getReader() + try { + expect(await readEvent(reader)).toMatchObject({ type: "server.connected", properties: {} }) + await expectOpen(reader, 250) + } finally { + await reader.cancel() + } + }) + + test("delivers instance bus events after the initial event", async () => { + await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) + const response = await app().request(EventPaths.event, { headers: { "x-kilo-directory": tmp.path } }) + if (!response.body) throw new Error("missing response body") + + const reader = response.body.getReader() + try { + expect(await readEvent(reader)).toMatchObject({ type: "server.connected", properties: {} }) + + const id = Bus.createID() + const next = readUntil(reader, (event) => event.id === id && event.type === "server.connected") + const ctx = await reloadTestInstance({ directory: tmp.path }) + await Instance.restore(ctx, () => Bus.publish(ServerEvent.Connected, {}, { id })) + + expect(await next).toMatchObject({ id, type: "server.connected", properties: {} }) + } finally { + await reader.cancel() + } }) }) diff --git a/packages/opencode/test/server/httpapi-exercise/backend.ts b/packages/opencode/test/server/httpapi-exercise/backend.ts index 30f7c0c6bb9..752b17d8791 100644 --- a/packages/opencode/test/server/httpapi-exercise/backend.ts +++ b/packages/opencode/test/server/httpapi-exercise/backend.ts @@ -2,7 +2,7 @@ import { ConfigProvider, Effect, Layer } from "effect" import { HttpRouter } from "effect/unstable/http" import { parse } from "./assertions" import { runtime, type Runtime } from "./runtime" -import type { ActiveScenario, Backend, BackendApp, CallResult, CaptureMode, SeededContext } from "./types" +import type { ActiveScenario, BackendApp, CallResult, CaptureMode, SeededContext } from "./types" type CallOptions = { auth?: { @@ -11,27 +11,18 @@ type CallOptions = { } } -export function call( - backend: Backend, - scenario: ActiveScenario, - ctx: SeededContext, - options: CallOptions = {}, -) { +export function call(scenario: ActiveScenario, ctx: SeededContext, options: CallOptions = {}) { return Effect.promise(async () => - capture(await app(await runtime(), backend, options).request(toRequest(scenario, ctx)), scenario.capture), + capture(await app(await runtime(), options).request(toRequest(scenario, ctx)), scenario.capture), ) } -export function callAuthProbe( - backend: Backend, - scenario: ActiveScenario, - credentials: "missing" | "valid" = "missing", -) { +export function callAuthProbe(scenario: ActiveScenario, credentials: "missing" | "valid" = "missing") { return Effect.promise(async () => { const controller = new AbortController() return Promise.race([ Promise.resolve( - app(await runtime(), backend, { auth: { password: "secret" } }).request( + app(await runtime(), { auth: { password: "secret" } }).request( toAuthProbeRequest(scenario, credentials, controller.signal), ), ).then((response) => capture(response, scenario.capture)), @@ -51,14 +42,14 @@ export function callAuthProbe( const appCache: Partial> = {} -function app(modules: Runtime, backend: Backend, options: CallOptions) { +function app(modules: Runtime, options: CallOptions) { const username = options.auth?.username const password = options.auth?.password - const cacheKey = `${backend}:${username ?? ""}:${password ?? ""}` + const cacheKey = `${username ?? ""}:${password ?? ""}` if (appCache[cacheKey]) return appCache[cacheKey] const handler = HttpRouter.toWebHandler( - modules.ExperimentalHttpApiServer.routes.pipe( + modules.HttpApiApp.routes.pipe( Layer.provide( ConfigProvider.layer( ConfigProvider.fromUnknown({ KILO_SERVER_PASSWORD: password, KILO_SERVER_USERNAME: username }), @@ -71,7 +62,7 @@ function app(modules: Runtime, backend: Backend, options: CallOptions) { request(input: string | URL | Request, init?: RequestInit) { return handler( input instanceof Request ? input : new Request(new URL(input, "http://localhost"), init), - modules.ExperimentalHttpApiServer.context, + modules.HttpApiApp.context, ) }, }) diff --git a/packages/opencode/test/server/httpapi-exercise/index.ts b/packages/opencode/test/server/httpapi-exercise/index.ts index 0606a7caaba..5b93d63464c 100644 --- a/packages/opencode/test/server/httpapi-exercise/index.ts +++ b/packages/opencode/test/server/httpapi-exercise/index.ts @@ -588,6 +588,12 @@ const scenarios: Scenario[] = [ check(auth.test === undefined, "auth remove should delete provider from isolated auth file") }), ), + http.protected.get("/api/model", "v2.model.list").json(200, array), + http.protected.get("/api/provider", "v2.provider.list").json(200, array), + http.protected + .get("/api/provider/{providerID}", "v2.provider.get") + .at((ctx) => ({ path: route("/api/provider/{providerID}", { providerID: "missing" }), headers: ctx.headers() })) + .json(404, object, "status"), http.protected .get("/api/session", "v2.session.list") .at((ctx) => ({ path: "/api/session?roots=true", headers: ctx.headers() })) diff --git a/packages/opencode/test/server/httpapi-exercise/runner.ts b/packages/opencode/test/server/httpapi-exercise/runner.ts index 8762db655bf..a98f8b929ad 100644 --- a/packages/opencode/test/server/httpapi-exercise/runner.ts +++ b/packages/opencode/test/server/httpapi-exercise/runner.ts @@ -30,28 +30,28 @@ function runActive(options: Options, scenario: ActiveScenario) { return withContext(options, scenario, "shared", (ctx) => Effect.gen(function* () { - yield* trace(options, scenario, "effect request start") - const effect = yield* call("effect", scenario, ctx) - yield* trace(options, scenario, `effect response ${effect.status}`) - yield* trace(options, scenario, "effect expect start") - yield* scenario.expect(ctx, ctx.state, effect) - yield* trace(options, scenario, "effect expect done") + yield* trace(options, scenario, "request start") + const result = yield* call(scenario, ctx) + yield* trace(options, scenario, `response ${result.status}`) + yield* trace(options, scenario, "expect start") + yield* scenario.expect(ctx, ctx.state, result) + yield* trace(options, scenario, "expect done") }), ) } function runAuth(scenario: ActiveScenario) { return Effect.gen(function* () { - const effect = yield* callAuthProbe("effect", scenario, "missing") + const result = yield* callAuthProbe(scenario, "missing") if (scenario.auth === "protected") { - if (effect.status !== 401) throw new Error(`effect auth expected 401, got ${effect.status}`) - const effectAuthed = yield* callAuthProbe("effect", scenario, "valid") - if (effectAuthed.status === 401) throw new Error("effect auth rejected valid credentials") + if (result.status !== 401) throw new Error(`auth expected 401, got ${result.status}`) + const authed = yield* callAuthProbe(scenario, "valid") + if (authed.status === 401) throw new Error("auth rejected valid credentials") return } - if (effect.status === 401) throw new Error("effect auth expected public access, got 401") - if (effect.timedOut) throw new Error("effect auth expected public access, probe timed out") + if (result.status === 401) throw new Error("auth expected public access, got 401") + if (result.timedOut) throw new Error("auth expected public access, probe timed out") }) } @@ -168,9 +168,10 @@ function withContext( ) return { info, part } }), - messages: (sessionID) => run(modules.Session.Service.use((svc) => svc.messages({ sessionID }))), + messages: (sessionID) => + run(modules.Session.Service.use((svc) => svc.messages({ sessionID }).pipe(Effect.orDie))), todos: (sessionID, todos) => run(modules.Todo.Service.use((svc) => svc.update({ sessionID, todos }))), - worktree: (input) => run(modules.Worktree.Service.use((svc) => svc.create(input))), + worktree: (input) => run(modules.Worktree.Service.use((svc) => svc.create(input).pipe(Effect.orDie))), worktreeRemove: (directory) => run(modules.Worktree.Service.use((svc) => svc.remove({ directory })).pipe(Effect.ignore)), llmText: (value) => Effect.suspend(() => llm().text(value)), diff --git a/packages/opencode/test/server/httpapi-exercise/runtime.ts b/packages/opencode/test/server/httpapi-exercise/runtime.ts index 7c0e6c6b89c..9d2b6d238e6 100644 --- a/packages/opencode/test/server/httpapi-exercise/runtime.ts +++ b/packages/opencode/test/server/httpapi-exercise/runtime.ts @@ -1,6 +1,6 @@ export type Runtime = { PublicApi: (typeof import("../../../src/server/routes/instance/httpapi/public"))["PublicApi"] - ExperimentalHttpApiServer: (typeof import("../../../src/server/routes/instance/httpapi/server"))["ExperimentalHttpApiServer"] + HttpApiApp: (typeof import("../../../src/server/routes/instance/httpapi/server"))["HttpApiApp"] AppLayer: (typeof import("../../../src/effect/app-runtime"))["AppLayer"] InstanceRef: (typeof import("../../../src/effect/instance-ref"))["InstanceRef"] Instance: (typeof import("../../../src/project/instance"))["Instance"] @@ -32,7 +32,7 @@ export function runtime() { const fixture = await import("../../fixture/fixture") return { PublicApi: publicApi.PublicApi, - ExperimentalHttpApiServer: httpApiServer.ExperimentalHttpApiServer, + HttpApiApp: httpApiServer.HttpApiApp, AppLayer: appRuntime.AppLayer, InstanceRef: instanceRef.InstanceRef, Instance: instance.Instance, diff --git a/packages/opencode/test/server/httpapi-exercise/types.ts b/packages/opencode/test/server/httpapi-exercise/types.ts index a0466d7b709..e1fe93ba7ef 100644 --- a/packages/opencode/test/server/httpapi-exercise/types.ts +++ b/packages/opencode/test/server/httpapi-exercise/types.ts @@ -11,7 +11,6 @@ export const Methods = ["GET", "POST", "PUT", "DELETE", "PATCH"] as const export type Method = (typeof Methods)[number] export type OpenApiMethod = (typeof OpenApiMethods)[number] export type Mode = "effect" | "coverage" | "auth" -export type Backend = "effect" export type Comparison = "none" | "status" | "json" export type CaptureMode = "full" | "stream" export type AuthPolicy = "protected" | "public" | "public-bypass" | "ticket-bypass" diff --git a/packages/opencode/test/server/httpapi-experimental.test.ts b/packages/opencode/test/server/httpapi-experimental.test.ts index 45aaa6512d4..a5e47ea7bca 100644 --- a/packages/opencode/test/server/httpapi-experimental.test.ts +++ b/packages/opencode/test/server/httpapi-experimental.test.ts @@ -1,48 +1,183 @@ -import { afterEach, describe, expect, test } from "bun:test" -import { Effect } from "effect" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" +import { afterEach, describe, expect } from "bun:test" +import { Deferred, Effect, Fiber, Layer } from "effect" +import { eq } from "drizzle-orm" +import { GlobalBus, type GlobalEvent } from "@/bus/global" import { Server } from "../../src/server/server" import { ExperimentalPaths } from "../../src/server/routes/instance/httpapi/groups/experimental" import { Session } from "@/session/session" +import { SessionTable } from "@/session/session.sql" import { Database } from "@/storage/db" import * as Log from "@opencode-ai/core/util/log" import { Worktree } from "../../src/worktree" import { resetDatabase } from "../fixture/db" -import { disposeAllInstances, tmpdir } from "../fixture/fixture" -import { waitGlobalBusEventPromise } from "./global-bus" +import { disposeAllInstances, TestInstance } from "../fixture/fixture" +import { testEffect } from "../lib/effect" void Log.init({ print: false }) -const testWorktreeMutations = process.platform === "win32" ? test.skip : test +const it = testEffect(Layer.mergeAll(Session.defaultLayer)) +const testWorktreeMutations = process.platform === "win32" ? it.instance.skip : it.instance function app() { return Server.Default().app } -function runSession(fx: Effect.Effect) { - return Effect.runPromise(fx.pipe(Effect.provide(Session.defaultLayer))) +function request(path: string, directory: string, init: RequestInit = {}) { + return Effect.promise(() => { + const headers = new Headers(init.headers) + headers.set("x-kilo-directory", directory) + return Promise.resolve(app().request(path, { ...init, headers })) + }) } function createSession(input?: Session.CreateInput) { - return runSession(Session.Service.use((svc) => svc.create(input))) + return Session.Service.use((svc) => svc.create(input)) +} + +function json(response: Response) { + return Effect.promise(() => response.json() as Promise) } -async function waitReady(directory: string) { - await waitGlobalBusEventPromise({ - message: "timed out waiting for worktree.ready", - predicate: (event) => event.payload.type === Worktree.Event.Ready.type && event.directory === directory, +function waitReady(input: { directory?: string; name?: string }) { + return Effect.gen(function* () { + const ready = yield* Deferred.make() + const on = (event: GlobalEvent) => { + if (event.payload.type !== Worktree.Event.Ready.type) return + if (input.directory && event.directory !== input.directory) return + if (input.name && event.payload.properties.name !== input.name) return + Deferred.doneUnsafe(ready, Effect.void) + } + + GlobalBus.on("event", on) + yield* Effect.addFinalizer(() => Effect.sync(() => GlobalBus.off("event", on))) + + return yield* Deferred.await(ready).pipe( + Effect.timeoutOrElse({ + duration: "10 seconds", + orElse: () => Effect.fail(new Error("timed out waiting for worktree.ready")), + }), + ) }) } +function insertAccount() { + return Effect.acquireRelease( + Effect.sync(() => { + Database.Client() + .$client.prepare( + "INSERT INTO account (id, email, url, access_token, refresh_token, time_created, time_updated) VALUES (?, ?, ?, ?, ?, ?, ?)", + ) + .run( + "account-test", + "test@example.com", + "https://console.example.com", + "access", + "refresh", + Date.now(), + Date.now(), + ) + return "account-test" + }), + (id) => + Effect.sync(() => { + Database.Client().$client.prepare("DELETE FROM account WHERE id = ?").run(id) + }), + ) +} + +function setSessionUpdated(session: Session.Info, updated: number) { + return Effect.sync(() => { + Database.use((db) => + db.update(SessionTable).set({ time_updated: updated }).where(eq(SessionTable.id, session.id)).run(), + ) + }) +} + +function withCreatedWorktree(directory: string, use: (info: Worktree.Info) => Effect.Effect) { + const name = "api-test" + const headers = { "content-type": "application/json" } + return Effect.acquireUseRelease( + Effect.gen(function* () { + const ready = yield* waitReady({ name }).pipe(Effect.forkScoped) + const created = yield* request(ExperimentalPaths.worktree, directory, { + method: "POST", + headers, + body: JSON.stringify({ name }), + }) + + expect(created.status).toBe(200) + const info = yield* json(created) + expect(info).toMatchObject({ name, branch: "opencode/api-test" }) + yield* Fiber.join(ready) + return info + }), + use, + (info) => + Effect.gen(function* () { + const removed = yield* request(ExperimentalPaths.worktree, directory, { + method: "DELETE", + headers, + body: JSON.stringify({ directory: info.directory }), + }) + if (removed.status !== 200) return yield* Effect.fail(new Error(`failed to remove worktree: ${removed.status}`)) + const ok = yield* json(removed) + if (!ok) return yield* Effect.fail(new Error(`failed to remove worktree ${info.directory}`)) + }), + ) +} + afterEach(async () => { await disposeAllInstances() await resetDatabase() }) describe("experimental HttpApi", () => { - test("serves read-only experimental endpoints", async () => { - await using tmp = await tmpdir({ + it.instance( + "serves read-only experimental endpoints through the default server app", + () => + Effect.gen(function* () { + const tmp = yield* TestInstance + const directory = tmp.directory + const [consoleState, consoleOrgs, toolList, toolIDs, worktrees, resources] = yield* Effect.all( + [ + request(ExperimentalPaths.console, directory), + request(ExperimentalPaths.consoleOrgs, directory), + request(`${ExperimentalPaths.tool}?provider=opencode&model=gpt-5`, directory), + request(ExperimentalPaths.toolIDs, directory), + request(ExperimentalPaths.worktree, directory), + request(ExperimentalPaths.resource, directory), + ], + { concurrency: "unbounded" }, + ) + + expect(consoleState.status).toBe(200) + expect(yield* json(consoleState)).toEqual({ + consoleManagedProviders: [], + switchableOrgCount: 0, + }) + + expect(consoleOrgs.status).toBe(200) + expect(yield* json(consoleOrgs)).toEqual({ orgs: [] }) + + expect(toolList.status).toBe(200) + expect(yield* json(toolList)).toContainEqual( + expect.objectContaining({ + id: "bash", + description: expect.any(String), + parameters: expect.any(Object), + }), + ) + + expect(toolIDs.status).toBe(200) + expect(yield* json(toolIDs)).toContain("bash") + + expect(worktrees.status).toBe(200) + expect(yield* json(worktrees)).toEqual([]) + + expect(resources.status).toBe(200) + expect(yield* json(resources)).toEqual({}) + }), + { config: { formatter: false, lsp: false, @@ -54,150 +189,105 @@ describe("experimental HttpApi", () => { }, }, }, - }) - - const headers = { "x-kilo-directory": tmp.path } - const [consoleState, consoleOrgs, toolList, toolIDs, worktrees, resources] = await Promise.all([ - app().request(ExperimentalPaths.console, { headers }), - app().request(ExperimentalPaths.consoleOrgs, { headers }), - app().request(`${ExperimentalPaths.tool}?provider=opencode&model=gpt-5`, { headers }), - app().request(ExperimentalPaths.toolIDs, { headers }), - app().request(ExperimentalPaths.worktree, { headers }), - app().request(ExperimentalPaths.resource, { headers }), - ]) - - expect(consoleState.status).toBe(200) - expect(await consoleState.json()).toEqual({ - consoleManagedProviders: [], - switchableOrgCount: 0, - }) - - expect(consoleOrgs.status).toBe(200) - expect(await consoleOrgs.json()).toEqual({ orgs: [] }) - - expect(toolList.status).toBe(200) - expect(await toolList.json()).toContainEqual( - expect.objectContaining({ - id: "bash", - description: expect.any(String), - parameters: expect.any(Object), - }), - ) + }, + ) - expect(toolIDs.status).toBe(200) - expect(await toolIDs.json()).toContain("bash") - - expect(worktrees.status).toBe(200) - expect(await worktrees.json()).toEqual([]) - - expect(resources.status).toBe(200) - expect(await resources.json()).toEqual({}) - }) - - test("serves Console org switch through Hono bridge", async () => { - await using tmp = await tmpdir({ config: { formatter: false, lsp: false } }) - Database.Client() - .$client.prepare( - "INSERT INTO account (id, email, url, access_token, refresh_token, time_created, time_updated) VALUES (?, ?, ?, ?, ?, ?, ?)", - ) - .run( - "account-test", - "test@example.com", - "https://console.example.com", - "access", - "refresh", - Date.now(), - Date.now(), - ) - - const switched = await app().request(ExperimentalPaths.consoleSwitch, { - method: "POST", - headers: { "x-kilo-directory": tmp.path, "content-type": "application/json" }, - body: JSON.stringify({ accountID: "account-test", orgID: "org-test" }), - }) - - expect(switched.status).toBe(200) - expect(await switched.json()).toBe(true) - }) - - test("serves global session list through Hono bridge", async () => { - await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) - - const first = await WithInstance.provide({ - directory: tmp.path, - fn: async () => createSession({ title: "page-one" }), - }) - await new Promise((resolve) => setTimeout(resolve, 5)) - const second = await WithInstance.provide({ - directory: tmp.path, - fn: async () => createSession({ title: "page-two" }), - }) - - const headers = { "x-kilo-directory": tmp.path } - const page = await app().request( - `${ExperimentalPaths.session}?${new URLSearchParams({ directory: tmp.path, limit: "1" })}`, - { headers }, - ) - expect(page.status).toBe(200) - expect(page.headers.get("x-next-cursor")).toBeTruthy() - - const body = (await page.json()) as Session.GlobalInfo[] - expect(body.map((session) => session.id)).toEqual([second.id]) - expect(body[0].project?.id).toBe(second.projectID) - - const next = await app().request( - `${ExperimentalPaths.session}?${new URLSearchParams({ - directory: tmp.path, - limit: "10", - cursor: body[0].time.updated.toString(), - })}`, - { headers }, - ) - expect(next.status).toBe(200) - expect(((await next.json()) as Session.GlobalInfo[]).map((session) => session.id)).toContain(first.id) - }) - - testWorktreeMutations("serves worktree mutations through Hono bridge", async () => { - await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) - - const headers = { "x-kilo-directory": tmp.path, "content-type": "application/json" } - const created = await app().request(ExperimentalPaths.worktree, { - method: "POST", - headers, - body: JSON.stringify({ name: "api-test" }), - }) - - expect(created.status).toBe(200) - const info = (await created.json()) as Worktree.Info - expect(info).toMatchObject({ name: "api-test", branch: "opencode/api-test" }) - await waitReady(info.directory) - - const listed = await app().request(ExperimentalPaths.worktree, { headers }) - expect(listed.status).toBe(200) - expect(await listed.json()).toContain(info.directory) - - if (process.platform !== "win32") { - const reset = await app().request(ExperimentalPaths.worktreeReset, { + it.instance("returns declared worktree errors", () => + Effect.gen(function* () { + const tmp = yield* TestInstance + const response = yield* request(ExperimentalPaths.worktree, tmp.directory, { method: "POST", - headers, - body: JSON.stringify({ directory: info.directory }), + headers: { "content-type": "application/json" }, + body: JSON.stringify({}), }) - expect(reset.status).toBe(200) - expect(await reset.json()).toBe(true) - } - - const removed = await app().request(ExperimentalPaths.worktree, { - method: "DELETE", - headers, - body: JSON.stringify({ directory: info.directory }), - }) - - expect(removed.status).toBe(200) - expect(await removed.json()).toBe(true) - - const afterRemove = await app().request(ExperimentalPaths.worktree, { headers }) - expect(afterRemove.status).toBe(200) - expect(await afterRemove.json()).toEqual([]) - }) + expect(response.status).toBe(400) + expect(yield* json(response)).toEqual({ + name: "WorktreeNotGitError", + data: { message: "Worktrees are only supported for git projects" }, + }) + }), + ) + + it.instance( + "serves Console org switch through the default server app", + () => + Effect.gen(function* () { + const tmp = yield* TestInstance + const accountID = yield* insertAccount() + const switched = yield* request(ExperimentalPaths.consoleSwitch, tmp.directory, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ accountID, orgID: "org-test" }), + }) + + expect(switched.status).toBe(200) + expect(yield* json(switched)).toBe(true) + }), + { config: { formatter: false, lsp: false } }, + ) + + it.instance( + "serves global session list through the default server app", + () => + Effect.gen(function* () { + const tmp = yield* TestInstance + const first = yield* createSession({ title: "page-one" }) + const second = yield* createSession({ title: "page-two" }) + yield* setSessionUpdated(first, 1) + yield* setSessionUpdated(second, 2) + + const page = yield* request( + `${ExperimentalPaths.session}?${new URLSearchParams({ directory: tmp.directory, limit: "1" })}`, + tmp.directory, + ) + expect(page.status).toBe(200) + expect(page.headers.get("x-next-cursor")).toBeTruthy() + + const body = yield* json(page) + expect(body.map((session) => session.id)).toEqual([second.id]) + expect(body[0].project?.id).toBe(second.projectID) + + const next = yield* request( + `${ExperimentalPaths.session}?${new URLSearchParams({ + directory: tmp.directory, + limit: "10", + cursor: body[0].time.updated.toString(), + })}`, + tmp.directory, + ) + expect(next.status).toBe(200) + expect((yield* json(next)).map((session) => session.id)).toContain(first.id) + }), + { git: true, config: { formatter: false, lsp: false } }, + ) + + testWorktreeMutations( + "serves worktree mutations through the default server app", + () => + Effect.gen(function* () { + const tmp = yield* TestInstance + yield* withCreatedWorktree(tmp.directory, (info) => + Effect.gen(function* () { + const listed = yield* request(ExperimentalPaths.worktree, tmp.directory) + expect(listed.status).toBe(200) + expect(yield* json(listed)).toContain(info.directory) + + const reset = yield* request(ExperimentalPaths.worktreeReset, tmp.directory, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ directory: info.directory }), + }) + + expect(reset.status).toBe(200) + expect(yield* json(reset)).toBe(true) + }), + ) + + const afterRemove = yield* request(ExperimentalPaths.worktree, tmp.directory) + expect(afterRemove.status).toBe(200) + expect(yield* json(afterRemove)).toEqual([]) + }), + { git: true, config: { formatter: false, lsp: false } }, + ) }) diff --git a/packages/opencode/test/server/httpapi-file.test.ts b/packages/opencode/test/server/httpapi-file.test.ts index 99a70be1477..442a996a291 100644 --- a/packages/opencode/test/server/httpapi-file.test.ts +++ b/packages/opencode/test/server/httpapi-file.test.ts @@ -1,7 +1,7 @@ import { afterEach, describe, expect, test } from "bun:test" import { Context } from "effect" import path from "path" -import { ExperimentalHttpApiServer } from "../../src/server/routes/instance/httpapi/server" +import { HttpApiApp } from "../../src/server/routes/instance/httpapi/server" import { FilePaths } from "../../src/server/routes/instance/httpapi/groups/file" import { Instance } from "../../src/project/instance" import * as Log from "@opencode-ai/core/util/log" @@ -17,7 +17,7 @@ function request(route: string, directory: string, query?: Record resetDatabase()) - Flag.KILO_EXPERIMENTAL_WORKSPACES = true yield* Effect.addFinalizer(() => Effect.promise(async () => { - Flag.KILO_EXPERIMENTAL_WORKSPACES = originalWorkspaces await disposeAllInstances() await resetDatabase() }), @@ -41,19 +35,14 @@ const testStateLayer = Layer.effectDiscard( }), ) -const workspaceLayer = Workspace.defaultLayer.pipe( - Layer.provide(InstanceStore.defaultLayer), - Layer.provide(InstanceBootstrap.defaultLayer), -) +const workspaceLayer = workspaceLayerWithRuntimeFlags({ experimentalWorkspaces: true }) const it = testEffect( Layer.mergeAll( testStateLayer, NodeHttpServer.layerTest, NodeServices.layer, - // kilocode_change start - local equivalent of InstanceLayer.layer. - InstanceStore.defaultLayer.pipe(Layer.provide(InstanceBootstrap.defaultLayer)), - // kilocode_change end + InstanceLayer.layer, Project.defaultLayer, workspaceLayer, ), diff --git a/packages/opencode/test/server/httpapi-instance.test.ts b/packages/opencode/test/server/httpapi-instance.test.ts index 631b4bfd5b1..0a988fb58a3 100644 --- a/packages/opencode/test/server/httpapi-instance.test.ts +++ b/packages/opencode/test/server/httpapi-instance.test.ts @@ -8,10 +8,10 @@ import { WorkspaceID } from "../../src/control-plane/schema" import { ControlPaths } from "../../src/server/routes/instance/httpapi/groups/control" import { InstancePaths } from "../../src/server/routes/instance/httpapi/groups/instance" import { SessionPaths } from "../../src/server/routes/instance/httpapi/groups/session" -import { ExperimentalHttpApiServer } from "../../src/server/routes/instance/httpapi/server" +import { HttpApiApp } from "../../src/server/routes/instance/httpapi/server" import { HEADER as FenceHeader } from "../../src/server/shared/fence" import { resetDatabase } from "../fixture/db" -import { disposeAllInstances, tmpdirScoped } from "../fixture/fixture" +import { tmpdirScoped } from "../fixture/fixture" import { testEffect } from "../lib/effect" // Flip the experimental workspaces flag so SyncEvent.run actually writes to @@ -35,10 +35,9 @@ const testStateLayer = Layer.effectDiscard( // Mount the production HttpApi route tree on a real Node HTTP server bound to // 127.0.0.1:0 and a fetch-based HttpClient that prepends the server URL. This -// keeps the test wired through the same route layer production uses, without -// going through Server.Default()/Hono. +// keeps the test wired directly through the same route layer production uses. const servedRoutes: Layer.Layer = HttpRouter.serve( - ExperimentalHttpApiServer.routes, + HttpApiApp.routes, { disableListenLog: true, disableLogger: true }, ) @@ -125,7 +124,7 @@ describe("instance HttpApi", () => { const dir = yield* tmpdirScoped({ git: true }) const request = (path: string, init?: RequestInit) => Effect.promise(() => - ExperimentalHttpApiServer.webHandler().handler( + HttpApiApp.webHandler().handler( new Request(`http://localhost${path}`, { ...init, headers: { "x-kilo-directory": dir, "content-type": "application/json", ...init?.headers }, diff --git a/packages/opencode/test/server/httpapi-listen.test.ts b/packages/opencode/test/server/httpapi-listen.test.ts index e81e15c65ce..17e10c22d92 100644 --- a/packages/opencode/test/server/httpapi-listen.test.ts +++ b/packages/opencode/test/server/httpapi-listen.test.ts @@ -1,4 +1,5 @@ import { afterEach, describe, expect, test } from "bun:test" +import net from "node:net" import { Flag } from "@opencode-ai/core/flag/flag" import * as Log from "@opencode-ai/core/util/log" import { Server } from "../../src/server/server" @@ -156,6 +157,16 @@ function waitForMessage(ws: WebSocket, predicate: (message: string) => boolean) }) } +async function openPtySocket(listener: Awaited>, dir: string) { + const info = await createCat(listener, dir) + const ticket = await connectTicket(listener, info.id, dir) + const ws = await openSocket(socketURL(listener, info.id, dir, ticket.ticket)) + return { + ws, + closed: new Promise((resolve) => ws.addEventListener("close", () => resolve(), { once: true })), + } +} + describe("HttpApi Server.listen", () => { testPty("serves HTTP routes and upgrades PTY websocket through Server.listen", async () => { await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) @@ -208,6 +219,124 @@ describe("HttpApi Server.listen", () => { } }) + testPty("stop(true) is safe when called concurrently and repeatedly", async () => { + await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) + const listener = await startListener() + let stopped = false + try { + const socket = await openPtySocket(listener, tmp.path) + + await withTimeout( + Promise.all([listener.stop(true), listener.stop(true)]).then(() => undefined), + 10_000, + "timed out waiting for concurrent listener.stop(true)", + ) + await withTimeout(socket.closed, 5_000, "timed out waiting for websocket close after concurrent stop") + await withTimeout(listener.stop(true), 5_000, "timed out waiting for repeated listener.stop(true)") + stopped = true + } finally { + if (!stopped) await stop(listener, "timed out cleaning up concurrent stop listener").catch(() => undefined) + } + }) + + testPty("stop(true) can force a graceful stop already in progress", async () => { + await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) + const listener = await startListener() + let stopped = false + try { + const socket = await openPtySocket(listener, tmp.path) + + const graceful = listener.stop() + const forced = listener.stop(true) + await withTimeout( + Promise.all([graceful, forced]).then(() => undefined), + 10_000, + "timed out waiting for forced listener stop", + ) + await withTimeout(socket.closed, 5_000, "timed out waiting for websocket close after forced stop") + stopped = true + } finally { + if (!stopped) await stop(listener, "timed out cleaning up forced stop listener").catch(() => undefined) + } + }) + + testPty("graceful stop waits for an overlapping forced stop", async () => { + await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) + const listener = await startListener() + let stopped = false + try { + const socket = await openPtySocket(listener, tmp.path) + const forced = listener.stop(true) + await withTimeout(listener.stop(), 10_000, "timed out waiting for graceful stop after forced stop") + stopped = true + await withTimeout(forced, 5_000, "timed out waiting for overlapping forced stop") + await withTimeout(socket.closed, 5_000, "timed out waiting for websocket close before graceful stop resolved") + } finally { + if (!stopped) await stop(listener, "timed out cleaning up overlapping stop listener").catch(() => undefined) + } + }) + + test("stop() gracefully closes an idle listener and is repeat-safe", async () => { + const listener = await startListener() + await withTimeout(listener.stop(), 10_000, "timed out waiting for graceful listener.stop()") + await withTimeout(listener.stop(), 5_000, "timed out waiting for repeated graceful listener.stop()") + await expect( + fetch(new URL(PtyPaths.shells, listener.url), { headers: { authorization: authorization() } }), + ).rejects.toThrow() + }) + + test("default in-process handler does not emit Effect HTTP response logs", async () => { + let output = "" + // oxlint-disable-next-line typescript-eslint/unbound-method -- restored in finally after temporarily capturing stderr. + const original = process.stderr.write + process.stderr.write = ((chunk) => { + output += String(chunk) + return true + }) as typeof process.stderr.write + try { + // kilocode_change start - use an authenticated local route instead of proxy-dependent status + Flag.KILO_SERVER_PASSWORD = auth.password + Flag.KILO_SERVER_USERNAME = auth.username + process.env.KILO_SERVER_PASSWORD = auth.password + process.env.KILO_SERVER_USERNAME = auth.username + const response = await Server.Default().app.request("/doc", { + headers: { authorization: authorization() }, + }) + // kilocode_change end + expect(response.status).toBe(200) + } finally { + process.stderr.write = original + } + + expect(output).not.toContain("Sent HTTP response") + }) + + test("port 0 prefers 4096 when free", async () => { + if (!(await isPortFree(4096))) return + const listener = await startListener() + try { + expect(listener.port).toBe(4096) + } finally { + await stop(listener, "timed out cleaning up port-0 prefers-4096 listener") + } + }) + + test("port 0 falls back when 4096 is taken", async () => { + const blocker = await occupyPort(4096) + if (!blocker) return + try { + const listener = await startListener() + try { + expect(listener.port).not.toBe(4096) + expect(listener.port).toBeGreaterThan(0) + } finally { + await stop(listener, "timed out cleaning up port-0 fallback listener") + } + } finally { + await new Promise((resolve) => blocker.close(() => resolve())) + } + }) + testPty("rejects unsafe PTY ticket mint and connect requests", async () => { await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) const listener = await startListener() @@ -295,3 +424,20 @@ describe("HttpApi Server.listen", () => { } }) }) + +function isPortFree(port: number) { + return new Promise((resolve) => { + const probe = net.createServer() + probe.once("error", () => resolve(false)) + probe.once("listening", () => probe.close(() => resolve(true))) + probe.listen(port, "127.0.0.1") + }) +} + +function occupyPort(port: number) { + return new Promise((resolve) => { + const server = net.createServer() + server.once("error", () => resolve(undefined)) + server.listen(port, "127.0.0.1", () => resolve(server)) + }) +} diff --git a/packages/opencode/test/server/httpapi-mcp.test.ts b/packages/opencode/test/server/httpapi-mcp.test.ts index e77c5931449..ab1bbe530a4 100644 --- a/packages/opencode/test/server/httpapi-mcp.test.ts +++ b/packages/opencode/test/server/httpapi-mcp.test.ts @@ -1,69 +1,57 @@ -import { afterEach, describe, expect, test } from "bun:test" -import { Context, Effect, FileSystem, Layer, Path } from "effect" -import { NodeFileSystem, NodePath } from "@effect/platform-node" -import { ExperimentalHttpApiServer } from "../../src/server/routes/instance/httpapi/server" +import { describe, expect } from "bun:test" +import { Context, Effect, Layer } from "effect" +import { HttpApiApp } from "../../src/server/routes/instance/httpapi/server" import { McpPaths } from "../../src/server/routes/instance/httpapi/groups/mcp" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" -import { InstanceRuntime } from "../../src/project/instance-runtime" import { Server } from "../../src/server/server" import * as Log from "@opencode-ai/core/util/log" import { resetDatabase } from "../fixture/db" -import { disposeAllInstances, provideInstance, tmpdir } from "../fixture/fixture" +import { TestInstance } from "../fixture/fixture" import { testEffect } from "../lib/effect" void Log.init({ print: false }) const context = Context.empty() as Context.Context -const it = testEffect(Layer.mergeAll(NodeFileSystem.layer, NodePath.layer)) +const testStateLayer = Layer.effectDiscard( + Effect.gen(function* () { + yield* Effect.promise(() => resetDatabase()) + yield* Effect.addFinalizer(() => Effect.promise(() => resetDatabase()).pipe(Effect.ignore)) + }), +) +const it = testEffect(testStateLayer) function app() { return Server.Default().app } type TestApp = ReturnType - -function request(route: string, directory: string, init?: RequestInit) { +type TestHandler = ReturnType + +const handlerScoped = Effect.acquireRelease( + Effect.sync(() => HttpApiApp.webHandler()), + (handler) => Effect.promise(() => handler.dispose()).pipe(Effect.ignore), +) + +const request = Effect.fnUntraced(function* ( + handler: TestHandler, + route: string, + directory: string, + init?: RequestInit, +) { const headers = new Headers(init?.headers) headers.set("x-kilo-directory", directory) - return ExperimentalHttpApiServer.webHandler().handler( - new Request(`http://localhost${route}`, { - ...init, - headers, - }), - context, + return yield* Effect.promise(() => + Promise.resolve( + handler.handler( + new Request(`http://localhost${route}`, { + ...init, + headers, + }), + context, + ), + ), ) -} - -function withMcpProject(self: (dir: string) => Effect.Effect) { - return Effect.gen(function* () { - const fs = yield* FileSystem.FileSystem - const path = yield* Path.Path - const dir = yield* fs.makeTempDirectoryScoped({ prefix: "opencode-test-" }) +}) - yield* fs.writeFileString( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - formatter: false, - lsp: false, - mcp: { - demo: { - type: "local", - command: ["echo", "demo"], - enabled: false, - }, - }, - }), - ) - yield* Effect.addFinalizer(() => - Effect.promise(() => - WithInstance.provide({ directory: dir, fn: () => InstanceRuntime.disposeInstance(Instance.current) }), - ).pipe(Effect.ignore), - ) - - return yield* self(dir).pipe(provideInstance(dir)) - }) -} +const json = (response: Response) => Effect.promise(() => response.json() as Promise) const readResponse = Effect.fnUntraced(function* (input: { app: TestApp; path: string; headers: HeadersInit }) { const response = yield* Effect.promise(() => @@ -75,14 +63,19 @@ const readResponse = Effect.fnUntraced(function* (input: { app: TestApp; path: s } }) -afterEach(async () => { - await disposeAllInstances() - await resetDatabase() -}) - describe("mcp HttpApi", () => { - test("serves status endpoint", async () => { - await using tmp = await tmpdir({ + it.instance( + "serves status endpoint", + () => + Effect.gen(function* () { + const tmp = yield* TestInstance + const handler = yield* handlerScoped + const response = yield* request(handler, McpPaths.status, tmp.directory) + + expect(response.status).toBe(200) + expect(yield* json(response)).toEqual({ demo: { status: "disabled" } }) + }), + { config: { mcp: { demo: { @@ -92,15 +85,39 @@ describe("mcp HttpApi", () => { }, }, }, - }) + }, + ) - const response = await request(McpPaths.status, tmp.path) - expect(response.status).toBe(200) - expect(await response.json()).toEqual({ demo: { status: "disabled" } }) - }) + it.instance( + "serves add, connect, and disconnect endpoints", + () => + Effect.gen(function* () { + const tmp = yield* TestInstance + const handler = yield* handlerScoped + const added = yield* request(handler, McpPaths.status, tmp.directory, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + name: "added", + config: { + type: "local", + command: ["echo", "added"], + enabled: false, + }, + }), + }) + expect(added.status).toBe(200) + expect(yield* json(added)).toMatchObject({ added: { status: "disabled" } }) - test("serves add, connect, and disconnect endpoints", async () => { - await using tmp = await tmpdir({ + const connected = yield* request(handler, "/mcp/demo/connect", tmp.directory, { method: "POST" }) + expect(connected.status).toBe(200) + expect(yield* json(connected)).toBe(true) + + const disconnected = yield* request(handler, "/mcp/demo/disconnect", tmp.directory, { method: "POST" }) + expect(disconnected.status).toBe(200) + expect(yield* json(disconnected)).toBe(true) + }), + { config: { mcp: { demo: { @@ -110,34 +127,26 @@ describe("mcp HttpApi", () => { }, }, }, - }) - - const added = await request(McpPaths.status, tmp.path, { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify({ - name: "added", - config: { - type: "local", - command: ["echo", "added"], - enabled: false, - }, - }), - }) - expect(added.status).toBe(200) - expect(await added.json()).toMatchObject({ added: { status: "disabled" } }) + }, + ) - const connected = await request("/mcp/demo/connect", tmp.path, { method: "POST" }) - expect(connected.status).toBe(200) - expect(await connected.json()).toBe(true) + it.instance( + "serves deterministic OAuth endpoints", + () => + Effect.gen(function* () { + const tmp = yield* TestInstance + const handler = yield* handlerScoped + const start = yield* request(handler, "/mcp/demo/auth", tmp.directory, { method: "POST" }) + expect(start.status).toBe(400) - const disconnected = await request("/mcp/demo/disconnect", tmp.path, { method: "POST" }) - expect(disconnected.status).toBe(200) - expect(await disconnected.json()).toBe(true) - }) + const authenticate = yield* request(handler, "/mcp/demo/auth/authenticate", tmp.directory, { method: "POST" }) + expect(authenticate.status).toBe(400) - test("serves deterministic OAuth endpoints", async () => { - await using tmp = await tmpdir({ + const removed = yield* request(handler, "/mcp/demo/auth", tmp.directory, { method: "DELETE" }) + expect(removed.status).toBe(200) + expect(yield* json(removed)).toEqual({ success: true }) + }), + { config: { mcp: { demo: { @@ -147,23 +156,15 @@ describe("mcp HttpApi", () => { }, }, }, - }) - - const start = await request("/mcp/demo/auth", tmp.path, { method: "POST" }) - expect(start.status).toBe(400) - - const authenticate = await request("/mcp/demo/auth/authenticate", tmp.path, { method: "POST" }) - expect(authenticate.status).toBe(400) - - const removed = await request("/mcp/demo/auth", tmp.path, { method: "DELETE" }) - expect(removed.status).toBe(200) - expect(await removed.json()).toEqual({ success: true }) - }) + }, + ) - it.live( + it.instance( "returns unsupported OAuth error responses", - withMcpProject((dir) => + () => Effect.gen(function* () { + const tmp = yield* TestInstance + const dir = tmp.directory const headers = { "x-kilo-directory": dir } yield* Effect.forEach(["/mcp/demo/auth", "/mcp/demo/auth/authenticate"], (path) => @@ -177,6 +178,18 @@ describe("mcp HttpApi", () => { }), ) }), - ), + { + config: { + formatter: false, + lsp: false, + mcp: { + demo: { + type: "local", + command: ["echo", "demo"], + enabled: false, + }, + }, + }, + }, ) }) diff --git a/packages/opencode/test/server/httpapi-mdns.test.ts b/packages/opencode/test/server/httpapi-mdns.test.ts new file mode 100644 index 00000000000..a0dd701ad9a --- /dev/null +++ b/packages/opencode/test/server/httpapi-mdns.test.ts @@ -0,0 +1,82 @@ +import { afterEach, describe, expect, mock, test } from "bun:test" +import { Flag } from "@opencode-ai/core/flag/flag" +import * as Log from "@opencode-ai/core/util/log" +import { withTimeout } from "../../src/util/timeout" +import { resetDatabase } from "../fixture/db" +import { disposeAllInstances } from "../fixture/fixture" + +void Log.init({ print: false }) + +type Event = { kind: "publish"; port: number; name: string } | { kind: "unpublishAll" } | { kind: "destroy" } +const events: Event[] = [] + +void mock.module("bonjour-service", () => ({ + Bonjour: class { + publish(opts: { port: number; name: string }) { + events.push({ kind: "publish", port: opts.port, name: opts.name }) + return { on: () => {} } + } + unpublishAll() { + events.push({ kind: "unpublishAll" }) + } + destroy() { + events.push({ kind: "destroy" }) + } + }, +})) + +// Import Server AFTER the mock so the MDNS module picks up the stub. +const { Server } = await import("../../src/server/server") + +const original = { + KILO_SERVER_PASSWORD: Flag.KILO_SERVER_PASSWORD, + KILO_SERVER_USERNAME: Flag.KILO_SERVER_USERNAME, +} + +afterEach(async () => { + events.length = 0 + Flag.KILO_SERVER_PASSWORD = original.KILO_SERVER_PASSWORD + Flag.KILO_SERVER_USERNAME = original.KILO_SERVER_USERNAME + await disposeAllInstances() + await resetDatabase() +}) + +describe("HttpApi Server.listen mDNS", () => { + test("skips publish for loopback hostnames", async () => { + Flag.KILO_SERVER_PASSWORD = "mdns-secret" + Flag.KILO_SERVER_USERNAME = "opencode" + const listener = await Server.listen({ hostname: "127.0.0.1", port: 0, mdns: true }) + try { + expect(events.filter((e) => e.kind === "publish")).toEqual([]) + } finally { + await withTimeout(listener.stop(true), 10_000, "timed out stopping loopback mdns listener") + } + expect(events.filter((e) => e.kind === "publish")).toEqual([]) + }) + + test("publishes for non-loopback hostnames and unpublishes on stop", async () => { + Flag.KILO_SERVER_PASSWORD = "mdns-secret" + Flag.KILO_SERVER_USERNAME = "opencode" + const listener = await Server.listen({ hostname: "0.0.0.0", port: 0, mdns: true }) + try { + const published = events.filter((e) => e.kind === "publish") + expect(published.length).toBe(1) + expect(published[0]!.port).toBe(listener.port) + expect(published[0]!.name).toBe(`kilo-${listener.port}`) // kilocode_change + } finally { + await withTimeout(listener.stop(true), 10_000, "timed out stopping mdns listener") + } + expect(events.some((e) => e.kind === "unpublishAll")).toBe(true) + expect(events.some((e) => e.kind === "destroy")).toBe(true) + }) + + test("scope finalizer unpublishes even if stop() is not called for force-close", async () => { + Flag.KILO_SERVER_PASSWORD = "mdns-secret" + Flag.KILO_SERVER_USERNAME = "opencode" + const listener = await Server.listen({ hostname: "0.0.0.0", port: 0, mdns: true }) + expect(events.filter((e) => e.kind === "publish").length).toBe(1) + // Plain (graceful) stop without close=true should still unpublish. + await withTimeout(listener.stop(), 10_000, "timed out stopping graceful mdns listener") + expect(events.some((e) => e.kind === "unpublishAll")).toBe(true) + }) +}) diff --git a/packages/opencode/test/server/httpapi-promptasync-context.test.ts b/packages/opencode/test/server/httpapi-promptasync-context.test.ts index 6cb36c3bf31..84f3df5b8cd 100644 --- a/packages/opencode/test/server/httpapi-promptasync-context.test.ts +++ b/packages/opencode/test/server/httpapi-promptasync-context.test.ts @@ -8,7 +8,6 @@ // Effect.provideService calls there are required, not defensive duplication. import { NodeHttpServer, NodeServices } from "@effect/platform-node" -import { Flag } from "@opencode-ai/core/flag/flag" import { describe, expect } from "bun:test" import { Deferred, Effect, Layer, Scope } from "effect" import * as Stream from "effect/Stream" @@ -19,24 +18,20 @@ import { registerAdapter } from "../../src/control-plane/adapters" import type { WorkspaceAdapter } from "../../src/control-plane/types" import { Workspace } from "../../src/control-plane/workspace" import { InstanceRef, WorkspaceRef } from "../../src/effect/instance-ref" -import { InstanceBootstrap } from "../../src/project/bootstrap" import { InstanceLayer } from "../../src/project/instance-layer" -import { InstanceStore } from "../../src/project/instance-store" import { Project } from "../../src/project/project" import { instanceRouterMiddleware } from "../../src/server/routes/instance/httpapi/middleware/instance-context" import { workspaceRouterMiddleware } from "../../src/server/routes/instance/httpapi/middleware/workspace-routing" import { resetDatabase } from "../fixture/db" import { disposeAllInstances, tmpdirScoped } from "../fixture/fixture" +import { workspaceLayerWithRuntimeFlags } from "../fixture/workspace" import { testEffect } from "../lib/effect" const testStateLayer = Layer.effectDiscard( Effect.gen(function* () { - const originalWorkspaces = Flag.KILO_EXPERIMENTAL_WORKSPACES yield* Effect.promise(() => resetDatabase()) - Flag.KILO_EXPERIMENTAL_WORKSPACES = true yield* Effect.addFinalizer(() => Effect.promise(async () => { - Flag.KILO_EXPERIMENTAL_WORKSPACES = originalWorkspaces await disposeAllInstances() await resetDatabase() }), @@ -44,10 +39,7 @@ const testStateLayer = Layer.effectDiscard( }), ) -const workspaceLayer = Workspace.defaultLayer.pipe( - Layer.provide(InstanceStore.defaultLayer), - Layer.provide(InstanceBootstrap.defaultLayer), -) +const workspaceLayer = workspaceLayerWithRuntimeFlags({ experimentalWorkspaces: true }) const it = testEffect( Layer.mergeAll( diff --git a/packages/opencode/test/server/httpapi-provider.test.ts b/packages/opencode/test/server/httpapi-provider.test.ts index b0ec5894ce3..9849a650bfc 100644 --- a/packages/opencode/test/server/httpapi-provider.test.ts +++ b/packages/opencode/test/server/httpapi-provider.test.ts @@ -1,19 +1,24 @@ -import { afterEach, describe, expect } from "bun:test" -import { Effect, FileSystem, Layer, Path } from "effect" -import { NodeFileSystem, NodePath } from "@effect/platform-node" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" -import { InstanceRuntime } from "../../src/project/instance-runtime" +import { describe, expect } from "bun:test" +import { AppFileSystem } from "@opencode-ai/core/filesystem" +import { Effect, Layer } from "effect" +import path from "path" import { Server } from "../../src/server/server" import * as Log from "@opencode-ai/core/util/log" import { resetDatabase } from "../fixture/db" -import { disposeAllInstances, provideInstance } from "../fixture/fixture" +import { TestInstance } from "../fixture/fixture" import { testEffect } from "../lib/effect" void Log.init({ print: false }) -const it = testEffect(Layer.mergeAll(NodeFileSystem.layer, NodePath.layer)) -const describeProvider = process.platform === "win32" ? describe.skip : describe // kilocode_change - scoped temp cleanup is flaky on Windows CI +const testStateLayer = Layer.effectDiscard( + Effect.acquireRelease( + Effect.promise(() => resetDatabase()), + () => Effect.promise(() => resetDatabase()), + ), +) + +const it = testEffect(Layer.mergeAll(testStateLayer, AppFileSystem.defaultLayer)) +const projectOptions = { config: { formatter: false, lsp: false } } const providerID = "test-oauth-parity" const oauthURL = "https://example.com/oauth" const oauthInstructions = "Finish OAuth" @@ -75,12 +80,33 @@ function requestAuthorize(input: { providerID: string method: number headers: HeadersInit + inputs?: Record }) { return Effect.promise(async () => { const response = await input.app.request(`/provider/${input.providerID}/oauth/authorize`, { method: "POST", headers: input.headers, - body: JSON.stringify({ method: input.method }), + body: JSON.stringify({ method: input.method, ...(input.inputs ? { inputs: input.inputs } : {}) }), + }) + return { + status: response.status, + body: await response.text(), + } + }) +} + +function requestCallback(input: { + app: ReturnType + providerID: string + method: number + headers: HeadersInit + code?: string +}) { + return Effect.promise(async () => { + const response = await input.app.request(`/provider/${input.providerID}/oauth/callback`, { + method: "POST", + headers: input.headers, + body: JSON.stringify({ method: input.method, ...(input.code ? { code: input.code } : {}) }), }) return { status: response.status, @@ -91,11 +117,9 @@ function requestAuthorize(input: { function writeProviderAuthPlugin(dir: string) { return Effect.gen(function* () { - const fs = yield* FileSystem.FileSystem - const path = yield* Path.Path + const fs = yield* AppFileSystem.Service - yield* fs.makeDirectory(path.join(dir, ".opencode", "plugin"), { recursive: true }) - yield* fs.writeFileString( + yield* fs.writeWithDirs( path.join(dir, ".opencode", "plugin", "provider-oauth-parity.ts"), [ "export default {", @@ -125,13 +149,52 @@ function writeProviderAuthPlugin(dir: string) { }) } +function writeProviderAuthValidationPlugin(dir: string) { + return Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + + yield* fs.writeWithDirs( + path.join(dir, ".opencode", "plugin", "provider-oauth-validation.ts"), + [ + "export default {", + ' id: "test.provider-oauth-validation",', + " server: async () => ({", + " auth: {", + ' provider: "test-oauth-validation",', + " methods: [", + " {", + ' type: "oauth",', + ' label: "OAuth",', + " prompts: [", + " {", + ' type: "text",', + ' key: "token",', + ' message: "Token",', + " validate: (value) => value === 'ok' ? undefined : 'Token must be ok',", + " },", + " ],", + " authorize: async () => ({", + ` url: "${oauthURL}",`, + ' method: "code",', + ` instructions: "${oauthInstructions}",`, + " callback: async () => ({ type: 'success', key: 'token' }),", + " }),", + " },", + " ],", + " },", + " }),", + "}", + "", + ].join("\n"), + ) + }) +} + function writeFunctionOptionsPlugin(dir: string) { return Effect.gen(function* () { - const fs = yield* FileSystem.FileSystem - const path = yield* Path.Path + const fs = yield* AppFileSystem.Service - yield* fs.makeDirectory(path.join(dir, ".opencode", "plugin"), { recursive: true }) - yield* fs.writeFileString( + yield* fs.writeWithDirs( path.join(dir, ".opencode", "plugin", "provider-function-options.ts"), [ "export default {", @@ -160,11 +223,9 @@ function writeFunctionOptionsPlugin(dir: string) { function writeProviderModelsMutationPlugin(dir: string) { return Effect.gen(function* () { - const fs = yield* FileSystem.FileSystem - const path = yield* Path.Path + const fs = yield* AppFileSystem.Service - yield* fs.makeDirectory(path.join(dir, ".opencode", "plugin"), { recursive: true }) - yield* fs.writeFileString( + yield* fs.writeWithDirs( path.join(dir, ".opencode", "plugin", "provider-models-mutation.ts"), [ "export default {", @@ -192,92 +253,114 @@ function writeProviderModelsMutationPlugin(dir: string) { }) } -function withProviderProject(self: (dir: string) => Effect.Effect) { - return Effect.gen(function* () { - const fs = yield* FileSystem.FileSystem - const path = yield* Path.Path - const dir = yield* fs.makeTempDirectoryScoped({ prefix: "opencode-test-" }) +function setEnvScoped(key: string, value: string) { + return Effect.acquireRelease( + Effect.sync(() => { + const previous = process.env[key] + process.env[key] = value + return previous + }), + (previous) => + Effect.sync(() => { + if (previous === undefined) delete process.env[key] + else process.env[key] = previous + }), + ) +} - yield* fs.writeFileString( - path.join(dir, "opencode.json"), - JSON.stringify({ $schema: "https://opencode.ai/config.json", formatter: false, lsp: false }), - ) - yield* writeProviderAuthPlugin(dir) - yield* Effect.addFinalizer(() => - Effect.promise(() => - WithInstance.provide({ directory: dir, fn: () => InstanceRuntime.disposeInstance(Instance.current) }), - ).pipe(Effect.ignore), - ) +describe("provider HttpApi", () => { + it.instance( + "serves OAuth authorize response shapes", + Effect.gen(function* () { + const instance = yield* TestInstance + yield* writeProviderAuthPlugin(instance.directory) + const headers = { "x-kilo-directory": instance.directory, "content-type": "application/json" } + const server = app() + + const api = yield* requestAuthorize({ + app: server, + providerID, + method: 0, + headers, + }) + // method 0 (api-key style) — authorize() resolves with no further + // redirect; #26474 changed the wire format to JSON `null` so clients + // can `.json()` parse uniformly instead of getting an empty body + // that throws. + expect(api).toEqual({ status: 200, body: "null" }) + + const oauth = yield* requestAuthorize({ + app: server, + providerID, + method: 1, + headers, + }) + expect(JSON.parse(oauth.body)).toEqual({ + url: oauthURL, + method: "code", + instructions: oauthInstructions, + }) + }), + projectOptions, + 30000, + ) - return yield* self(dir).pipe(provideInstance(dir)) - }) -} + it.instance( + "returns declared provider auth validation errors", + Effect.gen(function* () { + const instance = yield* TestInstance + yield* writeProviderAuthValidationPlugin(instance.directory) + const response = yield* requestAuthorize({ + app: app(), + providerID: "test-oauth-validation", + method: 0, + inputs: { token: "nope" }, + headers: { "x-kilo-directory": instance.directory, "content-type": "application/json" }, + }) -afterEach(async () => { - await disposeAllInstances() - await resetDatabase() -}) + expect(response.status).toBe(400) + expect(JSON.parse(response.body)).toEqual({ + name: "ProviderAuthValidationFailed", + data: { field: "token", message: "Token must be ok" }, + }) + }), + projectOptions, + 30000, + ) -describeProvider("provider HttpApi", () => { - // kilocode_change - it.live( - "serves OAuth authorize response shapes", - withProviderProject((dir) => - Effect.gen(function* () { - const headers = { "x-kilo-directory": dir, "content-type": "application/json" } - const server = app() - - const api = yield* requestAuthorize({ - app: server, - providerID, - method: 0, - headers, - }) - // method 0 (api-key style) — authorize() resolves with no further - // redirect; #26474 changed the wire format to JSON `null` so clients - // can `.json()` parse uniformly instead of getting an empty body - // that throws. - expect(api).toEqual({ status: 200, body: "null" }) - - const oauth = yield* requestAuthorize({ - app: server, - providerID, - method: 1, - headers, - }) - expect(JSON.parse(oauth.body)).toEqual({ - url: oauthURL, - method: "code", - instructions: oauthInstructions, - }) - }), - ), + it.instance( + "returns declared provider auth callback errors", + Effect.gen(function* () { + const instance = yield* TestInstance + const response = yield* requestCallback({ + app: app(), + providerID, + method: 0, + headers: { "x-kilo-directory": instance.directory, "content-type": "application/json" }, + }) + + expect(response.status).toBe(400) + expect(JSON.parse(response.body)).toEqual({ + name: "ProviderAuthOauthMissing", + data: { providerID }, + }) + }), + projectOptions, + 30000, ) - it.live("serves provider lists when auth loaders add runtime fetch options", () => + it.instance( + "serves provider lists when auth loaders add runtime fetch options", Effect.gen(function* () { - const fs = yield* FileSystem.FileSystem - const path = yield* Path.Path - const dir = yield* fs.makeTempDirectoryScoped({ prefix: "opencode-test-" }) - const previous = process.env.KILO_AUTH_CONTENT - - yield* fs.writeFileString( - path.join(dir, "opencode.json"), - JSON.stringify({ $schema: "https://opencode.ai/config.json", formatter: false, lsp: false }), - ) - yield* writeFunctionOptionsPlugin(dir) - yield* Effect.sync(() => { - process.env.KILO_AUTH_CONTENT = JSON.stringify({ + const instance = yield* TestInstance + yield* writeFunctionOptionsPlugin(instance.directory) + yield* setEnvScoped( + "KILO_AUTH_CONTENT", + JSON.stringify({ google: { type: "oauth", refresh: "dummy", access: "dummy", expires: 9999999999999 }, - }) - }) - yield* Effect.addFinalizer(() => - Effect.sync(() => { - if (previous === undefined) delete process.env.KILO_AUTH_CONTENT - if (previous !== undefined) process.env.KILO_AUTH_CONTENT = previous }), ) - const headers = { "x-kilo-directory": dir } + const headers = { "x-kilo-directory": instance.directory } const providerResponse = yield* Effect.promise(() => Promise.resolve(app().request("/provider", { headers }))) const configResponse = yield* Effect.promise(() => Promise.resolve(app().request("/config/providers", { headers })), @@ -293,21 +376,16 @@ describeProvider("provider HttpApi", () => { expect(hasNonZeroModelCost(providerBody, "all", "google")).toBe(true) expect(hasNonZeroModelCost(configBody, "providers", "google")).toBe(true) }), + projectOptions, ) - it.live("keeps provider.models hook input mutations out of provider state", () => + it.instance( + "keeps provider.models hook input mutations out of provider state", Effect.gen(function* () { - const fs = yield* FileSystem.FileSystem - const path = yield* Path.Path - const dir = yield* fs.makeTempDirectoryScoped({ prefix: "opencode-test-" }) - - yield* fs.writeFileString( - path.join(dir, "opencode.json"), - JSON.stringify({ $schema: "https://opencode.ai/config.json", formatter: false, lsp: false }), - ) - yield* writeProviderModelsMutationPlugin(dir) + const instance = yield* TestInstance + yield* writeProviderModelsMutationPlugin(instance.directory) - const headers = { "x-kilo-directory": dir } + const headers = { "x-kilo-directory": instance.directory } const providerResponse = yield* Effect.promise(() => Promise.resolve(app().request("/provider", { headers }))) const configResponse = yield* Effect.promise(() => Promise.resolve(app().request("/config/providers", { headers })), @@ -322,5 +400,6 @@ describeProvider("provider HttpApi", () => { expect(hasProviderMutationMarker(configBody, "providers", "google")).toBe(false) expect(hasNonZeroModelCost(providerBody, "all", "google")).toBe(true) }), + projectOptions, ) }) diff --git a/packages/opencode/test/server/httpapi-pty-websocket.test.ts b/packages/opencode/test/server/httpapi-pty-websocket.test.ts index 81ee952d968..19d97ef09c4 100644 --- a/packages/opencode/test/server/httpapi-pty-websocket.test.ts +++ b/packages/opencode/test/server/httpapi-pty-websocket.test.ts @@ -1,16 +1,19 @@ -import { describe, expect, test } from "bun:test" +import { describe, expect } from "bun:test" import { Effect } from "effect" import { handlePtyInput } from "../../src/pty/input" +import { it } from "../lib/effect" describe("pty HttpApi websocket input", () => { - test("does not forward invalid binary frames to the PTY handler", async () => { - const messages: Array = [] - const handler = { onMessage: (message: string | ArrayBuffer) => messages.push(message) } + it.effect("does not forward invalid binary frames to the PTY handler", () => + Effect.gen(function* () { + const messages: Array = [] + const handler = { onMessage: (message: string | ArrayBuffer) => messages.push(message) } - await Effect.runPromise(handlePtyInput(handler, "ready")) - await Effect.runPromise(handlePtyInput(handler, new Uint8Array([0xff, 0xfe, 0xfd]))) - await Effect.runPromise(handlePtyInput(handler, new TextEncoder().encode("hello"))) + yield* handlePtyInput(handler, "ready") + yield* handlePtyInput(handler, new Uint8Array([0xff, 0xfe, 0xfd])) + yield* handlePtyInput(handler, new TextEncoder().encode("hello")) - expect(messages).toEqual(["ready", "hello"]) - }) + expect(messages).toEqual(["ready", "hello"]) + }), + ) }) diff --git a/packages/opencode/test/server/httpapi-pty.test.ts b/packages/opencode/test/server/httpapi-pty.test.ts index f4b5ddcaebb..62758727a5a 100644 --- a/packages/opencode/test/server/httpapi-pty.test.ts +++ b/packages/opencode/test/server/httpapi-pty.test.ts @@ -10,7 +10,7 @@ import { disposeAllInstances, tmpdir, tmpdirScoped } from "../fixture/fixture" import { Config, Effect, Layer, Queue, Schema } from "effect" import { HttpClient, HttpClientRequest, HttpRouter, HttpServer } from "effect/unstable/http" import * as Socket from "effect/unstable/socket/Socket" -import { ExperimentalHttpApiServer } from "../../src/server/routes/instance/httpapi/server" +import { HttpApiApp } from "../../src/server/routes/instance/httpapi/server" import { Pty } from "../../src/pty" import { testEffect } from "../lib/effect" @@ -30,7 +30,7 @@ const testStateLayer = Layer.effectDiscard( ) const servedRoutes: Layer.Layer = HttpRouter.serve( - ExperimentalHttpApiServer.routes, + HttpApiApp.routes, { disableListenLog: true, disableLogger: true }, ) diff --git a/packages/opencode/test/server/httpapi-raw-route-auth.test.ts b/packages/opencode/test/server/httpapi-raw-route-auth.test.ts index 5464cd8cb57..102bcc8f50a 100644 --- a/packages/opencode/test/server/httpapi-raw-route-auth.test.ts +++ b/packages/opencode/test/server/httpapi-raw-route-auth.test.ts @@ -2,9 +2,9 @@ import { afterEach, describe, expect, test } from "bun:test" import { ConfigProvider, Layer } from "effect" import { HttpRouter } from "effect/unstable/http" import { Instance } from "../../src/project/instance" -import { EventPaths } from "../../src/server/routes/instance/httpapi/event" +import { EventPaths } from "../../src/server/routes/instance/httpapi/groups/event" import { PtyPaths } from "../../src/server/routes/instance/httpapi/groups/pty" -import { ExperimentalHttpApiServer } from "../../src/server/routes/instance/httpapi/server" +import { HttpApiApp } from "../../src/server/routes/instance/httpapi/server" import { PtyID } from "../../src/pty/schema" import { resetDatabase } from "../fixture/db" import { disposeAllInstances, tmpdir } from "../fixture/fixture" @@ -14,7 +14,7 @@ void Log.init({ print: false }) function app(input: { password?: string; username?: string }) { const handler = HttpRouter.toWebHandler( - ExperimentalHttpApiServer.routes.pipe( + HttpApiApp.routes.pipe( Layer.provide( ConfigProvider.layer( ConfigProvider.fromUnknown({ @@ -28,7 +28,7 @@ function app(input: { password?: string; username?: string }) { ).handler return { - fetch: (request: Request) => handler(request, ExperimentalHttpApiServer.context), + fetch: (request: Request) => handler(request, HttpApiApp.context), request(input: string | URL | Request, init?: RequestInit) { return this.fetch(input instanceof Request ? input : new Request(new URL(input, "http://localhost"), init)) }, diff --git a/packages/opencode/test/server/httpapi-schema-error-body.test.ts b/packages/opencode/test/server/httpapi-schema-error-body.test.ts index 32165290a64..2fff9675ba2 100644 --- a/packages/opencode/test/server/httpapi-schema-error-body.test.ts +++ b/packages/opencode/test/server/httpapi-schema-error-body.test.ts @@ -3,7 +3,6 @@ import { Effect } from "effect" import { eq } from "drizzle-orm" import * as Database from "@/storage/db" import { ModelID, ProviderID } from "../../src/provider/schema" -import { WithInstance } from "../../src/project/with-instance" import { Server } from "../../src/server/server" import { Session } from "@/session/session" import { SessionPaths } from "../../src/server/routes/instance/httpapi/groups/session" @@ -11,80 +10,68 @@ import { SyncPaths } from "../../src/server/routes/instance/httpapi/groups/sync" import { MessageID, PartID } from "../../src/session/schema" import { PartTable } from "@/session/session.sql" import { resetDatabase } from "../fixture/db" -import { disposeAllInstances, tmpdir } from "../fixture/fixture" -import { it } from "../lib/effect" +import { disposeAllInstances, TestInstance } from "../fixture/fixture" +import { testEffect } from "../lib/effect" + +const it = testEffect(Session.defaultLayer) afterEach(async () => { await disposeAllInstances() await resetDatabase() }) -const withTmp = ( - options: Parameters[0], - fn: (tmp: Awaited>) => Effect.Effect, -) => - Effect.acquireRelease( - Effect.promise(() => tmpdir(options)), - (tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()), - ).pipe(Effect.flatMap(fn)) - -async function seedCorruptStepFinishPart(directory: string) { - return WithInstance.provide({ - directory, - fn: () => - Effect.runPromise( - Effect.gen(function* () { - const session = yield* Session.Service - const info = yield* session.create({}) - const message = yield* session.updateMessage({ - id: MessageID.ascending(), - role: "user", - sessionID: info.id, - agent: "build", - model: { providerID: ProviderID.make("test"), modelID: ModelID.make("test") }, - time: { created: Date.now() }, - }) - const partID = PartID.ascending() - yield* session.updatePart({ - id: partID, - sessionID: info.id, - messageID: message.id, +const seedCorruptStepFinishPart = Effect.gen(function* () { + const session = yield* Session.Service + const info = yield* session.create({}) + const message = yield* session.updateMessage({ + id: MessageID.ascending(), + role: "user", + sessionID: info.id, + agent: "build", + model: { providerID: ProviderID.make("test"), modelID: ModelID.make("test") }, + time: { created: Date.now() }, + }) + const partID = PartID.ascending() + yield* session.updatePart({ + id: partID, + sessionID: info.id, + messageID: message.id, + type: "step-finish", + reason: "stop", + cost: 0, + tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }, + }) + // Schema.Finite still rejects NaN at encode: exact mirror of the corrupt row + // that broke the user's session in the OMO/Windows bug. + yield* Effect.sync(() => + Database.use((db) => + db + .update(PartTable) + .set({ + data: { type: "step-finish", reason: "stop", cost: 0, - tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }, - }) - // Schema.Finite still rejects NaN at encode — exact mirror of the - // corrupt row that broke the user's session in the OMO/Windows bug. - Database.use((db) => - db - .update(PartTable) - .set({ - data: { - type: "step-finish", - reason: "stop", - cost: 0, - tokens: { input: 0, output: NaN, reasoning: 0, cache: { read: 0, write: 0 } }, - } as never, // drizzle's .set() can't narrow the discriminated union - }) - .where(eq(PartTable.id, partID)) - .run(), - ) - return info.id - }).pipe(Effect.provide(Session.defaultLayer)), - ), - }) -} + tokens: { input: 0, output: NaN, reasoning: 0, cache: { read: 0, write: 0 } }, + } as never, // drizzle's .set() can't narrow the discriminated union + }) + .where(eq(PartTable.id, partID)) + .run(), + ), + ) + return info.id +}) describe("schema-rejection wire shape", () => { - it.live( + it.instance( "Payload schema rejection returns NamedError-shaped JSON, not empty", - withTmp({ git: true, config: { formatter: false, lsp: false } }, (tmp) => + () => Effect.gen(function* () { + const test = yield* TestInstance const res = yield* Effect.promise(async () => Server.Default().app.request(SyncPaths.history, { method: "POST", - headers: { "x-kilo-directory": tmp.path, "content-type": "application/json" }, + headers: { "x-kilo-directory": test.directory, "content-type": "application/json" }, body: JSON.stringify({ aggregate: -1 }), }), ) @@ -99,36 +86,38 @@ describe("schema-rejection wire shape", () => { expect(parsed.data.message).toEqual(expect.any(String)) expect(parsed.data.message.length).toBeGreaterThan(0) }), - ), + { git: true, config: { formatter: false, lsp: false } }, ) - it.live( + it.instance( "Query schema rejection returns NamedError-shaped JSON", - withTmp({ git: true, config: { formatter: false, lsp: false } }, (tmp) => + () => Effect.gen(function* () { + const test = yield* TestInstance // /find/file?limit=999999 violates the limit constraint check. - const url = `/find/file?query=foo&limit=999999&directory=${encodeURIComponent(tmp.path)}` + const url = `/find/file?query=foo&limit=999999&directory=${encodeURIComponent(test.directory)}` const res = yield* Effect.promise(async () => Server.Default().app.request(url)) const body = yield* Effect.promise(async () => res.text()) expect(res.status).toBe(400) const parsed = JSON.parse(body) expect(parsed).toMatchObject({ name: "BadRequest", data: { kind: "Query" } }) }), - ), + { git: true, config: { formatter: false, lsp: false } }, ) - it.live( + it.instance( "rejected request body never echoes back unbounded — message is capped", // Defense against DoS-amplification + secret-echo: Effect's Issue formatter // dumps the rejected `actual` verbatim. A multi-MB invalid array would // become a multi-MB 400 response and log line. Cap kicks in around 1KB. - withTmp({ git: true, config: { formatter: false, lsp: false } }, (tmp) => + () => Effect.gen(function* () { + const test = yield* TestInstance const huge = "X".repeat(50_000) const res = yield* Effect.promise(async () => Server.Default().app.request(SyncPaths.history, { method: "POST", - headers: { "x-kilo-directory": tmp.path, "content-type": "application/json" }, + headers: { "x-kilo-directory": test.directory, "content-type": "application/json" }, body: JSON.stringify({ aggregate: huge }), }), ) @@ -139,15 +128,16 @@ describe("schema-rejection wire shape", () => { const parsed = JSON.parse(body) expect(parsed.data.message).not.toContain(huge) }), - ), + { git: true, config: { formatter: false, lsp: false } }, ) - it.live( + it.instance( "response-encode failure: corrupted stored row returns NamedError-shaped JSON with field path", - withTmp({ config: { formatter: false, lsp: false } }, (tmp) => + () => Effect.gen(function* () { - const sessionID = yield* Effect.promise(() => seedCorruptStepFinishPart(tmp.path)) - const url = `${SessionPaths.messages.replace(":sessionID", sessionID)}?limit=80&directory=${encodeURIComponent(tmp.path)}` + const test = yield* TestInstance + const sessionID = yield* seedCorruptStepFinishPart + const url = `${SessionPaths.messages.replace(":sessionID", sessionID)}?limit=80&directory=${encodeURIComponent(test.directory)}` const res = yield* Effect.promise(async () => Server.Default().app.request(url)) const body = yield* Effect.promise(async () => res.text()) expect(res.status).toBe(400) @@ -157,6 +147,6 @@ describe("schema-rejection wire shape", () => { // Field path in data.message — what made this PR worth shipping. expect(parsed.data.message).toMatch(/output/) }), - ), + { config: { formatter: false, lsp: false } }, ) }) diff --git a/packages/opencode/test/server/httpapi-sdk.test.ts b/packages/opencode/test/server/httpapi-sdk.test.ts index ce2d57eb046..a23c2017b41 100644 --- a/packages/opencode/test/server/httpapi-sdk.test.ts +++ b/packages/opencode/test/server/httpapi-sdk.test.ts @@ -2,12 +2,15 @@ import { afterEach, describe, expect } from "bun:test" import { ConfigProvider, Effect, Layer } from "effect" import type * as Scope from "effect/Scope" import { HttpRouter } from "effect/unstable/http" +import { ChildProcessSpawner } from "effect/unstable/process" +import { AppFileSystem } from "@opencode-ai/core/filesystem" +import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { Flag } from "@opencode-ai/core/flag/flag" import { createKiloClient } from "@kilocode/sdk/v2" import { validateSession } from "../../src/cli/cmd/tui/validate-session" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" -import { ExperimentalHttpApiServer } from "../../src/server/routes/instance/httpapi/server" +import { InstanceBootstrap } from "../../src/project/bootstrap-service" +import { InstanceStore } from "../../src/project/instance-store" +import { HttpApiApp } from "../../src/server/routes/instance/httpapi/server" import { Server } from "../../src/server/server" import { MessageID, PartID, SessionID } from "../../src/session/schema" import { MessageV2 } from "../../src/session/message-v2" @@ -18,8 +21,17 @@ import { errorMessage } from "../../src/util/error" import { TestLLMServer } from "../lib/llm-server" import path from "path" import { resetDatabase } from "../fixture/db" -import { disposeAllInstances, tmpdir } from "../fixture/fixture" -import { it } from "../lib/effect" +import { disposeAllInstances, TestInstance, tmpdirScoped } from "../fixture/fixture" +import { testEffect } from "../lib/effect" + +const noopBootstrap = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void })) +const it = testEffect( + Layer.mergeAll( + AppFileSystem.defaultLayer, + CrossSpawnSpawner.defaultLayer, + InstanceStore.defaultLayer.pipe(Layer.provide(noopBootstrap)), + ), +) const original = { KILO_SERVER_PASSWORD: Flag.KILO_SERVER_PASSWORD, @@ -32,6 +44,8 @@ type SdkResult = { response: Response; data?: unknown; error?: unknown } type Captured = { status: number; data?: unknown; error?: unknown } type ProjectFixture = { sdk: Sdk; directory: string } type LlmProjectFixture = ProjectFixture & { llm: TestLLMServer["Service"] } +type TestServices = AppFileSystem.Service | ChildProcessSpawner.ChildProcessSpawner | InstanceStore.Service +type TestScope = Scope.Scope | TestServices function app(serverPath: ServerPath, input?: { password?: string; username?: string }) { Flag.KILO_SERVER_PASSWORD = input?.password @@ -39,7 +53,7 @@ function app(serverPath: ServerPath, input?: { password?: string; username?: str if (serverPath === "default") return Server.Default().app const handler = HttpRouter.toWebHandler( - ExperimentalHttpApiServer.routes.pipe( + HttpApiApp.routes.pipe( Layer.provide( ConfigProvider.layer( ConfigProvider.fromUnknown({ @@ -52,7 +66,7 @@ function app(serverPath: ServerPath, input?: { password?: string; username?: str { disableLogger: true }, ).handler return { - fetch: (request: Request) => handler(request, ExperimentalHttpApiServer.context), + fetch: (request: Request) => handler(request, HttpApiApp.context), request(input: string | URL | Request, init?: RequestInit) { return this.fetch(input instanceof Request ? input : new Request(new URL(input, "http://localhost"), init)) }, @@ -149,12 +163,27 @@ function expectStatus(request: () => Promise<{ response: Response }>, status: nu ) } -function firstEvent(open: () => Promise<{ stream: AsyncIterator }>) { - return Effect.acquireRelease(call(open), (events) => - call(async () => void (await events.stream.return?.(undefined))).pipe(Effect.ignore), +function firstEvent(open: (signal: AbortSignal) => Promise<{ stream: AsyncIterator }>) { + return Effect.acquireRelease( + Effect.sync(() => new AbortController()), + (controller) => Effect.sync(() => controller.abort()), ).pipe( - Effect.flatMap((events) => call(() => events.stream.next())), - Effect.map((result) => result.value), + Effect.flatMap((controller) => + Effect.acquireRelease( + call(() => open(controller.signal)), + (events) => call(async () => void (await events.stream.return?.(undefined))).pipe(Effect.ignore), + ).pipe( + Effect.flatMap((events) => + call(() => events.stream.next()).pipe( + Effect.timeoutOrElse({ + duration: "1 second", + orElse: () => Effect.fail(new Error("timed out waiting for SDK event")), + }), + ), + ), + Effect.map((result) => result.value), + ), + ), ) } @@ -188,11 +217,32 @@ function resetState() { }) } -function httpapi(name: string, effect: Effect.Effect) { +function httpapi(name: string, effect: Effect.Effect) { it.live(name, effect) } -function serverPathParity(name: string, scenario: (serverPath: ServerPath) => Effect.Effect) { +function httpapiInstance( + name: string, + options: { + serverPath: ServerPath + git?: boolean + config?: Partial + setup?: (dir: string) => Effect.Effect + }, + run: (input: ProjectFixture) => Effect.Effect, +) { + it.instance( + name, + Effect.gen(function* () { + const instance = yield* TestInstance + yield* options.setup?.(instance.directory) ?? Effect.void + return yield* run({ sdk: client(options.serverPath, instance.directory), directory: instance.directory }) + }), + { git: options.git ?? true, config: { formatter: false, lsp: false, ...options.config } }, + ) +} + +function serverPathParity(name: string, scenario: (serverPath: ServerPath) => Effect.Effect) { it.live( name, Effect.gen(function* () { @@ -204,35 +254,43 @@ function serverPathParity(name: string, scenario: (serverPath: ServerPath) ) } -function withProject( +function withProject( serverPath: ServerPath, - options: { git?: boolean; config?: Partial; setup?: (dir: string) => Effect.Effect }, - run: (input: ProjectFixture) => Effect.Effect, + options: { + git?: boolean + config?: Partial + setup?: (dir: string) => Effect.Effect + }, + run: (input: ProjectFixture) => Effect.Effect, ) { - return Effect.acquireRelease( - call(() => tmpdir({ git: options.git ?? true, config: { formatter: false, lsp: false, ...options.config } })), - (tmp) => call(() => tmp[Symbol.asyncDispose]()).pipe(Effect.ignore), - ).pipe( - Effect.tap((tmp) => options.setup?.(tmp.path) ?? Effect.void), - Effect.flatMap((tmp) => run({ sdk: client(serverPath, tmp.path), directory: tmp.path })), - ) + return Effect.gen(function* () { + const directory = yield* tmpdirScoped({ + git: options.git ?? true, + config: { formatter: false, lsp: false, ...options.config }, + }) + yield* options.setup?.(directory) ?? Effect.void + return yield* run({ sdk: client(serverPath, directory), directory }) + }) } -function withStandardProject(serverPath: ServerPath, run: (input: ProjectFixture) => Effect.Effect) { +function withStandardProject( + serverPath: ServerPath, + run: (input: ProjectFixture) => Effect.Effect, +) { return withProject(serverPath, { setup: writeStandardFiles }, run) } -function withFakeLlm(serverPath: ServerPath, run: (input: LlmProjectFixture) => Effect.Effect) { +function withFakeLlm(serverPath: ServerPath, run: (input: LlmProjectFixture) => Effect.Effect) { return Effect.gen(function* () { const llm = yield* TestLLMServer return yield* withProject(serverPath, { config: providerConfig(llm.url) }, (input) => run({ ...input, llm })) }).pipe(Effect.provide(TestLLMServer.layer)) } -function withFakeLlmProject( +function withFakeLlmProject( serverPath: ServerPath, - options: { setup?: (dir: string) => Effect.Effect }, - run: (input: LlmProjectFixture) => Effect.Effect, + options: { setup?: (dir: string) => Effect.Effect }, + run: (input: LlmProjectFixture) => Effect.Effect, ) { return Effect.gen(function* () { const llm = yield* TestLLMServer @@ -248,15 +306,17 @@ function withFakeLlmProject( } function writeStandardFiles(dir: string) { - return Effect.all([ - call(() => Bun.write(path.join(dir, "hello.txt"), "hello")), - call(() => Bun.write(path.join(dir, "needle.ts"), "export const needle = 'sdk-parity'\n")), - ]).pipe(Effect.asVoid) + return AppFileSystem.Service.use((fs) => + Effect.all([ + fs.writeWithDirs(path.join(dir, "hello.txt"), "hello"), + fs.writeWithDirs(path.join(dir, "needle.ts"), "export const needle = 'sdk-parity'\n"), + ]).pipe(Effect.asVoid), + ) } function writeProjectSkill(dir: string) { - return call(() => - Bun.write( + return AppFileSystem.Service.use((fs) => + fs.writeWithDirs( path.join(dir, ".opencode", "skills", "project-rest-skill", "SKILL.md"), `--- name: project-rest-skill @@ -266,40 +326,36 @@ description: A project skill visible to REST API prompts. # Project REST Skill `, ), - ).pipe(Effect.asVoid) + ) } function seedMessage(directory: string, sessionID: string) { const id = SessionID.make(sessionID) - return call( - async () => - await WithInstance.provide({ - directory, - fn: () => - Effect.runPromise( - SessionNs.Service.use((svc) => - Effect.gen(function* () { - const message = yield* svc.updateMessage({ - id: MessageID.ascending(), - sessionID: id, - role: "user", - time: { created: Date.now() }, - agent: "test", - model: { providerID: ProviderID.make("test"), modelID: ModelID.make("test") }, - tools: {}, - } satisfies MessageV2.User) - const part = yield* svc.updatePart({ - id: PartID.ascending(), - sessionID: id, - messageID: message.id, - type: "text", - text: "seeded message", - }) - return { message, part } - }), - ).pipe(Effect.provide(SessionNs.defaultLayer)), - ), - }), + return InstanceStore.Service.use((store) => + store.provide( + { directory }, + SessionNs.Service.use((svc) => + Effect.gen(function* () { + const message = yield* svc.updateMessage({ + id: MessageID.ascending(), + sessionID: id, + role: "user", + time: { created: Date.now() }, + agent: "test", + model: { providerID: ProviderID.make("test"), modelID: ModelID.make("test") }, + tools: {}, + } satisfies MessageV2.User) + const part = yield* svc.updatePart({ + id: PartID.ascending(), + sessionID: id, + messageID: message.id, + type: "text", + text: "seeded message", + }) + return { message, part } + }), + ).pipe(Effect.provide(SessionNs.defaultLayer)), + ), ) } @@ -320,7 +376,7 @@ describe("HttpApi SDK", () => { expect(health.response.status).toBe(200) expect(health.data).toMatchObject({ healthy: true }) - expect(yield* firstEvent(() => sdk.global.event({ signal: AbortSignal.timeout(1_000) }))).toMatchObject({ + expect(yield* firstEvent((signal) => sdk.global.event({ signal }))).toMatchObject({ payload: { type: "server.connected" }, }) expect(log.response.status).toBe(200) @@ -329,9 +385,10 @@ describe("HttpApi SDK", () => { }), ) - httpapi( + httpapiInstance( "uses the generated SDK for safe instance routes", - withProject("raw", { git: false, setup: writeStandardFiles }, ({ sdk }) => + { serverPath: "raw", git: false, setup: writeStandardFiles }, + ({ sdk }) => Effect.gen(function* () { const file = yield* call(() => sdk.file.read({ path: "hello.txt" })) const session = yield* call(() => sdk.session.create({ title: "sdk" })) @@ -351,7 +408,6 @@ describe("HttpApi SDK", () => { expectStatus(() => sdk.find.files({ query: "hello", limit: 10 }), 200), ]) }), - ), ) serverPathParity("matches generated SDK global and control behavior", (serverPath) => @@ -370,14 +426,14 @@ describe("HttpApi SDK", () => { ) serverPathParity("matches generated SDK global event stream", (serverPath) => - firstEvent(() => client(serverPath).global.event({ signal: AbortSignal.timeout(1_000) })).pipe( + firstEvent((signal) => client(serverPath).global.event({ signal })).pipe( Effect.map((event) => ({ type: record(record(event).payload).type })), ), ) serverPathParity("matches generated SDK instance event stream", (serverPath) => withStandardProject(serverPath, ({ sdk }) => - firstEvent(() => sdk.event.subscribe(undefined, { signal: AbortSignal.timeout(1_000) })).pipe( + firstEvent((signal) => sdk.event.subscribe(undefined, { signal })).pipe( Effect.map((event) => ({ type: record(record(event).payload).type })), ), ), @@ -431,9 +487,10 @@ describe("HttpApi SDK", () => { ), ) - httpapi( + httpapiInstance( "uses generated SDK basic auth behavior", - withStandardProject("raw", ({ directory }) => + { serverPath: "raw", setup: writeStandardFiles }, + ({ directory }) => Effect.gen(function* () { const missing = yield* capture(() => client("raw", directory, { password: "secret" }).file.read({ path: "hello.txt" }), @@ -458,7 +515,6 @@ describe("HttpApi SDK", () => { content: record(good.data).content, } }), - ), ) serverPathParity("matches generated SDK instance read routes", (serverPath) => @@ -688,37 +744,34 @@ describe("HttpApi SDK", () => { ), ) serverPathParity("rejects oversized user image files before persistence", (serverPath) => - withProject( - serverPath, - { config: { attachment: { image: { max_base64_bytes: 4 } } } }, - ({ sdk, directory }) => - Effect.gen(function* () { - const filepath = path.join(directory, "oversized.png") - yield* call(() => Bun.write(filepath, Buffer.alloc(1024, 1))) - const session = yield* capture(() => sdk.session.create({ title: "oversized image" })) - const sessionID = String(record(session.data).id) - const prompt = yield* capture(() => - sdk.session.prompt({ - sessionID, - agent: "build", - noReply: true, - parts: [ - { - type: "file", - mime: "image/png", - filename: "oversized.png", - url: `file://${filepath}`, - }, - ], - }), - ) - const messages = yield* capture(() => sdk.session.messages({ sessionID })) + withProject(serverPath, { config: { attachment: { image: { max_base64_bytes: 4 } } } }, ({ sdk, directory }) => + Effect.gen(function* () { + const filepath = path.join(directory, "oversized.png") + yield* call(() => Bun.write(filepath, Buffer.alloc(1024, 1))) + const session = yield* capture(() => sdk.session.create({ title: "oversized image" })) + const sessionID = String(record(session.data).id) + const prompt = yield* capture(() => + sdk.session.prompt({ + sessionID, + agent: "build", + noReply: true, + parts: [ + { + type: "file", + mime: "image/png", + filename: "oversized.png", + url: `file://${filepath}`, + }, + ], + }), + ) + const messages = yield* capture(() => sdk.session.messages({ sessionID })) - expect(prompt.status).toBe(400) - expect(JSON.stringify(messages.data)).not.toContain("oversized.png") + expect(prompt.status).toBe(400) + expect(JSON.stringify(messages.data)).not.toContain("oversized.png") - return { promptStatus: prompt.status, persisted: JSON.stringify(messages.data).includes("oversized.png") } - }), + return { promptStatus: prompt.status, persisted: JSON.stringify(messages.data).includes("oversized.png") } + }), ), ) // kilocode_change end @@ -814,7 +867,7 @@ describe("HttpApi SDK", () => { // kilocode_change end httpapi( - "includes project skills in REST API async prompt context", + "includes project skills in REST API prompt context", withFakeLlmProject("default", { setup: writeProjectSkill }, ({ sdk, llm }) => Effect.gen(function* () { yield* llm.text("skill context ok", { usage: { input: 11, output: 7 } }) @@ -826,18 +879,17 @@ describe("HttpApi SDK", () => { ) const sessionID = String(record(session.data).id) const prompt = yield* capture(() => - sdk.session.promptAsync({ + sdk.session.prompt({ sessionID, agent: "build", model: { providerID: "test", modelID: "test-model" }, parts: [{ type: "text", text: "hello skill context" }], }), ) - yield* llm.wait(1) const inputs = yield* llm.inputs expect(session.status).toBe(200) - expect(prompt.status).toBe(204) + expect(prompt.status).toBe(200) expect(JSON.stringify(inputs[0])).toContain("project-rest-skill") }), ), diff --git a/packages/opencode/test/server/httpapi-session.test.ts b/packages/opencode/test/server/httpapi-session.test.ts index 3f45e9e4381..4b6feb7e1b6 100644 --- a/packages/opencode/test/server/httpapi-session.test.ts +++ b/packages/opencode/test/server/httpapi-session.test.ts @@ -8,8 +8,8 @@ import type { WorkspaceAdapter } from "../../src/control-plane/types" import { Workspace } from "../../src/control-plane/workspace" import { PermissionID } from "../../src/permission/schema" import { ModelID, ProviderID } from "../../src/provider/schema" -import { WithInstance } from "../../src/project/with-instance" import { InstanceBootstrap } from "../../src/project/bootstrap" +import { InstanceBootstrap as InstanceBootstrapService } from "../../src/project/bootstrap-service" import { InstanceStore } from "../../src/project/instance-store" import { Project } from "../../src/project/project" import { Server } from "../../src/server/server" @@ -20,13 +20,14 @@ import { MessageV2 } from "../../src/session/message-v2" import { Database } from "@/storage/db" import { SessionMessageTable, SessionTable } from "@/session/session.sql" import { SessionMessage } from "../../src/v2/session-message" -import { Modelv2 } from "../../src/v2/model" +import { ModelV2 } from "@opencode-ai/core/model" +import { ProviderV2 } from "@opencode-ai/core/provider" import * as DateTime from "effect/DateTime" import * as Log from "@opencode-ai/core/util/log" import { eq } from "drizzle-orm" import { resetDatabase } from "../fixture/db" -import { disposeAllInstances, tmpdir } from "../fixture/fixture" -import { it } from "../lib/effect" +import { disposeAllInstances, TestInstance } from "../fixture/fixture" +import { testEffect } from "../lib/effect" void Log.init({ print: false }) @@ -35,58 +36,45 @@ const workspaceLayer = Workspace.defaultLayer.pipe( Layer.provide(InstanceStore.defaultLayer), Layer.provide(InstanceBootstrap.defaultLayer), ) +const instanceStoreLayer = InstanceStore.defaultLayer.pipe( + Layer.provide( + Layer.succeed(InstanceBootstrapService.Service, InstanceBootstrapService.Service.of({ run: Effect.void })), + ), +) +const it = testEffect(Layer.mergeAll(instanceStoreLayer, Project.defaultLayer, Session.defaultLayer, workspaceLayer)) function app() { return Server.Default().app } -function runSession(fx: Effect.Effect) { - return Effect.runPromise(fx.pipe(Effect.provide(Session.defaultLayer))) -} - function pathFor(path: string, params: Record) { return Object.entries(params).reduce((result, [key, value]) => result.replace(`:${key}`, value), path) } -function createSession(directory: string, input?: Session.CreateInput) { - return Effect.promise( - async () => - await WithInstance.provide({ - directory, - fn: () => runSession(Session.Service.use((svc) => svc.create(input))), - }), - ) +function createSession(input?: Session.CreateInput) { + return Session.Service.use((svc) => svc.create(input)) } -function createTextMessage(directory: string, sessionID: SessionIDType, text: string) { - return Effect.promise( - async () => - await WithInstance.provide({ - directory, - fn: () => - runSession( - Effect.gen(function* () { - const svc = yield* Session.Service - const info = yield* svc.updateMessage({ - id: MessageID.ascending(), - role: "user", - sessionID, - agent: "build", - model: { providerID: ProviderID.make("test"), modelID: ModelID.make("test") }, - time: { created: Date.now() }, - }) - const part = yield* svc.updatePart({ - id: PartID.ascending(), - sessionID, - messageID: info.id, - type: "text", - text, - }) - return { info, part } - }), - ), - }), - ) +function createTextMessage(sessionID: SessionIDType, text: string) { + return Effect.gen(function* () { + const svc = yield* Session.Service + const info = yield* svc.updateMessage({ + id: MessageID.ascending(), + role: "user", + sessionID, + agent: "build", + model: { providerID: ProviderID.make("test"), modelID: ModelID.make("test") }, + time: { created: Date.now() }, + }) + const part = yield* svc.updatePart({ + id: PartID.ascending(), + sessionID, + messageID: info.id, + type: "text", + text, + }) + return { info, part } + }) } const localAdapter = (directory: string): WorkspaceAdapter => ({ @@ -101,18 +89,88 @@ const localAdapter = (directory: string): WorkspaceAdapter => ({ }) const createLocalWorkspace = (input: { projectID: Project.Info["id"]; type: string; directory: string }) => - Effect.gen(function* () { - registerAdapter(input.projectID, input.type, localAdapter(input.directory)) - return yield* Workspace.Service.use((svc) => - svc.create({ - type: input.type, - branch: null, - extra: null, - projectID: input.projectID, - }), - ).pipe(Effect.provide(workspaceLayer)) + Effect.acquireRelease( + Effect.gen(function* () { + registerAdapter(input.projectID, input.type, localAdapter(input.directory)) + return yield* Workspace.Service.use((svc) => + svc.create({ + type: input.type, + branch: null, + extra: null, + projectID: input.projectID, + }), + ) + }), + (info) => Workspace.Service.use((svc) => svc.remove(info.id)).pipe(Effect.ignore), + ) + +const insertLegacyAssistantMessage = (sessionID: SessionIDType) => + Effect.sync(() => { + const message = new SessionMessage.Assistant({ + id: SessionMessage.ID.create(), + type: "assistant", + agent: "build", + model: { + id: ModelV2.ID.make("model"), + providerID: ProviderV2.ID.make("provider"), + variant: ModelV2.VariantID.make("default"), + }, + time: { created: DateTime.makeUnsafe(1) }, + content: [], + }) + Database.use((db) => + db + .insert(SessionMessageTable) + .values([ + { + id: message.id, + session_id: sessionID, + type: message.type, + time_created: 1, + data: { + time: { created: 1 }, + agent: message.agent, + model: message.model, + content: message.content, + } as NonNullable<(typeof SessionMessageTable.$inferInsert)["data"]>, + }, + ]) + .run(), + ) }) +const setLegacySummaryDiff = (sessionID: SessionIDType) => + Effect.sync(() => + Database.use((db) => + db + .update(SessionTable) + .set({ + summary_additions: 1, + summary_deletions: 0, + summary_files: 1, + summary_diffs: [{ additions: 1, deletions: 0 }], + }) + .where(eq(SessionTable.id, sessionID)) + .run(), + ), + ) + +const getWorkspaceID = (sessionID: SessionIDType) => + Effect.sync(() => + Database.use((db) => + db + .select({ workspaceID: SessionTable.workspace_id }) + .from(SessionTable) + .where(eq(SessionTable.id, sessionID)) + .get(), + ), + ) + +const clearSessionPath = (sessionID: SessionIDType) => + Effect.sync(() => + Database.use((db) => db.update(SessionTable).set({ path: null }).where(eq(SessionTable.id, sessionID)).run()), + ) + function request(path: string, init?: RequestInit) { return Effect.promise(async () => app().request(path, init)) } @@ -132,16 +190,6 @@ function requestJson(path: string, init?: RequestInit) { return request(path, init).pipe(Effect.flatMap(json)) } -function withTmp( - options: Parameters[0], - fn: (tmp: Awaited>) => Effect.Effect, -) { - return Effect.acquireRelease( - Effect.promise(() => tmpdir(options)), - (tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()), - ).pipe(Effect.flatMap(fn)) -} - afterEach(async () => { Flag.KILO_EXPERIMENTAL_WORKSPACES = originalWorkspaces await disposeAllInstances() @@ -149,11 +197,12 @@ afterEach(async () => { }) describe("session HttpApi", () => { - it.live( + it.instance( "returns declared not found errors for read routes", - withTmp({ git: true, config: { formatter: false, lsp: false } }, (tmp) => + () => Effect.gen(function* () { - const headers = { "x-kilo-directory": tmp.path } + const test = yield* TestInstance + const headers = { "x-kilo-directory": test.directory } const missingSession = SessionID.descending() const missingSessionBody = { name: "NotFoundError", @@ -164,6 +213,14 @@ describe("session HttpApi", () => { expect(get.status).toBe(404) expect(yield* responseJson(get)).toEqual(missingSessionBody) + const children = yield* request(pathFor(SessionPaths.children, { sessionID: missingSession }), { headers }) + expect(children.status).toBe(404) + expect(yield* responseJson(children)).toEqual(missingSessionBody) + + const todo = yield* request(pathFor(SessionPaths.todo, { sessionID: missingSession }), { headers }) + expect(todo.status).toBe(404) + expect(yield* responseJson(todo)).toEqual(missingSessionBody) + const messages = yield* request(pathFor(SessionPaths.messages, { sessionID: missingSession }), { headers }) expect(messages.status).toBe(404) expect(yield* responseJson(messages)).toEqual(missingSessionBody) @@ -175,7 +232,22 @@ describe("session HttpApi", () => { expect(remove.status).toBe(404) expect(yield* responseJson(remove)).toEqual(missingSessionBody) - const session = yield* createSession(tmp.path, { title: "missing message" }) + const prompt = yield* request(pathFor(SessionPaths.prompt, { sessionID: missingSession }), { + headers: { ...headers, "content-type": "application/json" }, + method: "POST", + body: JSON.stringify({ agent: "build", noReply: true, parts: [{ type: "text", text: "hello" }] }), + }) + expect(prompt.status).toBe(404) + expect(yield* responseJson(prompt)).toEqual(missingSessionBody) + + const abort = yield* request(pathFor(SessionPaths.abort, { sessionID: missingSession }), { + headers, + method: "POST", + }) + expect(abort.status).toBe(200) + expect(yield* responseJson(abort)).toBe(true) + + const session = yield* createSession({ title: "missing message" }) const missingMessage = MessageID.ascending() const message = yield* request( pathFor(SessionPaths.message, { sessionID: session.id, messageID: missingMessage }), @@ -187,18 +259,19 @@ describe("session HttpApi", () => { data: { message: `Message not found: ${missingMessage}` }, }) }), - ), + { git: true, config: { formatter: false, lsp: false } }, ) - it.live( + it.instance( "serves read routes", - withTmp({ git: true, config: { formatter: false, lsp: false } }, (tmp) => + () => Effect.gen(function* () { - const headers = { "x-kilo-directory": tmp.path } - const parent = yield* createSession(tmp.path, { title: "parent" }) - const child = yield* createSession(tmp.path, { title: "child", parentID: parent.id }) - const message = yield* createTextMessage(tmp.path, parent.id, "hello") - yield* createTextMessage(tmp.path, parent.id, "world") + const test = yield* TestInstance + const headers = { "x-kilo-directory": test.directory } + const parent = yield* createSession({ title: "parent" }) + const child = yield* createSession({ title: "child", parentID: parent.id }) + const message = yield* createTextMessage(parent.id, "hello") + yield* createTextMessage(parent.id, "world") const listed = yield* requestJson(`${SessionPaths.list}?roots=true`, { headers }) expect(listed.map((item) => item.id)).toContain(parent.id) @@ -250,88 +323,40 @@ describe("session HttpApi", () => { ), ).toMatchObject({ info: { id: message.info.id } }) - yield* Effect.promise(() => - WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const message = new SessionMessage.Assistant({ - id: SessionMessage.ID.create(), - type: "assistant", - agent: "build", - model: { - id: Modelv2.ID.make("model"), - providerID: Modelv2.ProviderID.make("provider"), - variant: Modelv2.VariantID.make("default"), - }, - time: { created: DateTime.makeUnsafe(1) }, - content: [], - }) - Database.use((db) => - db - .insert(SessionMessageTable) - .values([ - { - id: message.id, - session_id: parent.id, - type: message.type, - time_created: 1, - data: { - time: { created: 1 }, - agent: message.agent, - model: message.model, - content: message.content, - } as NonNullable<(typeof SessionMessageTable.$inferInsert)["data"]>, - }, - ]) - .run(), - ) - }, - }), - ) + yield* insertLegacyAssistantMessage(parent.id) expect( (yield* requestJson<{ items: SessionMessage.Message[] }>(`/api/session/${parent.id}/message`, { headers })) .items, ).toMatchObject([{ type: "assistant" }]) }), - ), + { git: true, config: { formatter: false, lsp: false } }, ) - it.live( + it.instance( "serves sessions with migrated summary diffs missing file details", - withTmp({ git: true, config: { formatter: false, lsp: false } }, (tmp) => + () => Effect.gen(function* () { - const session = yield* createSession(tmp.path, { title: "legacy diff" }) - yield* Effect.sync(() => - Database.use((db) => - db - .update(SessionTable) - .set({ - summary_additions: 1, - summary_deletions: 0, - summary_files: 1, - summary_diffs: [{ additions: 1, deletions: 0 }], - }) - .where(eq(SessionTable.id, session.id)) - .run(), - ), - ) + const test = yield* TestInstance + const session = yield* createSession({ title: "legacy diff" }) + yield* setLegacySummaryDiff(session.id) const response = yield* request(pathFor(SessionPaths.get, { sessionID: session.id }), { - headers: { "x-kilo-directory": tmp.path }, + headers: { "x-kilo-directory": test.directory }, }) expect(response.status).toBe(200) expect((yield* json(response)).summary?.diffs).toEqual([{ additions: 1, deletions: 0 }]) }), - ), + { git: true, config: { formatter: false, lsp: false } }, ) - it.live( + it.instance( "serves lifecycle mutation routes", - withTmp({ git: true, config: { formatter: false, lsp: false, share: "disabled" } }, (tmp) => + () => Effect.gen(function* () { - const headers = { "x-kilo-directory": tmp.path, "content-type": "application/json" } + const test = yield* TestInstance + const headers = { "x-kilo-directory": test.directory, "content-type": "application/json" } const createdEmpty = yield* requestJson(SessionPaths.create, { method: "POST", @@ -373,56 +398,48 @@ describe("session HttpApi", () => { }), ).toBe(true) }), - ), + { git: true, config: { formatter: false, lsp: false, share: "disabled" } }, ) - it.live( + it.instance( "persists selected workspace id when creating a session", - withTmp({ git: true, config: { formatter: false, lsp: false, share: "disabled" } }, (tmp) => + () => Effect.gen(function* () { + const test = yield* TestInstance Flag.KILO_EXPERIMENTAL_WORKSPACES = true - const project = yield* Project.use.fromDirectory(tmp.path).pipe(Effect.provide(Project.defaultLayer)) + const project = yield* Project.use.fromDirectory(test.directory) const workspace = yield* createLocalWorkspace({ projectID: project.project.id, type: "session-create-workspace", - directory: path.join(tmp.path, ".workspace-local"), + directory: path.join(test.directory, ".workspace-local"), }) const created = yield* requestJson(`${SessionPaths.create}?workspace=${workspace.id}`, { method: "POST", - headers: { "x-kilo-directory": tmp.path, "content-type": "application/json" }, + headers: { "x-kilo-directory": test.directory, "content-type": "application/json" }, body: JSON.stringify({ title: "workspace session" }), }) const messages = yield* request( `${pathFor(SessionPaths.messages, { sessionID: created.id })}?workspace=${workspace.id}`, { - headers: { "x-kilo-directory": tmp.path }, + headers: { "x-kilo-directory": test.directory }, }, ) expect(created).toMatchObject({ id: created.id, workspaceID: workspace.id }) expect(messages.status).toBe(200) - expect( - yield* Effect.sync(() => - Database.use((db) => - db - .select({ workspaceID: SessionTable.workspace_id }) - .from(SessionTable) - .where(eq(SessionTable.id, created.id)) - .get(), - ), - ), - ).toEqual({ workspaceID: workspace.id }) + expect(yield* getWorkspaceID(created.id)).toEqual({ workspaceID: workspace.id }) }), - ), + { git: true, config: { formatter: false, lsp: false, share: "disabled" } }, ) - it.live( + it.instance( "validates archived timestamp values", - withTmp({ git: true, config: { formatter: false, lsp: false } }, (tmp) => + () => Effect.gen(function* () { - const headers = { "x-kilo-directory": tmp.path, "content-type": "application/json" } - const session = yield* createSession(tmp.path, { title: "archived" }) + const test = yield* TestInstance + const headers = { "x-kilo-directory": test.directory, "content-type": "application/json" } + const session = yield* createSession({ title: "archived" }) const body = JSON.stringify({ time: { archived: -1 } }) const response = yield* request(pathFor(SessionPaths.update, { sessionID: session.id }), { @@ -433,30 +450,35 @@ describe("session HttpApi", () => { expect(response.status).toBe(200) expect((yield* json(response)).time.archived).toBe(-1) }), - ), + { git: true, config: { formatter: false, lsp: false } }, ) - it.live( + it.instance( "uses project-scoped path and directory precedence", - withTmp({ git: true, config: { formatter: false, lsp: false } }, (tmp) => + () => Effect.gen(function* () { - const currentDir = path.join(tmp.path, "packages", "opencode", "src") + const test = yield* TestInstance + const currentDir = path.join(test.directory, "packages", "opencode", "src") yield* Effect.promise(() => mkdir(currentDir, { recursive: true })) - const pathSession = yield* createSession(currentDir) - const pathlessSession = yield* createSession(currentDir) - yield* Effect.sync(() => - Database.use((db) => - db.update(SessionTable).set({ path: null }).where(eq(SessionTable.id, pathlessSession.id)).run(), - ), + const store = yield* InstanceStore.Service + const { pathSession, pathlessSession } = yield* store.provide( + { directory: currentDir }, + Effect.gen(function* () { + return { + pathSession: yield* createSession(), + pathlessSession: yield* createSession(), + } + }).pipe(Effect.provideService(TestInstance, { directory: currentDir }), Effect.provide(Session.defaultLayer)), ) + yield* clearSessionPath(pathlessSession.id) const query = new URLSearchParams({ scope: "project", path: "packages/opencode/src", directory: currentDir, }) - const headers = { "x-kilo-directory": tmp.path } + const headers = { "x-kilo-directory": test.directory } const sessions = (yield* json( yield* request(`${SessionPaths.list}?${query}`, { headers }), )).map((item) => item.id) @@ -464,17 +486,18 @@ describe("session HttpApi", () => { expect(sessions).toContain(pathSession.id) expect(sessions).not.toContain(pathlessSession.id) }), - ), + { git: true, config: { formatter: false, lsp: false } }, ) - it.live( + it.instance( "serves paginated message link headers", - withTmp({ git: true, config: { formatter: false, lsp: false } }, (tmp) => + () => Effect.gen(function* () { - const headers = { "x-kilo-directory": tmp.path } - const session = yield* createSession(tmp.path, { title: "messages" }) - yield* createTextMessage(tmp.path, session.id, "first") - yield* createTextMessage(tmp.path, session.id, "second") + const test = yield* TestInstance + const headers = { "x-kilo-directory": test.directory } + const session = yield* createSession({ title: "messages" }) + yield* createTextMessage(session.id, "first") + yield* createTextMessage(session.id, "second") const route = `${pathFor(SessionPaths.messages, { sessionID: session.id })}?limit=1` const response = yield* request(route, { headers }) @@ -483,17 +506,18 @@ describe("session HttpApi", () => { expect(response.headers.get("link")).toContain("limit=1") expect(response.headers.get("access-control-expose-headers")?.toLowerCase()).toContain("x-next-cursor") }), - ), + { git: true, config: { formatter: false, lsp: false } }, ) - it.live( + it.instance( "serves message mutation routes", - withTmp({ git: true, config: { formatter: false, lsp: false } }, (tmp) => + () => Effect.gen(function* () { - const headers = { "x-kilo-directory": tmp.path, "content-type": "application/json" } - const session = yield* createSession(tmp.path, { title: "messages" }) - const first = yield* createTextMessage(tmp.path, session.id, "first") - const second = yield* createTextMessage(tmp.path, session.id, "second") + const test = yield* TestInstance + const headers = { "x-kilo-directory": test.directory, "content-type": "application/json" } + const session = yield* createSession({ title: "messages" }) + const first = yield* createTextMessage(session.id, "first") + const second = yield* createTextMessage(session.id, "second") const updated = yield* requestJson( pathFor(SessionPaths.updatePart, { @@ -527,15 +551,42 @@ describe("session HttpApi", () => { ), ).toBe(true) }), - ), + { git: true, config: { formatter: false, lsp: false } }, + ) + + it.instance( + "rejects part updates whose path and body ids disagree", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const headers = { "x-kilo-directory": test.directory, "content-type": "application/json" } + const session = yield* createSession({ title: "part mismatch" }) + const message = yield* createTextMessage(session.id, "first") + const response = yield* request( + pathFor(SessionPaths.updatePart, { + sessionID: session.id, + messageID: message.info.id, + partID: message.part.id, + }), + { + method: "PATCH", + headers, + body: JSON.stringify({ ...message.part, id: PartID.ascending() }), + }, + ) + + expect(response.status).toBe(400) + }), + { git: true, config: { formatter: false, lsp: false } }, ) - it.live( + it.instance( "serves remaining non-LLM session mutation routes", - withTmp({ git: true, config: { formatter: false, lsp: false } }, (tmp) => + () => Effect.gen(function* () { - const headers = { "x-kilo-directory": tmp.path, "content-type": "application/json" } - const session = yield* createSession(tmp.path, { title: "remaining" }) + const test = yield* TestInstance + const headers = { "x-kilo-directory": test.directory, "content-type": "application/json" } + const session = yield* createSession({ title: "remaining" }) expect( yield* requestJson(pathFor(SessionPaths.revert, { sessionID: session.id }), { @@ -566,6 +617,6 @@ describe("session HttpApi", () => { ), ).toBe(true) }), - ), + { git: true, config: { formatter: false, lsp: false } }, ) }) diff --git a/packages/opencode/test/server/httpapi-sync.test.ts b/packages/opencode/test/server/httpapi-sync.test.ts index ac62dc18ed9..228f20457a4 100644 --- a/packages/opencode/test/server/httpapi-sync.test.ts +++ b/packages/opencode/test/server/httpapi-sync.test.ts @@ -1,29 +1,25 @@ -import { afterEach, describe, expect, mock, spyOn, test } from "bun:test" +import { afterEach, describe, expect, mock, spyOn } from "bun:test" import { Context, Effect } from "effect" import { Flag } from "@opencode-ai/core/flag/flag" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" import { Server } from "../../src/server/server" import { SyncPaths } from "../../src/server/routes/instance/httpapi/groups/sync" -import { ExperimentalHttpApiServer } from "../../src/server/routes/instance/httpapi/server" +import { HttpApiApp } from "../../src/server/routes/instance/httpapi/server" import { Session } from "@/session/session" import * as Log from "@opencode-ai/core/util/log" import { resetDatabase } from "../fixture/db" -import { disposeAllInstances, tmpdir } from "../fixture/fixture" +import { disposeAllInstances, TestInstance } from "../fixture/fixture" +import { testEffect } from "../lib/effect" void Log.init({ print: false }) const originalWorkspaces = Flag.KILO_EXPERIMENTAL_WORKSPACES const context = Context.empty() as Context.Context +const it = testEffect(Session.defaultLayer) function app() { return Server.Default().app } -function runSession(fx: Effect.Effect) { - return Effect.runPromise(fx.pipe(Effect.provide(Session.defaultLayer))) -} - afterEach(async () => { mock.restore() Flag.KILO_EXPERIMENTAL_WORKSPACES = originalWorkspaces @@ -32,111 +28,138 @@ afterEach(async () => { }) describe("sync HttpApi", () => { - test("serves sync routes", async () => { - Flag.KILO_EXPERIMENTAL_WORKSPACES = true - await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) - const headers = { "x-kilo-directory": tmp.path, "content-type": "application/json" } - const info = spyOn(Log.create({ service: "server.sync" }), "info") - - const session = await WithInstance.provide({ - directory: tmp.path, - fn: async () => runSession(Session.Service.use((svc) => svc.create({ title: "sync" }))), - }) + it.instance( + "serves sync routes", + () => + Effect.gen(function* () { + Flag.KILO_EXPERIMENTAL_WORKSPACES = true + const tmp = yield* TestInstance + const headers = { "x-kilo-directory": tmp.directory, "content-type": "application/json" } + const info = spyOn(Log.create({ service: "server.sync" }), "info") + const session = yield* Session.Service.use((svc) => svc.create({ title: "sync" })) - const started = await app().request(SyncPaths.start, { method: "POST", headers }) - expect(started.status).toBe(200) - expect(await started.json()).toBe(true) + const started = yield* Effect.promise(() => + Promise.resolve(app().request(SyncPaths.start, { method: "POST", headers })), + ) + expect(started.status).toBe(200) + expect(yield* Effect.promise(() => started.json())).toBe(true) - const history = await app().request(SyncPaths.history, { - method: "POST", - headers, - body: JSON.stringify({}), - }) - expect(history.status).toBe(200) - const rows = (await history.json()) as Array<{ - id: string - aggregate_id: string - seq: number - type: string - data: Record - }> - expect(rows.map((row) => row.aggregate_id)).toContain(session.id) + const history = yield* Effect.promise(() => + Promise.resolve( + app().request(SyncPaths.history, { + method: "POST", + headers, + body: JSON.stringify({}), + }), + ), + ) + expect(history.status).toBe(200) + const rows = (yield* Effect.promise(() => history.json())) as Array<{ + id: string + aggregate_id: string + seq: number + type: string + data: Record + }> + expect(rows.map((row) => row.aggregate_id)).toContain(session.id) - const replayed = await app().request(SyncPaths.replay, { - method: "POST", - headers, - body: JSON.stringify({ - directory: tmp.path, - events: rows - .filter((row) => row.aggregate_id === session.id) - .map((row) => ({ - id: row.id, - aggregateID: row.aggregate_id, - seq: row.seq, - type: row.type, - data: row.data, - })), + const replayed = yield* Effect.promise(() => + Promise.resolve( + app().request(SyncPaths.replay, { + method: "POST", + headers, + body: JSON.stringify({ + directory: tmp.directory, + events: rows + .filter((row) => row.aggregate_id === session.id) + .map((row) => ({ + id: row.id, + aggregateID: row.aggregate_id, + seq: row.seq, + type: row.type, + data: row.data, + })), + }), + }), + ), + ) + expect(replayed.status).toBe(200) + expect(yield* Effect.promise(() => replayed.json())).toEqual({ sessionID: session.id }) + expect(info.mock.calls.some(([message]) => message === "sync replay requested")).toBe(true) + expect(info.mock.calls.some(([message]) => message === "sync replay complete")).toBe(true) }), - }) - expect(replayed.status).toBe(200) - expect(await replayed.json()).toEqual({ sessionID: session.id }) - expect(info.mock.calls.some(([message]) => message === "sync replay requested")).toBe(true) - expect(info.mock.calls.some(([message]) => message === "sync replay complete")).toBe(true) - }) + { git: true, config: { formatter: false, lsp: false } }, + ) - test("validates seq values", async () => { - await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) - const headers = { "x-kilo-directory": tmp.path, "content-type": "application/json" } - const cases = [ - { - path: SyncPaths.history, - body: { aggregate: -1 }, - }, - { - path: SyncPaths.history, - body: { aggregate: 1.5 }, - }, - { - path: SyncPaths.replay, - body: { - directory: tmp.path, - events: [{ id: "event", aggregateID: "session", seq: -1, type: "session.created", data: {} }], - }, - }, - { - path: SyncPaths.replay, - body: { - directory: tmp.path, - events: [{ id: "event", aggregateID: "session", seq: 1.5, type: "session.created", data: {} }], - }, - }, - ] + it.instance( + "validates seq values", + () => + Effect.gen(function* () { + const tmp = yield* TestInstance + const headers = { "x-kilo-directory": tmp.directory, "content-type": "application/json" } + const cases = [ + { + path: SyncPaths.history, + body: { aggregate: -1 }, + }, + { + path: SyncPaths.history, + body: { aggregate: 1.5 }, + }, + { + path: SyncPaths.replay, + body: { + directory: tmp.directory, + events: [{ id: "event", aggregateID: "session", seq: -1, type: "session.created", data: {} }], + }, + }, + { + path: SyncPaths.replay, + body: { + directory: tmp.directory, + events: [{ id: "event", aggregateID: "session", seq: 1.5, type: "session.created", data: {} }], + }, + }, + ] - for (const item of cases) { - const response = await app().request(item.path, { - method: "POST", - headers, - body: JSON.stringify(item.body), - }) - expect(response.status).toBe(400) - } - }) - - test.todo("returns structured validation errors", async () => { - await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) - const response = await ExperimentalHttpApiServer.webHandler().handler( - new Request(`http://localhost${SyncPaths.history}`, { - method: "POST", - headers: { "x-kilo-directory": tmp.path, "content-type": "application/json" }, - body: JSON.stringify({ aggregate: -1 }), + for (const item of cases) { + const response = yield* Effect.promise(() => + Promise.resolve( + app().request(item.path, { + method: "POST", + headers, + body: JSON.stringify(item.body), + }), + ), + ) + expect(response.status).toBe(400) + } }), - context, - ) + { git: true, config: { formatter: false, lsp: false } }, + ) + + it.instance.skip( + "returns structured validation errors", + () => + Effect.gen(function* () { + const tmp = yield* TestInstance + const response = yield* Effect.promise(() => + HttpApiApp.webHandler().handler( + new Request(`http://localhost${SyncPaths.history}`, { + method: "POST", + headers: { "x-kilo-directory": tmp.directory, "content-type": "application/json" }, + body: JSON.stringify({ aggregate: -1 }), + }), + context, + ), + ) - expect(response.status).toBe(400) - expect(response.headers.get("content-type") ?? "").toContain("application/json") - const body = (await response.json()) as Record - expect(body.success).toBe(false) - expect(Array.isArray(body.error) || Array.isArray(body.errors)).toBe(true) - }) + expect(response.status).toBe(400) + expect(response.headers.get("content-type") ?? "").toContain("application/json") + const body = (yield* Effect.promise(() => response.json())) as Record + expect(body.success).toBe(false) + expect(Array.isArray(body.error) || Array.isArray(body.errors)).toBe(true) + }), + { git: true, config: { formatter: false, lsp: false } }, + ) }) diff --git a/packages/opencode/test/server/httpapi-ui.test.ts b/packages/opencode/test/server/httpapi-ui.test.ts index 6b4fc891aee..e7790895c74 100644 --- a/packages/opencode/test/server/httpapi-ui.test.ts +++ b/packages/opencode/test/server/httpapi-ui.test.ts @@ -1,5 +1,5 @@ import { createHash } from "node:crypto" -import { afterEach, describe, expect, test } from "bun:test" +import { describe, expect } from "bun:test" import { Flag } from "@opencode-ai/core/flag/flag" import * as Log from "@opencode-ai/core/util/log" import { ConfigProvider, Effect, Layer } from "effect" @@ -12,29 +12,36 @@ import { HttpServerResponse, } from "effect/unstable/http" import { AppFileSystem } from "@opencode-ai/core/filesystem" +import { RuntimeFlags } from "../../src/effect/runtime-flags" import { ServerAuth } from "../../src/server/auth" import { authorizationRouterMiddleware } from "../../src/server/routes/instance/httpapi/middleware/authorization" -import { ExperimentalHttpApiServer } from "../../src/server/routes/instance/httpapi/server" +import { HttpApiApp } from "../../src/server/routes/instance/httpapi/server" import { serveEmbeddedUIEffect, serveUIEffect } from "../../src/server/shared/ui" -import { Server } from "../../src/server/server" +import { testEffect } from "../lib/effect" void Log.init({ print: false }) -const original = { - KILO_DISABLE_EMBEDDED_WEB_UI: Flag.KILO_DISABLE_EMBEDDED_WEB_UI, - KILO_SERVER_PASSWORD: Flag.KILO_SERVER_PASSWORD, - KILO_SERVER_USERNAME: Flag.KILO_SERVER_USERNAME, - envPassword: process.env.KILO_SERVER_PASSWORD, - envUsername: process.env.KILO_SERVER_USERNAME, -} +const testStateLayer = Layer.effectDiscard( + Effect.gen(function* () { + const original = { + KILO_SERVER_PASSWORD: Flag.KILO_SERVER_PASSWORD, + KILO_SERVER_USERNAME: Flag.KILO_SERVER_USERNAME, + envPassword: process.env.KILO_SERVER_PASSWORD, + envUsername: process.env.KILO_SERVER_USERNAME, + } -afterEach(() => { - Flag.KILO_DISABLE_EMBEDDED_WEB_UI = original.KILO_DISABLE_EMBEDDED_WEB_UI - Flag.KILO_SERVER_PASSWORD = original.KILO_SERVER_PASSWORD - Flag.KILO_SERVER_USERNAME = original.KILO_SERVER_USERNAME - restoreEnv("KILO_SERVER_PASSWORD", original.envPassword) - restoreEnv("KILO_SERVER_USERNAME", original.envUsername) -}) + yield* Effect.addFinalizer(() => + Effect.sync(() => { + Flag.KILO_SERVER_PASSWORD = original.KILO_SERVER_PASSWORD + Flag.KILO_SERVER_USERNAME = original.KILO_SERVER_USERNAME + restoreEnv("KILO_SERVER_PASSWORD", original.envPassword) + restoreEnv("KILO_SERVER_USERNAME", original.envUsername) + }), + ) + }), +) + +const it = testEffect(Layer.mergeAll(testStateLayer, AppFileSystem.defaultLayer, RuntimeFlags.layer())) function restoreEnv(key: string, value: string | undefined) { if (value === undefined) { @@ -46,7 +53,7 @@ function restoreEnv(key: string, value: string | undefined) { function app(input?: { password?: string; username?: string }) { const handler = HttpRouter.toWebHandler( - ExperimentalHttpApiServer.routes.pipe( + HttpApiApp.routes.pipe( Layer.provide( ConfigProvider.layer( ConfigProvider.fromUnknown({ @@ -60,27 +67,40 @@ function app(input?: { password?: string; username?: string }) { ).handler return { request(input: string | URL | Request, init?: RequestInit) { - return handler( - input instanceof Request ? input : new Request(new URL(input, "http://localhost"), init), - ExperimentalHttpApiServer.context, + return Effect.promise((): Promise => + Promise.resolve( + handler( + input instanceof Request ? input : new Request(new URL(input, "http://localhost"), init), + HttpApiApp.context, + ), + ), ) }, } } -function uiApp(input?: { password?: string; username?: string; client?: Layer.Layer }) { +function uiApp(input?: { + password?: string + username?: string + client?: Layer.Layer + disableEmbeddedWebUi?: boolean +}) { const handler = HttpRouter.toWebHandler( HttpRouter.use((router) => Effect.gen(function* () { const fs = yield* AppFileSystem.Service const client = yield* HttpClient.HttpClient - yield* router.add("*", "/*", (request) => serveUIEffect(request, { fs, client })) + const flags = yield* RuntimeFlags.Service + yield* router.add("*", "/*", (request) => + serveUIEffect(request, { fs, client, disableEmbeddedWebUi: flags.disableEmbeddedWebUi }), + ) }), ).pipe( Layer.provide(authorizationRouterMiddleware.layer.pipe(Layer.provide(ServerAuth.Config.defaultLayer))), Layer.provide([ AppFileSystem.defaultLayer, input?.client ?? httpClient(new Response("ui")), + RuntimeFlags.layer({ disableEmbeddedWebUi: input?.disableEmbeddedWebUi ?? false }), HttpServer.layerServices, ConfigProvider.layer( ConfigProvider.fromUnknown({ @@ -94,9 +114,55 @@ function uiApp(input?: { password?: string; username?: string; client?: Layer.La ).handler return { request(input: string | URL | Request, init?: RequestInit) { - return handler( - input instanceof Request ? input : new Request(new URL(input, "http://localhost"), init), - ExperimentalHttpApiServer.context, + return Effect.promise((): Promise => + Promise.resolve( + handler( + input instanceof Request ? input : new Request(new URL(input, "http://localhost"), init), + HttpApiApp.context, + ), + ), + ) + }, + } +} + +function routeOrderingApp() { + let proxiedUrl: string | undefined + const handler = HttpRouter.toWebHandler( + HttpRouter.use((router) => + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const client = yield* HttpClient.HttpClient + const flags = yield* RuntimeFlags.Service + yield* router.add("GET", "/session/:sessionID", () => + Effect.succeed(HttpServerResponse.jsonUnsafe({ error: "Not Found" }, { status: 404 })), + ) + yield* router.add("*", "/*", (request) => + serveUIEffect(request, { fs, client, disableEmbeddedWebUi: flags.disableEmbeddedWebUi }), + ) + }), + ).pipe( + Layer.provide([ + AppFileSystem.defaultLayer, + RuntimeFlags.layer({ disableEmbeddedWebUi: true }), + httpClient(new Response("ui"), (request) => { + proxiedUrl = request.url + }), + HttpServer.layerServices, + ]), + ), + { disableLogger: true }, + ).handler + return { + proxiedUrl: () => proxiedUrl, + request(input: string | URL | Request, init?: RequestInit) { + return Effect.promise((): Promise => + Promise.resolve( + handler( + input instanceof Request ? input : new Request(new URL(input, "http://localhost"), init), + HttpApiApp.context, + ), + ), ) }, } @@ -112,158 +178,186 @@ function httpClient(response: Response, onRequest?: (request: HttpClientRequest. ) } +function responseText(response: Response) { + return Effect.promise(() => response.text()) +} + describe("HttpApi UI fallback", () => { // kilocode_change start - embedded UI is the only supported fallback; never proxy to app.opencode.ai - test("returns not found without proxying when embedded UI is disabled", async () => { - Flag.KILO_DISABLE_EMBEDDED_WEB_UI = true - let proxied = false + it.live("returns not found without proxying when embedded UI is disabled", () => + Effect.gen(function* () { + let proxied = false + const response = yield* uiApp({ + disableEmbeddedWebUi: true, + client: httpClient(new Response("ui"), () => { + proxied = true + }), + }).request("/") - const response = await uiApp({ - client: httpClient(new Response("ui"), () => { - proxied = true - }), - }).request("/") - - expect(response.status).toBe(404) - expect(await response.json()).toEqual({ error: "Not Found" }) - expect(proxied).toBe(false) - }) + expect(response.status).toBe(404) + expect(yield* Effect.promise(() => response.json())).toEqual({ error: "Not Found" }) + expect(proxied).toBe(false) + }), + ) // kilocode_change end - test("serves embedded UI assets when Bun can read them but access reports missing", async () => { - let readPath: string | undefined + it.live("serves embedded UI assets when Bun can read them but access reports missing", () => + Effect.gen(function* () { + let readPath: string | undefined - const response = await Effect.runPromise( - Effect.gen(function* () { - const fs = yield* AppFileSystem.Service - return yield* serveEmbeddedUIEffect( - "/assets/app.js", - { - ...fs, - existsSafe: () => Effect.die("embedded UI should not rely on filesystem access checks"), - readFile: (path) => { - readPath = path - return path === "/$bunfs/root/assets/app.js" - ? Effect.succeed(new TextEncoder().encode("console.log('embedded')")) - : Effect.die(`unexpected embedded UI path: ${path}`) - }, + const fs = yield* AppFileSystem.Service + const response = yield* serveEmbeddedUIEffect( + "/assets/app.js", + { + ...fs, + existsSafe: () => Effect.die("embedded UI should not rely on filesystem access checks"), + readFile: (path) => { + readPath = path + return path === "/$bunfs/root/assets/app.js" + ? Effect.succeed(new TextEncoder().encode("console.log('embedded')")) + : Effect.die(`unexpected embedded UI path: ${path}`) }, - { "assets/app.js": "/$bunfs/root/assets/app.js" }, - ) - }).pipe(Effect.provide(AppFileSystem.defaultLayer), Effect.map(HttpServerResponse.toWeb)), - ) + }, + { "assets/app.js": "/$bunfs/root/assets/app.js" }, + ).pipe(Effect.map(HttpServerResponse.toWeb)) - expect(response.status).toBe(200) - expect(readPath).toBe("/$bunfs/root/assets/app.js") - expect(response.headers.get("content-type")).toContain("text/javascript") - expect(await response.text()).toBe("console.log('embedded')") - }) + expect(response.status).toBe(200) + expect(readPath).toBe("/$bunfs/root/assets/app.js") + expect(response.headers.get("content-type")).toContain("text/javascript") + expect(yield* responseText(response)).toBe("console.log('embedded')") + }), + ) - test("allows embedded UI terminal wasm and theme preload CSP", async () => { - const script = 'document.documentElement.dataset.theme = "dark"' + it.live("allows embedded UI terminal wasm and theme preload CSP", () => + Effect.gen(function* () { + const script = 'document.documentElement.dataset.theme = "dark"' - const response = await Effect.runPromise( - Effect.gen(function* () { - const fs = yield* AppFileSystem.Service - return yield* serveEmbeddedUIEffect( - "/", - { - ...fs, - readFile: (path) => { - return path === "/$bunfs/root/index.html" - ? Effect.succeed( - new TextEncoder().encode( - ``, - ), - ) - : Effect.die(`unexpected embedded UI path: ${path}`) - }, + const fs = yield* AppFileSystem.Service + const response = yield* serveEmbeddedUIEffect( + "/", + { + ...fs, + readFile: (path) => { + return path === "/$bunfs/root/index.html" + ? Effect.succeed( + new TextEncoder().encode( + ``, + ), + ) + : Effect.die(`unexpected embedded UI path: ${path}`) }, - { "index.html": "/$bunfs/root/index.html" }, - ) - }).pipe(Effect.provide(AppFileSystem.defaultLayer), Effect.map(HttpServerResponse.toWeb)), - ) + }, + { "index.html": "/$bunfs/root/index.html" }, + ).pipe(Effect.map(HttpServerResponse.toWeb)) - const csp = response.headers.get("content-security-policy") ?? "" - expect(csp).toContain("script-src 'self' 'wasm-unsafe-eval'") - expect(csp).toContain(`'sha256-${createHash("sha256").update(script).digest("base64")}'`) - expect(csp).toContain("connect-src * data:") - }) - - test("keeps matched API routes ahead of the UI fallback", async () => { - const response = await Server.Default().app.request("/session/ses_nope") + const csp = response.headers.get("content-security-policy") ?? "" + expect(csp).toContain("script-src 'self' 'wasm-unsafe-eval'") + expect(csp).toContain(`'sha256-${createHash("sha256").update(script).digest("base64")}'`) + expect(csp).toContain("connect-src * data:") + }), + ) - expect(response.status).toBe(404) - }) + it.live("keeps matched API routes ahead of the UI fallback", () => + Effect.gen(function* () { + const server = routeOrderingApp() + const response = yield* server.request("/session/ses_nope") - test("requires server password for the web UI", async () => { - Flag.KILO_DISABLE_EMBEDDED_WEB_UI = true + expect(response.status).toBe(404) + expect(server.proxiedUrl()).toBeUndefined() + }), + ) - const response = await uiApp({ password: "secret", username: "kilo" }).request("/") // kilocode_change + it.live("requires server password for the web UI", () => + Effect.gen(function* () { + const response = yield* uiApp({ + password: "secret", + username: "kilo", // kilocode_change + disableEmbeddedWebUi: true, + }).request("/") - expect(response.status).toBe(401) - expect(response.headers.get("www-authenticate")).toBe('Basic realm="Secure Area"') - }) + expect(response.status).toBe(401) + expect(response.headers.get("www-authenticate")).toBe('Basic realm="Secure Area"') + }), + ) - test("accepts auth token for the web UI", async () => { - Flag.KILO_DISABLE_EMBEDDED_WEB_UI = true + it.live("accepts auth token for the web UI", () => + Effect.gen(function* () { + let proxied = false // kilocode_change + const response = yield* uiApp({ + password: "secret", + username: "kilo", // kilocode_change + disableEmbeddedWebUi: true, + // kilocode_change start - authenticated requests still must not proxy when embedded UI is disabled + client: httpClient(new Response("kilo", { headers: { "content-type": "text/html" } }), () => { + proxied = true + }), + // kilocode_change end + }).request(`/?auth_token=${btoa("kilo:secret")}`) - const response = await uiApp({ - password: "secret", // kilocode_change start - username: "kilo", - }).request(`/?auth_token=${btoa("kilo:secret")}`) - - expect(response.status).toBe(404) - expect(await response.json()).toEqual({ error: "Not Found" }) - // kilocode_change end - }) + expect(response.status).toBe(404) + expect(yield* Effect.promise(() => response.json())).toEqual({ error: "Not Found" }) + expect(proxied).toBe(false) + // kilocode_change end + }), + ) - test("accepts basic auth for the web UI", async () => { - Flag.KILO_DISABLE_EMBEDDED_WEB_UI = true + it.live("accepts basic auth for the web UI", () => + Effect.gen(function* () { + let proxied = false // kilocode_change + const response = yield* uiApp({ + password: "secret", + username: "kilo", // kilocode_change + disableEmbeddedWebUi: true, + // kilocode_change start + client: httpClient(new Response("ui"), () => { + proxied = true + }), + // kilocode_change end + }).request("/", { + headers: { authorization: `Basic ${btoa("kilo:secret")}` }, + }) - // kilocode_change start - const response = await uiApp({ password: "secret", username: "kilo" }).request("/", { - headers: { authorization: `Basic ${btoa("kilo:secret")}` }, + // kilocode_change start + expect(response.status).toBe(404) + expect(yield* Effect.promise(() => response.json())).toEqual({ error: "Not Found" }) + expect(proxied).toBe(false) // kilocode_change end - }) - - // kilocode_change start - expect(response.status).toBe(404) - expect(await response.json()).toEqual({ error: "Not Found" }) - // kilocode_change end - }) + }), + ) // Regression for #25698 (Ope): the browser fetches the PWA manifest and // its icons via flows that don't carry app-managed credentials (the // `` request is not under page-auth control), so the // server returning 401 breaks PWA install. These specific public assets // should bypass auth. - test("allows public PWA assets through auth without proxying", async () => { - // kilocode_change - Flag.KILO_DISABLE_EMBEDDED_WEB_UI = true - - for (const path of ["/site.webmanifest", "/web-app-manifest-192x192.png", "/web-app-manifest-512x512.png"]) { - const response = await uiApp({ - password: "secret", - username: "kilo", // kilocode_change - client: httpClient(new Response("ok")), - }).request(path) - expect(response.status).toBe(404) // kilocode_change - } - }) + it.live("serves the PWA manifest without auth even when a server password is set", () => + Effect.gen(function* () { + for (const path of ["/site.webmanifest", "/web-app-manifest-192x192.png", "/web-app-manifest-512x512.png"]) { + const response = yield* uiApp({ + password: "secret", + username: "kilo", // kilocode_change + disableEmbeddedWebUi: true, + client: httpClient(new Response("ok")), + }).request(path) + expect(response.status).not.toBe(401) + } + }), + ) - test("allows web UI preflight without auth", async () => { - const response = await app({ password: "secret", username: "kilo" }).request("/", { - // kilocode_change - method: "OPTIONS", - headers: { - origin: "http://localhost:3000", - "access-control-request-method": "GET", - }, - }) + it.live("allows web UI preflight without auth", () => + Effect.gen(function* () { + const response = yield* app({ password: "secret", username: "kilo" }).request("/", { + // kilocode_change + method: "OPTIONS", + headers: { + origin: "http://localhost:3000", + "access-control-request-method": "GET", + }, + }) - expect(response.status).toBe(204) - expect(response.headers.get("access-control-allow-origin")).toBe("http://localhost:3000") - }) + expect(response.status).toBe(204) + expect(response.headers.get("access-control-allow-origin")).toBe("http://localhost:3000") + }), + ) }) diff --git a/packages/opencode/test/server/httpapi-workspace-routing.test.ts b/packages/opencode/test/server/httpapi-workspace-routing.test.ts index 2bc99f40bc5..b29a683a0d5 100644 --- a/packages/opencode/test/server/httpapi-workspace-routing.test.ts +++ b/packages/opencode/test/server/httpapi-workspace-routing.test.ts @@ -1,5 +1,4 @@ import { NodeHttpServer, NodeServices } from "@effect/platform-node" -import { Flag } from "@opencode-ai/core/flag/flag" import { describe, expect } from "bun:test" import { Context, Effect, Layer, Queue, Ref } from "effect" import { @@ -20,8 +19,6 @@ import { WorkspaceID } from "../../src/control-plane/schema" import type { WorkspaceAdapter } from "../../src/control-plane/types" import { Workspace } from "../../src/control-plane/workspace" import { WorkspaceTable } from "../../src/control-plane/workspace.sql" -import { InstanceBootstrap } from "../../src/project/bootstrap" -import { InstanceStore } from "../../src/project/instance-store" import { Project } from "../../src/project/project" import { WorkspacePaths } from "../../src/server/routes/instance/httpapi/groups/workspace" import { @@ -31,27 +28,22 @@ import { import { HEADER as FenceHeader } from "../../src/server/shared/fence" import { Database } from "../../src/storage/db" import { resetDatabase } from "../fixture/db" +import { workspaceLayerWithRuntimeFlags } from "../fixture/workspace" import { tmpdirScoped } from "../fixture/fixture" import { testEffect } from "../lib/effect" const testStateLayer = Layer.effectDiscard( Effect.gen(function* () { - const originalWorkspaces = Flag.KILO_EXPERIMENTAL_WORKSPACES yield* Effect.promise(() => resetDatabase()) - Flag.KILO_EXPERIMENTAL_WORKSPACES = true yield* Effect.addFinalizer(() => Effect.promise(async () => { - Flag.KILO_EXPERIMENTAL_WORKSPACES = originalWorkspaces await resetDatabase() }), ) }), ) -const workspaceLayer = Workspace.defaultLayer.pipe( - Layer.provide(InstanceStore.defaultLayer), - Layer.provide(InstanceBootstrap.defaultLayer), -) +const workspaceLayer = workspaceLayerWithRuntimeFlags({ experimentalWorkspaces: true }) const it = testEffect( Layer.mergeAll( diff --git a/packages/opencode/test/server/negative-tokens-regression.test.ts b/packages/opencode/test/server/negative-tokens-regression.test.ts index 77ad1bc279a..290023ead75 100644 --- a/packages/opencode/test/server/negative-tokens-regression.test.ts +++ b/packages/opencode/test/server/negative-tokens-regression.test.ts @@ -5,11 +5,10 @@ // negative. The pre-fix `safe()` clamp only guarded against non-finite. The // strict `NonNegativeInt` schema then made every load of the message list // fail to encode, killing Desktop boot for every user with such a row. -import { afterEach, describe, expect } from "bun:test" +import { describe, expect } from "bun:test" import { Effect } from "effect" import { eq } from "drizzle-orm" import { ModelID, ProviderID } from "../../src/provider/schema" -import { WithInstance } from "../../src/project/with-instance" import { Server } from "../../src/server/server" import { SessionPaths } from "../../src/server/routes/instance/httpapi/groups/session" import { Session } from "@/session/session" @@ -17,81 +16,66 @@ import { MessageID, PartID } from "../../src/session/schema" import * as Database from "@/storage/db" import { PartTable } from "@/session/session.sql" import { resetDatabase } from "../fixture/db" -import { disposeAllInstances, tmpdir } from "../fixture/fixture" -import { it } from "../lib/effect" +import { TestInstance } from "../fixture/fixture" +import { testEffect } from "../lib/effect" -afterEach(async () => { - await disposeAllInstances() - await resetDatabase() -}) +const it = testEffect(Session.defaultLayer) -function seedNegativeTokenSession(directory: string) { - return Effect.promise(async () => - WithInstance.provide({ - directory, - fn: () => - Effect.runPromise( - Effect.gen(function* () { - const session = yield* Session.Service - const info = yield* session.create({}) - const message = yield* session.updateMessage({ - id: MessageID.ascending(), - role: "user", - sessionID: info.id, - agent: "build", - model: { providerID: ProviderID.make("test"), modelID: ModelID.make("test") }, - time: { created: Date.now() }, - }) - const partID = PartID.ascending() - yield* session.updatePart({ - id: partID, - sessionID: info.id, - messageID: message.id, - type: "step-finish", - reason: "stop", - cost: 0, - tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }, - }) +function seedNegativeTokenSession() { + return Effect.gen(function* () { + const session = yield* Session.Service + const info = yield* session.create({}) + const message = yield* session.updateMessage({ + id: MessageID.ascending(), + role: "user", + sessionID: info.id, + agent: "build", + model: { providerID: ProviderID.make("test"), modelID: ModelID.make("test") }, + time: { created: Date.now() }, + }) + const partID = PartID.ascending() + yield* session.updatePart({ + id: partID, + sessionID: info.id, + messageID: message.id, + type: "step-finish", + reason: "stop", + cost: 0, + tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }, + }) - // Bypass the schema with a direct SQL update to install the - // negative `output` value we want to test loading. - Database.use((db) => - db - .update(PartTable) - .set({ - data: { - type: "step-finish", - reason: "stop", - cost: 0, - tokens: { input: 0, output: -42, reasoning: 0, cache: { read: 0, write: 0 } }, - } as never, - }) - .where(eq(PartTable.id, partID)) - .run(), - ) + // Bypass the schema with a direct SQL update to install the + // negative `output` value we want to test loading. + Database.use((db) => + db + .update(PartTable) + .set({ + data: { + type: "step-finish", + reason: "stop", + cost: 0, + tokens: { input: 0, output: -42, reasoning: 0, cache: { read: 0, write: 0 } }, + } as never, + }) + .where(eq(PartTable.id, partID)) + .run(), + ) - return info.id - }).pipe(Effect.provide(Session.defaultLayer)), - ), - }), - ) + return info.id + }) } describe("messages endpoint tolerates legacy negative token counts", () => { - it.live( + it.instance( "returns 200 even when a step-finish part has tokens.output < 0", - Effect.acquireRelease( - Effect.promise(() => tmpdir({ config: { formatter: false, lsp: false } })), - (tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()), - ).pipe( - Effect.flatMap((tmp) => - Effect.gen(function* () { - const sessionID = yield* seedNegativeTokenSession(tmp.path) - const url = `${SessionPaths.messages.replace(":sessionID", sessionID)}?limit=80&directory=${encodeURIComponent(tmp.path)}` - const res = yield* Effect.promise(async () => Server.Default().app.request(url)) - expect(res.status, "messages endpoint 400'd on legacy negative tokens").not.toBe(400) - }), - ), - ), + Effect.gen(function* () { + yield* Effect.addFinalizer(() => Effect.promise(() => resetDatabase())) + const test = yield* TestInstance + const sessionID = yield* seedNegativeTokenSession() + const url = `${SessionPaths.messages.replace(":sessionID", sessionID)}?limit=80&directory=${encodeURIComponent(test.directory)}` + const res = yield* Effect.promise(async () => Server.Default().app.request(url)) + expect(res.status, "messages endpoint 400'd on legacy negative tokens").not.toBe(400) + }), + { git: true, config: { formatter: false, lsp: false } }, ) }) diff --git a/packages/opencode/test/server/project-init-git.test.ts b/packages/opencode/test/server/project-init-git.test.ts index 5f2bd99efe0..ff7e5f56c73 100644 --- a/packages/opencode/test/server/project-init-git.test.ts +++ b/packages/opencode/test/server/project-init-git.test.ts @@ -1,113 +1,120 @@ -import { afterEach, describe, expect, test } from "bun:test" -import { Effect } from "effect" +import { afterEach, describe, expect } from "bun:test" +import { AppFileSystem } from "@opencode-ai/core/filesystem" +import { Effect, Layer } from "effect" import path from "path" -import { GlobalBus } from "../../src/bus/global" +import { InstanceRef } from "../../src/effect/instance-ref" +import { InstanceBootstrap } from "../../src/project/bootstrap-service" +import { InstanceStore } from "../../src/project/instance-store" +import { GlobalBus, type GlobalEvent } from "../../src/bus/global" import { Snapshot } from "../../src/snapshot" import { Server } from "../../src/server/server" -import { Filesystem } from "@/util/filesystem" import * as Log from "@opencode-ai/core/util/log" import { resetDatabase } from "../fixture/db" -import { disposeAllInstances, provideInstance, tmpdir } from "../fixture/fixture" +import { disposeAllInstances, TestInstance } from "../fixture/fixture" +import { testEffect } from "../lib/effect" void Log.init({ print: false }) afterEach(async () => { + await disposeAllInstances() await resetDatabase() }) -const disposedEvents = (seen: { directory?: string; payload: { type: string } }[], dir: string) => +const noopBootstrap = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void })) +const testInstanceStore = InstanceStore.defaultLayer.pipe(Layer.provide(noopBootstrap)) + +const it = testEffect(Layer.mergeAll(AppFileSystem.defaultLayer, Snapshot.defaultLayer, testInstanceStore)) + +function request(directory: string, url: string, init: RequestInit = {}) { + return Effect.promise(() => { + const headers = new Headers(init.headers) + headers.set("x-kilo-directory", directory) + return Promise.resolve(Server.Default().app.request(url, { ...init, headers })) + }) +} + +function json(response: Response) { + return Effect.promise(() => response.json() as Promise) +} + +function collectGlobalEvents() { + return Effect.acquireRelease( + Effect.sync(() => { + const seen: GlobalEvent[] = [] + const on = (event: GlobalEvent) => { + seen.push(event) + } + GlobalBus.on("event", on) + return { seen, on } + }), + ({ on }) => Effect.sync(() => GlobalBus.off("event", on)), + ) +} + +const disposedEvents = (seen: GlobalEvent[], dir: string) => seen.filter((evt) => evt.directory === dir && evt.payload.type === "server.instance.disposed").length describe("project.initGit endpoint", () => { - test("initializes git and reloads immediately", async () => { - await using tmp = await tmpdir() - const app = Server.Default().app - const seen: { directory?: string; payload: { type: string } }[] = [] - const fn = (evt: { directory?: string; payload: { type: string } }) => { - seen.push(evt) - } - GlobalBus.on("event", fn) + it.instance("initializes git and reloads immediately", () => + Effect.gen(function* () { + const tmp = yield* TestInstance + const fs = yield* AppFileSystem.Service + const events = yield* collectGlobalEvents() - try { - const init = await app.request("/project/git/init", { + const init = yield* request(tmp.directory, "/project/git/init", { method: "POST", - headers: { - "x-kilo-directory": tmp.path, - }, }) - const body = await init.json() + const body = yield* json(init) expect(init.status).toBe(200) expect(body).toMatchObject({ id: "global", vcs: "git", - worktree: tmp.path, + worktree: tmp.directory, }) // Reload behavior: bus emits exactly one server.instance.disposed for the directory. - expect(disposedEvents(seen, tmp.path)).toBe(1) - expect(await Filesystem.exists(path.join(tmp.path, ".git", "opencode"))).toBe(false) + expect(disposedEvents(events.seen, tmp.directory)).toBe(1) + expect(yield* fs.exists(path.join(tmp.directory, ".git", "opencode"))).toBe(false) - const current = await app.request("/project/current", { - headers: { - "x-kilo-directory": tmp.path, - }, - }) + const current = yield* request(tmp.directory, "/project/current") expect(current.status).toBe(200) - expect(await current.json()).toMatchObject({ + expect(yield* json(current)).toMatchObject({ id: "global", vcs: "git", - worktree: tmp.path, + worktree: tmp.directory, }) - expect( - await Effect.runPromise( - Snapshot.Service.use((svc) => svc.track()).pipe( - provideInstance(tmp.path), - Effect.provide(Snapshot.defaultLayer), - ), - ), - ).toBeTruthy() - } finally { - await disposeAllInstances() - GlobalBus.off("event", fn) - } - }) + const ctx = yield* InstanceStore.Service.use((store) => store.reload({ directory: tmp.directory })) + const tracked = yield* Snapshot.Service.use((snapshot) => snapshot.track()).pipe( + Effect.provideService(InstanceRef, ctx), + ) + expect(tracked).toBeTruthy() + }), + ) - test("does not reload when the project is already git", async () => { - await using tmp = await tmpdir({ git: true }) - const app = Server.Default().app - const seen: { directory?: string; payload: { type: string } }[] = [] - const fn = (evt: { directory?: string; payload: { type: string } }) => { - seen.push(evt) - } - GlobalBus.on("event", fn) + it.instance( + "does not reload when the project is already git", + () => + Effect.gen(function* () { + const tmp = yield* TestInstance + const events = yield* collectGlobalEvents() - try { - const init = await app.request("/project/git/init", { - method: "POST", - headers: { - "x-kilo-directory": tmp.path, - }, - }) - expect(init.status).toBe(200) - expect(await init.json()).toMatchObject({ - vcs: "git", - worktree: tmp.path, - }) - expect(disposedEvents(seen, tmp.path)).toBe(0) + const init = yield* request(tmp.directory, "/project/git/init", { + method: "POST", + }) + expect(init.status).toBe(200) + expect(yield* json(init)).toMatchObject({ + vcs: "git", + worktree: tmp.directory, + }) + expect(disposedEvents(events.seen, tmp.directory)).toBe(0) - const current = await app.request("/project/current", { - headers: { - "x-kilo-directory": tmp.path, - }, - }) - expect(current.status).toBe(200) - expect(await current.json()).toMatchObject({ - vcs: "git", - worktree: tmp.path, - }) - } finally { - await disposeAllInstances() - GlobalBus.off("event", fn) - } - }) + const current = yield* request(tmp.directory, "/project/current") + expect(current.status).toBe(200) + expect(yield* json(current)).toMatchObject({ + vcs: "git", + worktree: tmp.directory, + }) + }), + { git: true }, + ) }) diff --git a/packages/opencode/test/server/session-actions.test.ts b/packages/opencode/test/server/session-actions.test.ts index 1ccc9bc8e62..4237a6f425f 100644 --- a/packages/opencode/test/server/session-actions.test.ts +++ b/packages/opencode/test/server/session-actions.test.ts @@ -1,28 +1,14 @@ -import { afterEach, describe, expect, mock, test } from "bun:test" +import { afterEach, describe, expect, mock } from "bun:test" import { Effect } from "effect" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" import { Server } from "../../src/server/server" import { Session as SessionNs } from "@/session/session" -import type { SessionID } from "../../src/session/schema" import * as Log from "@opencode-ai/core/util/log" -import { disposeAllInstances, tmpdir } from "../fixture/fixture" +import { disposeAllInstances, TestInstance } from "../fixture/fixture" +import { testEffect } from "../lib/effect" void Log.init({ print: false }) -function run(fx: Effect.Effect) { - return Effect.runPromise(fx.pipe(Effect.provide(SessionNs.defaultLayer))) -} - -const svc = { - ...SessionNs, - create(input?: SessionNs.CreateInput) { - return run(SessionNs.Service.use((svc) => svc.create(input))) - }, - remove(id: SessionID) { - return run(SessionNs.Service.use((svc) => svc.remove(id))) - }, -} +const it = testEffect(SessionNs.defaultLayer) afterEach(async () => { mock.restore() @@ -30,21 +16,28 @@ afterEach(async () => { }) describe("session action routes", () => { - test("abort route returns success", async () => { - await using tmp = await tmpdir({ git: true }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const session = await svc.create({}) - const app = Server.Default().app - - const res = await app.request(`/session/${session.id}/abort`, { method: "POST" }) + it.instance( + "abort route returns success", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const session = yield* Effect.acquireRelease( + SessionNs.Service.use((svc) => svc.create({})), + (created) => SessionNs.Service.use((svc) => svc.remove(created.id)).pipe(Effect.ignore), + ) + + const res = yield* Effect.promise(() => + Promise.resolve( + Server.Default().app.request(`/session/${session.id}/abort`, { + method: "POST", + headers: { "x-kilo-directory": test.directory }, + }), + ), + ) expect(res.status).toBe(200) - expect(await res.json()).toBe(true) - - await svc.remove(session.id) - }, - }) - }) + expect(yield* Effect.promise(() => res.json())).toBe(true) + }), + { git: true }, + ) }) diff --git a/packages/opencode/test/server/session-diff-missing-patch.test.ts b/packages/opencode/test/server/session-diff-missing-patch.test.ts index 8079fc5ce43..3d9e74d87d8 100644 --- a/packages/opencode/test/server/session-diff-missing-patch.test.ts +++ b/packages/opencode/test/server/session-diff-missing-patch.test.ts @@ -10,19 +10,20 @@ * asserts that GET /session//diff returns 200 with the row intact. */ import { afterEach, describe, expect } from "bun:test" -import { Effect } from "effect" +import { Effect, Layer } from "effect" import { Server } from "@/server/server" import { SessionPaths } from "@/server/routes/instance/httpapi/groups/session" import { Session } from "@/session/session" import { Storage } from "@/storage/storage" -import { WithInstance } from "@/project/with-instance" import { resetDatabase } from "../fixture/db" -import { disposeAllInstances, tmpdir } from "../fixture/fixture" -import { it } from "../lib/effect" +import { disposeAllInstances, TestInstance } from "../fixture/fixture" +import { testEffect } from "../lib/effect" import * as Log from "@opencode-ai/core/util/log" void Log.init({ print: false }) +const it = testEffect(Layer.mergeAll(Session.defaultLayer, Storage.defaultLayer)) + afterEach(async () => { await disposeAllInstances() await resetDatabase() @@ -32,50 +33,46 @@ function pathFor(template: string, params: Record) { return Object.entries(params).reduce((result, [key, value]) => result.replace(`:${key}`, value), template) } +const withSession = (input?: Parameters[0]) => + Effect.acquireRelease( + Session.Service.use((session) => session.create(input)), + (created) => Session.Service.use((session) => session.remove(created.id)).pipe(Effect.ignore), + ) + describe("session diff with missing patch (#26574)", () => { - it.live("GET /session//diff returns 200 when summary_diffs row has no patch", () => - Effect.gen(function* () { - const tmp = yield* Effect.acquireRelease( - Effect.promise(() => tmpdir({ git: true, config: { formatter: false, lsp: false } })), - (t) => Effect.promise(() => t[Symbol.asyncDispose]()), - ) + it.instance( + "GET /session//diff returns 200 when summary_diffs row has no patch", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const session = yield* withSession({ title: "missing-patch" }) - yield* Effect.promise(() => - WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const session = await Effect.runPromise( - Effect.provide( - Session.Service.use((s) => s.create({ title: "missing-patch" })), - Session.defaultLayer, - ), - ) + // Mimic legacy/imported on-disk shape: a diff entry with no + // `patch` text. Pre-fix the typed response encoder rejects + // this and returns 400. + yield* Storage.Service.use((storage) => + storage.write(["session_diff", session.id], [{ file: "legacy.txt", additions: 1, deletions: 0 }]), + ) - // Mimic legacy/imported on-disk shape: a diff entry with no - // `patch` text. Pre-fix the typed response encoder rejects - // this and returns 400. - await Effect.runPromise( - Effect.provide( - Storage.Service.use((s) => - s.write(["session_diff", session.id], [{ file: "legacy.txt", additions: 1, deletions: 0 }]), - ), - Storage.defaultLayer, - ), - ) + const response = yield* Effect.promise(() => + Promise.resolve( + Server.Default().app.request(pathFor(SessionPaths.diff, { sessionID: session.id }), { + headers: { "x-kilo-directory": test.directory }, + }), + ), + ) - const headers = { "x-kilo-directory": tmp.path } - const response = await Server.Default().app.request(pathFor(SessionPaths.diff, { sessionID: session.id }), { - headers, - }) - expect(response.status).toBe(200) - const body = (await response.json()) as Array<{ file: string; patch?: string; additions: number }> - expect(body).toHaveLength(1) - expect(body[0]?.file).toBe("legacy.txt") - expect(body[0]?.additions).toBe(1) - expect(body[0]?.patch).toBeUndefined() - }, - }), - ) - }), + expect(response.status).toBe(200) + const body = (yield* Effect.promise(() => response.json())) as Array<{ + file: string + patch?: string + additions: number + }> + expect(body).toHaveLength(1) + expect(body[0]?.file).toBe("legacy.txt") + expect(body[0]?.additions).toBe(1) + expect(body[0]?.patch).toBeUndefined() + }), + { git: true, config: { formatter: false, lsp: false } }, ) }) diff --git a/packages/opencode/test/server/session-list.test.ts b/packages/opencode/test/server/session-list.test.ts index dda5a746fa6..4aab0c4b1f3 100644 --- a/packages/opencode/test/server/session-list.test.ts +++ b/packages/opencode/test/server/session-list.test.ts @@ -1,239 +1,231 @@ -import { afterEach, describe, expect, test } from "bun:test" -import { Effect } from "effect" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" +import { afterEach, describe, expect } from "bun:test" +import { Effect, Layer } from "effect" import { Session as SessionNs } from "@/session/session" import * as Log from "@opencode-ai/core/util/log" -import { disposeAllInstances, tmpdir } from "../fixture/fixture" -import { Flag } from "@opencode-ai/core/flag/flag" +import { disposeAllInstances, provideInstance, TestInstance } from "../fixture/fixture" import { mkdir } from "fs/promises" import path from "path" import { Database } from "@/storage/db" import { SessionTable } from "@/session/session.sql" import { eq } from "drizzle-orm" +import { testEffect } from "../lib/effect" +import { Bus } from "@/bus" +import { Storage } from "@/storage/storage" +import { SyncEvent } from "@/sync" +import { RuntimeFlags } from "@/effect/runtime-flags" +import { BackgroundJob } from "@/background/job" void Log.init({ print: false }) -const originalWorkspaces = Flag.KILO_EXPERIMENTAL_WORKSPACES - -function run(fx: Effect.Effect) { - return Effect.runPromise(fx.pipe(Effect.provide(SessionNs.defaultLayer))) -} - -const svc = { - ...SessionNs, - create(input?: SessionNs.CreateInput) { - return run(SessionNs.Service.use((svc) => svc.create(input))) - }, - list(input?: SessionNs.ListInput) { - return run(SessionNs.Service.use((svc) => svc.list(input))) - }, -} +const it = testEffect( + SessionNs.layer.pipe( + Layer.provide(Bus.layer), + Layer.provide(Storage.defaultLayer), + Layer.provide(SyncEvent.defaultLayer), + Layer.provide(RuntimeFlags.layer({ experimentalWorkspaces: false })), + Layer.provide(BackgroundJob.defaultLayer), + ), +) + +const withSession = (input?: Parameters[0]) => + Effect.acquireRelease( + SessionNs.Service.use((session) => session.create(input)), + (created) => SessionNs.Service.use((session) => session.remove(created.id).pipe(Effect.ignore)), + ) afterEach(async () => { - Flag.KILO_EXPERIMENTAL_WORKSPACES = originalWorkspaces await disposeAllInstances() }) describe("session.list", () => { - test("does not filter by directory when directory is omitted", async () => { - Flag.KILO_EXPERIMENTAL_WORKSPACES = false - await using tmp = await tmpdir({ git: true }) - await mkdir(path.join(tmp.path, "packages", "opencode"), { recursive: true }) - await mkdir(path.join(tmp.path, "packages", "app"), { recursive: true }) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const root = await svc.create({ title: "root" }) - - const parent = await WithInstance.provide({ - directory: path.join(tmp.path, "packages"), - fn: async () => svc.create({ title: "parent" }), - }) - const current = await WithInstance.provide({ - directory: path.join(tmp.path, "packages", "opencode"), - fn: async () => svc.create({ title: "current" }), - }) - const sibling = await WithInstance.provide({ - directory: path.join(tmp.path, "packages", "app"), - fn: async () => svc.create({ title: "sibling" }), - }) - - const ids = (await svc.list()).map((s) => s.id) + it.instance( + "does not filter by directory when directory is omitted", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => mkdir(path.join(test.directory, "packages", "opencode"), { recursive: true })) + yield* Effect.promise(() => mkdir(path.join(test.directory, "packages", "app"), { recursive: true })) + + const root = yield* withSession({ title: "root" }) + const parent = yield* withSession({ title: "parent" }).pipe( + provideInstance(path.join(test.directory, "packages")), + ) + const current = yield* withSession({ title: "current" }).pipe( + provideInstance(path.join(test.directory, "packages", "opencode")), + ) + const sibling = yield* withSession({ title: "sibling" }).pipe( + provideInstance(path.join(test.directory, "packages", "app")), + ) + + const ids = (yield* SessionNs.Service.use((session) => session.list())).map((session) => session.id) expect(ids).toContain(root.id) expect(ids).toContain(parent.id) expect(ids).toContain(current.id) expect(ids).toContain(sibling.id) - }, - }) - }) - - test("filters by directory when directory is provided", async () => { - Flag.KILO_EXPERIMENTAL_WORKSPACES = false - await using tmp = await tmpdir({ git: true }) - await mkdir(path.join(tmp.path, "packages", "opencode"), { recursive: true }) - await mkdir(path.join(tmp.path, "packages", "app"), { recursive: true }) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const root = await svc.create({ title: "root" }) - - const parent = await WithInstance.provide({ - directory: path.join(tmp.path, "packages"), - fn: async () => svc.create({ title: "parent" }), - }) - const current = await WithInstance.provide({ - directory: path.join(tmp.path, "packages", "opencode"), - fn: async () => svc.create({ title: "current" }), - }) - const sibling = await WithInstance.provide({ - directory: path.join(tmp.path, "packages", "app"), - fn: async () => svc.create({ title: "sibling" }), - }) - - const ids = (await svc.list({ directory: path.join(tmp.path, "packages", "opencode") })).map((s) => s.id) + }), + { git: true }, + ) + + it.instance( + "filters by directory when directory is provided", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => mkdir(path.join(test.directory, "packages", "opencode"), { recursive: true })) + yield* Effect.promise(() => mkdir(path.join(test.directory, "packages", "app"), { recursive: true })) + + const root = yield* withSession({ title: "root" }) + const parent = yield* withSession({ title: "parent" }).pipe( + provideInstance(path.join(test.directory, "packages")), + ) + const current = yield* withSession({ title: "current" }).pipe( + provideInstance(path.join(test.directory, "packages", "opencode")), + ) + const sibling = yield* withSession({ title: "sibling" }).pipe( + provideInstance(path.join(test.directory, "packages", "app")), + ) + + const ids = (yield* SessionNs.Service.use((session) => + session.list({ directory: path.join(test.directory, "packages", "opencode") }), + )).map((session) => session.id) expect(ids).not.toContain(root.id) expect(ids).not.toContain(parent.id) expect(ids).toContain(current.id) expect(ids).not.toContain(sibling.id) - }, - }) - }) - - test("filters by path and ignores directory when path is provided", async () => { - Flag.KILO_EXPERIMENTAL_WORKSPACES = false - await using tmp = await tmpdir({ git: true }) - await mkdir(path.join(tmp.path, "packages", "opencode", "src", "deep"), { recursive: true }) - await mkdir(path.join(tmp.path, "packages", "app"), { recursive: true }) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const parent = await WithInstance.provide({ - directory: path.join(tmp.path, "packages", "opencode"), - fn: async () => svc.create({ title: "parent" }), - }) - const current = await WithInstance.provide({ - directory: path.join(tmp.path, "packages", "opencode", "src"), - fn: async () => svc.create({ title: "current" }), - }) - const deeper = await WithInstance.provide({ - directory: path.join(tmp.path, "packages", "opencode", "src", "deep"), - fn: async () => svc.create({ title: "deeper" }), - }) - const sibling = await WithInstance.provide({ - directory: path.join(tmp.path, "packages", "app"), - fn: async () => svc.create({ title: "sibling" }), - }) - - const pathIDs = ( - await svc.list({ - directory: path.join(tmp.path, "packages", "app"), + }), + { git: true }, + ) + + it.instance( + "filters by path and ignores directory when path is provided", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => + mkdir(path.join(test.directory, "packages", "opencode", "src", "deep"), { recursive: true }), + ) + yield* Effect.promise(() => mkdir(path.join(test.directory, "packages", "app"), { recursive: true })) + + const parent = yield* withSession({ title: "parent" }).pipe( + provideInstance(path.join(test.directory, "packages", "opencode")), + ) + const current = yield* withSession({ title: "current" }).pipe( + provideInstance(path.join(test.directory, "packages", "opencode", "src")), + ) + const deeper = yield* withSession({ title: "deeper" }).pipe( + provideInstance(path.join(test.directory, "packages", "opencode", "src", "deep")), + ) + const sibling = yield* withSession({ title: "sibling" }).pipe( + provideInstance(path.join(test.directory, "packages", "app")), + ) + + const pathIDs = (yield* SessionNs.Service.use((session) => + session.list({ + directory: path.join(test.directory, "packages", "app"), path: "packages/opencode/src", - }) - ).map((s) => s.id) + }), + )).map((session) => session.id) expect(pathIDs).not.toContain(parent.id) expect(pathIDs).toContain(current.id) expect(pathIDs).toContain(deeper.id) expect(pathIDs).not.toContain(sibling.id) - }, - }) - }) - - test("falls back to directory when filtering legacy sessions without path", async () => { - Flag.KILO_EXPERIMENTAL_WORKSPACES = false - await using tmp = await tmpdir({ git: true }) - await mkdir(path.join(tmp.path, "packages", "opencode", "src"), { recursive: true }) - await mkdir(path.join(tmp.path, "packages", "app"), { recursive: true }) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const current = await WithInstance.provide({ - directory: path.join(tmp.path, "packages", "opencode", "src"), - fn: async () => svc.create({ title: "legacy-current" }), - }) - const sibling = await WithInstance.provide({ - directory: path.join(tmp.path, "packages", "app"), - fn: async () => svc.create({ title: "legacy-sibling" }), - }) - - Database.use((db) => db.update(SessionTable).set({ path: null }).where(eq(SessionTable.id, current.id)).run()) - Database.use((db) => db.update(SessionTable).set({ path: null }).where(eq(SessionTable.id, sibling.id)).run()) - - const pathIDs = ( - await svc.list({ - directory: path.join(tmp.path, "packages", "opencode", "src"), + }), + { git: true }, + ) + + it.instance( + "falls back to directory when filtering legacy sessions without path", + () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* Effect.promise(() => + mkdir(path.join(test.directory, "packages", "opencode", "src"), { recursive: true }), + ) + yield* Effect.promise(() => mkdir(path.join(test.directory, "packages", "app"), { recursive: true })) + + const current = yield* withSession({ title: "legacy-current" }).pipe( + provideInstance(path.join(test.directory, "packages", "opencode", "src")), + ) + const sibling = yield* withSession({ title: "legacy-sibling" }).pipe( + provideInstance(path.join(test.directory, "packages", "app")), + ) + + yield* Effect.sync(() => + Database.use((db) => + db.update(SessionTable).set({ path: null }).where(eq(SessionTable.id, current.id)).run(), + ), + ) + yield* Effect.sync(() => + Database.use((db) => + db.update(SessionTable).set({ path: null }).where(eq(SessionTable.id, sibling.id)).run(), + ), + ) + + const pathIDs = (yield* SessionNs.Service.use((session) => + session.list({ + directory: path.join(test.directory, "packages", "opencode", "src"), path: "packages/opencode/src", - }) - ).map((s) => s.id) + }), + )).map((session) => session.id) expect(pathIDs).toContain(current.id) expect(pathIDs).not.toContain(sibling.id) - }, - }) - }) + }), + { git: true }, + ) - test("filters root sessions", async () => { - await using tmp = await tmpdir({ git: true }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const root = await svc.create({ title: "root-session" }) - const child = await svc.create({ title: "child-session", parentID: root.id }) + it.instance( + "filters root sessions", + () => + Effect.gen(function* () { + const root = yield* withSession({ title: "root-session" }) + const child = yield* withSession({ title: "child-session", parentID: root.id }) - const sessions = await svc.list({ roots: true }) - const ids = sessions.map((s) => s.id) + const sessions = yield* SessionNs.Service.use((session) => session.list({ roots: true })) + const ids = sessions.map((session) => session.id) expect(ids).toContain(root.id) expect(ids).not.toContain(child.id) - }, - }) - }) - - test("filters by start time", async () => { - await using tmp = await tmpdir({ git: true }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await svc.create({ title: "new-session" }) - const futureStart = Date.now() + 86400000 - - const sessions = await svc.list({ start: futureStart }) + }), + { git: true }, + ) + + it.instance( + "filters by start time", + () => + Effect.gen(function* () { + yield* withSession({ title: "new-session" }) + const sessions = yield* SessionNs.Service.use((session) => session.list({ start: Date.now() + 86400000 })) expect(sessions.length).toBe(0) - }, - }) - }) + }), + { git: true }, + ) - test("filters by search term", async () => { - await using tmp = await tmpdir({ git: true }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await svc.create({ title: "unique-search-term-abc" }) - await svc.create({ title: "other-session-xyz" }) + it.instance( + "filters by search term", + () => + Effect.gen(function* () { + yield* withSession({ title: "unique-search-term-abc" }) + yield* withSession({ title: "other-session-xyz" }) - const sessions = await svc.list({ search: "unique-search" }) - const titles = sessions.map((s) => s.title) + const sessions = yield* SessionNs.Service.use((session) => session.list({ search: "unique-search" })) + const titles = sessions.map((session) => session.title) expect(titles).toContain("unique-search-term-abc") expect(titles).not.toContain("other-session-xyz") - }, - }) - }) - - test("respects limit parameter", async () => { - await using tmp = await tmpdir({ git: true }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await svc.create({ title: "session-1" }) - await svc.create({ title: "session-2" }) - await svc.create({ title: "session-3" }) - - const sessions = await svc.list({ limit: 2 }) + }), + { git: true }, + ) + + it.instance( + "respects limit parameter", + () => + Effect.gen(function* () { + yield* withSession({ title: "session-1" }) + yield* withSession({ title: "session-2" }) + yield* withSession({ title: "session-3" }) + + const sessions = yield* SessionNs.Service.use((session) => session.list({ limit: 2 })) expect(sessions.length).toBe(2) - }, - }) - }) + }), + { git: true }, + ) }) diff --git a/packages/opencode/test/server/session-messages.test.ts b/packages/opencode/test/server/session-messages.test.ts index 1ef8b490a21..a256770fda7 100644 --- a/packages/opencode/test/server/session-messages.test.ts +++ b/packages/opencode/test/server/session-messages.test.ts @@ -1,191 +1,179 @@ -import { afterEach, describe, expect, test } from "bun:test" +import { afterEach, describe, expect } from "bun:test" import { Effect } from "effect" -import { WithInstance } from "../../src/project/with-instance" import { Server } from "../../src/server/server" import { Session as SessionNs } from "@/session/session" import { MessageV2 } from "../../src/session/message-v2" +import { ModelID, ProviderID } from "../../src/provider/schema" import { MessageID, PartID, type SessionID } from "../../src/session/schema" import * as Log from "@opencode-ai/core/util/log" -import { disposeAllInstances, tmpdir } from "../fixture/fixture" +import { disposeAllInstances, TestInstance } from "../fixture/fixture" +import { testEffect } from "../lib/effect" void Log.init({ print: false }) -function run(fx: Effect.Effect) { - return Effect.runPromise(fx.pipe(Effect.provide(SessionNs.defaultLayer))) -} +const it = testEffect(SessionNs.defaultLayer) -const svc = { - ...SessionNs, - create(input?: SessionNs.CreateInput) { - return run(SessionNs.Service.use((svc) => svc.create(input))) - }, - remove(id: SessionID) { - return run(SessionNs.Service.use((svc) => svc.remove(id))) - }, - updateMessage(msg: T) { - return run(SessionNs.Service.use((svc) => svc.updateMessage(msg))) - }, - updatePart(part: T) { - return run(SessionNs.Service.use((svc) => svc.updatePart(part))) - }, +const model = { + providerID: ProviderID.make("test"), + modelID: ModelID.make("test"), } afterEach(async () => { await disposeAllInstances() }) -async function withoutWatcher(fn: () => Promise) { - if (process.platform !== "win32") return fn() - const prev = process.env.KILO_EXPERIMENTAL_DISABLE_FILEWATCHER - process.env.KILO_EXPERIMENTAL_DISABLE_FILEWATCHER = "true" - try { - return await fn() - } finally { - if (prev === undefined) delete process.env.KILO_EXPERIMENTAL_DISABLE_FILEWATCHER - else process.env.KILO_EXPERIMENTAL_DISABLE_FILEWATCHER = prev - } +const withoutWatcher = (effect: Effect.Effect) => { + if (process.platform !== "win32") return effect + return Effect.acquireUseRelease( + Effect.sync(() => { + const previous = process.env.KILO_EXPERIMENTAL_DISABLE_FILEWATCHER + process.env.KILO_EXPERIMENTAL_DISABLE_FILEWATCHER = "true" + return previous + }), + () => effect, + (previous) => + Effect.sync(() => { + if (previous === undefined) delete process.env.KILO_EXPERIMENTAL_DISABLE_FILEWATCHER + else process.env.KILO_EXPERIMENTAL_DISABLE_FILEWATCHER = previous + }), + ) +} + +const sessionScoped = Effect.acquireRelease( + SessionNs.Service.use((svc) => svc.create({})), + (session) => SessionNs.Service.use((svc) => svc.remove(session.id)).pipe(Effect.ignore), +) + +const fill = Effect.fn("SessionMessagesTest.fill")(function* ( + sessionID: SessionID, + count: number, + time = (i: number) => Date.now() + i, +) { + const session = yield* SessionNs.Service + return yield* Effect.forEach( + Array.from({ length: count }, (_, i) => i), + (i) => + Effect.gen(function* () { + const id = MessageID.ascending() + yield* session.updateMessage({ + id, + sessionID, + role: "user", + time: { created: time(i) }, + agent: "test", + model, + tools: {}, + } satisfies MessageV2.User) + yield* session.updatePart({ + id: PartID.ascending(), + sessionID, + messageID: id, + type: "text", + text: `m${i}`, + } satisfies MessageV2.TextPart) + return id + }), + ) +}) + +function request(path: string) { + return Effect.promise(() => Promise.resolve(Server.Default().app.request(path))) } -async function fill(sessionID: SessionID, count: number, time = (i: number) => Date.now() + i) { - const ids = [] as MessageID[] - for (let i = 0; i < count; i++) { - const id = MessageID.ascending() - ids.push(id) - await svc.updateMessage({ - id, - sessionID, - role: "user", - time: { created: time(i) }, - agent: "test", - model: { providerID: "test", modelID: "test" }, - tools: {}, - mode: "", - } as unknown as MessageV2.Info) - await svc.updatePart({ - id: PartID.ascending(), - sessionID, - messageID: id, - type: "text", - text: `m${i}`, - }) - } - return ids +function json(response: Response) { + return Effect.promise(() => response.json() as Promise) } describe("session messages endpoint", () => { - test("returns cursor headers for older pages", async () => { - await using tmp = await tmpdir({ git: true }) - await withoutWatcher(() => - WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const session = await svc.create({}) - const ids = await fill(session.id, 5) - const app = Server.Default().app - - const a = await app.request(`/session/${session.id}/message?limit=2`) - expect(a.status).toBe(200) - const aBody = (await a.json()) as MessageV2.WithParts[] - expect(aBody.map((item) => item.info.id)).toEqual(ids.slice(-2)) - const cursor = a.headers.get("x-next-cursor") - expect(cursor).toBeTruthy() - expect(a.headers.get("link")).toContain('rel="next"') - - const b = await app.request(`/session/${session.id}/message?limit=2&before=${encodeURIComponent(cursor!)}`) - expect(b.status).toBe(200) - const bBody = (await b.json()) as MessageV2.WithParts[] - expect(bBody.map((item) => item.info.id)).toEqual(ids.slice(-4, -2)) - - await svc.remove(session.id) - }, + it.instance( + "returns cursor headers for older pages", + withoutWatcher( + Effect.gen(function* () { + const session = yield* sessionScoped + const ids = yield* fill(session.id, 5) + + const a = yield* request(`/session/${session.id}/message?limit=2`) + expect(a.status).toBe(200) + const aBody = yield* json(a) + expect(aBody.map((item) => item.info.id)).toEqual(ids.slice(-2)) + const cursor = a.headers.get("x-next-cursor") + expect(cursor).toBeTruthy() + expect(a.headers.get("link")).toContain('rel="next"') + + const b = yield* request(`/session/${session.id}/message?limit=2&before=${encodeURIComponent(cursor!)}`) + expect(b.status).toBe(200) + const bBody = yield* json(b) + expect(bBody.map((item) => item.info.id)).toEqual(ids.slice(-4, -2)) }), - ) - }) - - test("keeps full-history responses when limit is omitted", async () => { - await using tmp = await tmpdir({ git: true }) - await withoutWatcher(() => - WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const session = await svc.create({}) - const ids = await fill(session.id, 3) - const app = Server.Default().app - - const res = await app.request(`/session/${session.id}/message`) - expect(res.status).toBe(200) - const body = (await res.json()) as MessageV2.WithParts[] - expect(body.map((item) => item.info.id)).toEqual(ids) - - await svc.remove(session.id) - }, + ), + { git: true }, + ) + + it.instance( + "keeps full-history responses when limit is omitted", + withoutWatcher( + Effect.gen(function* () { + const session = yield* sessionScoped + const ids = yield* fill(session.id, 3) + + const res = yield* request(`/session/${session.id}/message`) + expect(res.status).toBe(200) + const body = yield* json(res) + expect(body.map((item) => item.info.id)).toEqual(ids) }), - ) - }) - - test("rejects invalid cursors and missing sessions", async () => { - await using tmp = await tmpdir({ git: true }) - await withoutWatcher(() => - WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const session = await svc.create({}) - const app = Server.Default().app - - const bad = await app.request(`/session/${session.id}/message?limit=2&before=bad`) - expect(bad.status).toBe(400) - - const miss = await app.request(`/session/ses_missing/message?limit=2`) - expect(miss.status).toBe(404) - - await svc.remove(session.id) - }, + ), + { git: true }, + ) + + it.instance( + "rejects invalid cursors and missing sessions", + withoutWatcher( + Effect.gen(function* () { + const session = yield* sessionScoped + + const bad = yield* request(`/session/${session.id}/message?limit=2&before=bad`) + expect(bad.status).toBe(400) + + const miss = yield* request(`/session/ses_missing/message?limit=2`) + expect(miss.status).toBe(404) }), - ) - }) - - test("does not truncate large legacy limit requests", async () => { - await using tmp = await tmpdir({ git: true }) - await withoutWatcher(() => - WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const session = await svc.create({}) - await fill(session.id, 520) - const app = Server.Default().app - - const res = await app.request(`/session/${session.id}/message?limit=510`) - expect(res.status).toBe(200) - const body = (await res.json()) as MessageV2.WithParts[] - expect(body).toHaveLength(510) - - await svc.remove(session.id) - }, + ), + { git: true }, + ) + + it.instance( + "does not truncate large legacy limit requests", + withoutWatcher( + Effect.gen(function* () { + const session = yield* sessionScoped + yield* fill(session.id, 520) + + const res = yield* request(`/session/${session.id}/message?limit=510`) + expect(res.status).toBe(200) + const body = yield* json(res) + expect(body).toHaveLength(510) }), - ) - }) - - test("accepts directory query used by workspace routing", async () => { - await using tmp = await tmpdir({ git: true }) - await withoutWatcher(() => - WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const session = await svc.create({}) - await fill(session.id, 1) - const app = Server.Default().app - - const res = await app.request( - `/session/${session.id}/message?limit=80&directory=${encodeURIComponent(tmp.path)}`, - ) - expect(res.status).toBe(200) - const body = await res.json() - expect(Array.isArray(body)).toBe(true) - expect(body).toHaveLength(1) - - await svc.remove(session.id) - }, + ), + { git: true }, + ) + + it.instance( + "accepts directory query used by workspace routing", + withoutWatcher( + Effect.gen(function* () { + const tmp = yield* TestInstance + const session = yield* sessionScoped + yield* fill(session.id, 1) + + const res = yield* request( + `/session/${session.id}/message?limit=80&directory=${encodeURIComponent(tmp.directory)}`, + ) + expect(res.status).toBe(200) + const body = yield* json(res) + expect(Array.isArray(body)).toBe(true) + expect(body).toHaveLength(1) }), - ) - }) + ), + { git: true }, + ) }) diff --git a/packages/opencode/test/server/session-select.test.ts b/packages/opencode/test/server/session-select.test.ts index 13edca14584..8754181937c 100644 --- a/packages/opencode/test/server/session-select.test.ts +++ b/packages/opencode/test/server/session-select.test.ts @@ -1,101 +1,93 @@ -import { afterEach, describe, expect, test } from "bun:test" +import { describe, expect } from "bun:test" import { Effect } from "effect" -import { Session as SessionNs } from "@/session/session" -import type { SessionID } from "../../src/session/schema" +import { Session } from "@/session/session" import * as Log from "@opencode-ai/core/util/log" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" import { Server } from "../../src/server/server" -import { disposeAllInstances, tmpdir } from "../fixture/fixture" +import { TestInstance } from "../fixture/fixture" +import { testEffect } from "../lib/effect" void Log.init({ print: false }) -function run(fx: Effect.Effect) { - return Effect.runPromise(fx.pipe(Effect.provide(SessionNs.defaultLayer))) -} - -const svc = { - ...SessionNs, - create(input?: SessionNs.CreateInput) { - return run(SessionNs.Service.use((svc) => svc.create(input))) - }, - remove(id: SessionID) { - return run(SessionNs.Service.use((svc) => svc.remove(id))) - }, -} - -afterEach(async () => { - await disposeAllInstances() -}) +const it = testEffect(Session.defaultLayer) describe("tui.selectSession endpoint", () => { - test("should return 200 when called with valid session", async () => { - await using tmp = await tmpdir({ git: true }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // #given - const session = await svc.create({}) + it.instance( + "should return 200 when called with valid session", + () => + Effect.gen(function* () { + const tmp = yield* TestInstance + const session = yield* Session.Service.use((svc) => svc.create({})) - // #when const app = Server.Default().app - const response = await app.request("/tui/select-session", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ sessionID: session.id }), - }) + const response = yield* Effect.promise(() => + Promise.resolve( + app.request("/tui/select-session", { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-kilo-directory": tmp.directory, + }, + body: JSON.stringify({ sessionID: session.id }), + }), + ), + ) - // #then expect(response.status).toBe(200) - const body = await response.json() + const body = yield* Effect.promise(() => response.json()) expect(body).toBe(true) + }), + { git: true }, + ) - await svc.remove(session.id) - }, - }) - }) - - test("should return 404 when session does not exist", async () => { - await using tmp = await tmpdir({ git: true }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // #given + it.instance( + "should return 404 when session does not exist", + () => + Effect.gen(function* () { + const tmp = yield* TestInstance const nonExistentSessionID = "ses_nonexistent123" - // #when const app = Server.Default().app - const response = await app.request("/tui/select-session", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ sessionID: nonExistentSessionID }), - }) + const response = yield* Effect.promise(() => + Promise.resolve( + app.request("/tui/select-session", { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-kilo-directory": tmp.directory, + }, + body: JSON.stringify({ sessionID: nonExistentSessionID }), + }), + ), + ) - // #then expect(response.status).toBe(404) - }, - }) - }) + }), + { git: true }, + ) - test("should return 400 when session ID format is invalid", async () => { - await using tmp = await tmpdir({ git: true }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // #given + it.instance( + "should return 400 when session ID format is invalid", + () => + Effect.gen(function* () { + const tmp = yield* TestInstance const invalidSessionID = "invalid_session_id" - // #when const app = Server.Default().app - const response = await app.request("/tui/select-session", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ sessionID: invalidSessionID }), - }) + const response = yield* Effect.promise(() => + Promise.resolve( + app.request("/tui/select-session", { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-kilo-directory": tmp.directory, + }, + body: JSON.stringify({ sessionID: invalidSessionID }), + }), + ), + ) - // #then expect(response.status).toBe(400) - }, - }) - }) + }), + { git: true }, + ) }) diff --git a/packages/opencode/test/server/worktree-endpoint-repro.test.ts b/packages/opencode/test/server/worktree-endpoint-repro.test.ts index 290857001df..3096bf44222 100644 --- a/packages/opencode/test/server/worktree-endpoint-repro.test.ts +++ b/packages/opencode/test/server/worktree-endpoint-repro.test.ts @@ -1,13 +1,14 @@ import { describe, expect } from "bun:test" -import { Effect, Layer } from "effect" +import { Effect, Layer, Queue } from "effect" import { HttpRouter } from "effect/unstable/http" import { Flag } from "@opencode-ai/core/flag/flag" -import { ExperimentalHttpApiServer } from "../../src/server/routes/instance/httpapi/server" +import { GlobalBus, type GlobalEvent } from "@/bus/global" +import { Worktree } from "@/worktree" +import { HttpApiApp } from "../../src/server/routes/instance/httpapi/server" import { ExperimentalPaths } from "../../src/server/routes/instance/httpapi/groups/experimental" import { WorkspacePaths } from "../../src/server/routes/instance/httpapi/groups/workspace" -import { withTimeout } from "../../src/util/timeout" import { resetDatabase } from "../fixture/db" -import { TestInstance } from "../fixture/fixture" +import { disposeAllInstances, TestInstance } from "../fixture/fixture" import { testEffect } from "../lib/effect" const stateLayer = Layer.effectDiscard( @@ -28,30 +29,127 @@ const stateLayer = Layer.effectDiscard( ) const it = testEffect(stateLayer) +const worktreeTest = process.platform === "win32" ? it.instance.skip : it.instance type TestServer = ReturnType +type CreatedWorktree = { directory: string } +type ScopedWorktree = { directory: string; body: CreatedWorktree; ready: Effect.Effect } function serverScoped() { return Effect.acquireRelease( - Effect.sync(() => HttpRouter.toWebHandler(ExperimentalHttpApiServer.routes, { disableLogger: true })), + Effect.sync(() => HttpRouter.toWebHandler(HttpApiApp.routes, { disableLogger: true })), (server) => Effect.promise(() => server.dispose()).pipe(Effect.ignore), ) } function request(server: TestServer, input: string, init?: RequestInit) { - return Effect.promise(() => - server.handler(new Request(new URL(input, "http://localhost"), init), ExperimentalHttpApiServer.context), - ) + return Effect.promise(() => server.handler(new Request(new URL(input, "http://localhost"), init), HttpApiApp.context)) } function withRequestTimeout(effect: Effect.Effect, label: string, ms = 5_000) { - return Effect.promise(() => withTimeout(Effect.runPromise(effect), ms, label)) + return effect.pipe( + Effect.timeoutOrElse({ + duration: `${ms} millis`, + orElse: () => Effect.fail(new Error(`${label} timed out after ${ms}ms`)), + }), + ) +} + +function json(response: Response) { + return Effect.promise(() => response.json() as Promise) +} + +function readyWatcher() { + return Effect.gen(function* () { + const events = yield* Queue.bounded(1) + const on = (event: GlobalEvent) => { + if (event.payload.type === Worktree.Event.Ready.type) Queue.offerUnsafe(events, event) + } + + GlobalBus.on("event", on) + yield* Effect.addFinalizer(() => Effect.sync(() => GlobalBus.off("event", on))) + + return (directory: string) => + Effect.gen(function* () { + while (true) { + const event = yield* Queue.take(events) + if (event.directory === directory) return + } + }).pipe( + Effect.timeoutOrElse({ + duration: "10 seconds", + orElse: () => Effect.fail(new Error(`timed out waiting for worktree.ready: ${directory}`)), + }), + ) + }) +} + +function removeCreatedWorktree(input: { + server: TestServer + rootDirectory: string + worktreeDirectory: string + ready: Effect.Effect +}) { + return Effect.gen(function* () { + yield* input.ready.pipe(Effect.timeout("1 second"), Effect.ignore) + yield* Effect.promise(() => disposeAllInstances()).pipe(Effect.ignore) + + const removed = yield* request( + input.server, + `${ExperimentalPaths.worktree}?directory=${encodeURIComponent(input.rootDirectory)}`, + { + method: "DELETE", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ directory: input.worktreeDirectory }), + }, + ) + if (removed.status !== 200) { + const message = yield* Effect.promise(() => removed.text()) + throw new Error(`failed to remove worktree: ${removed.status} ${message}`) + } + const ok = yield* json(removed) + if (!ok) throw new Error(`failed to remove worktree ${input.worktreeDirectory}`) + }) +} + +function createWorktreeScoped(input: { + server: TestServer + directory: string + path: string + init: RequestInit + timeoutLabel: string + timeoutMs?: number +}) { + return Effect.acquireRelease( + Effect.gen(function* () { + const waitReady = yield* readyWatcher() + const response = yield* withRequestTimeout( + request(input.server, input.path, input.init), + input.timeoutLabel, + input.timeoutMs, + ) + if (response.status !== 200) { + const message = yield* Effect.promise(() => response.text()) + throw new Error(`${input.timeoutLabel} failed: ${response.status} ${message}`) + } + expect(response.status).toBe(200) + const body = yield* json(response) + return { directory: body.directory, body, ready: waitReady(body.directory) } satisfies ScopedWorktree + }), + (created) => + removeCreatedWorktree({ + server: input.server, + rootDirectory: input.directory, + worktreeDirectory: created.directory, + ready: created.ready, + }).pipe(Effect.orDie), + ).pipe(Effect.map((created) => created.body)) } function setProjectStartCommand(input: { server: TestServer; directory: string; command: string }) { return Effect.gen(function* () { const current = yield* request(input.server, `/project/current?directory=${encodeURIComponent(input.directory)}`) expect(current.status).toBe(200) - const project = (yield* Effect.promise(() => current.json())) as { id: string } + const project = yield* json<{ id: string }>(current) const updated = yield* request( input.server, `/project/${project.id}?directory=${encodeURIComponent(input.directory)}`, @@ -66,47 +164,91 @@ function setProjectStartCommand(input: { server: TestServer; directory: string; } describe("worktree endpoint reproduction", () => { - it.instance( + worktreeTest( "direct HttpApi worktree create returns without waiting for boot", () => Effect.gen(function* () { const test = yield* TestInstance const server = yield* serverScoped() - const response = yield* withRequestTimeout( - request(server, `${ExperimentalPaths.worktree}?directory=${encodeURIComponent(test.directory)}`, { + const response = yield* createWorktreeScoped({ + server, + directory: test.directory, + path: `${ExperimentalPaths.worktree}?directory=${encodeURIComponent(test.directory)}`, + init: { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({}), - }), - "direct worktree create", - ) + }, + timeoutLabel: "direct worktree create", + }) - expect(response.status).toBe(200) - expect(yield* Effect.promise(() => response.json())).toMatchObject({ directory: expect.any(String) }) + expect(response).toMatchObject({ directory: expect.any(String) }) }), { git: true }, ) - it.instance( + worktreeTest( + "direct HttpApi worktree create accepts missing body", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const server = yield* serverScoped() + + const response = yield* createWorktreeScoped({ + server, + directory: test.directory, + path: `${ExperimentalPaths.worktree}?directory=${encodeURIComponent(test.directory)}`, + init: { method: "POST", headers: { "content-type": "application/json" } }, + timeoutLabel: "direct worktree create without body", + }) + + expect(response).toMatchObject({ directory: expect.any(String) }) + }), + { git: true }, + ) + + worktreeTest( + "direct HttpApi worktree create accepts missing content type and body", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const server = yield* serverScoped() + + const response = yield* createWorktreeScoped({ + server, + directory: test.directory, + path: `${ExperimentalPaths.worktree}?directory=${encodeURIComponent(test.directory)}`, + init: { method: "POST" }, + timeoutLabel: "direct worktree create without content type or body", + }) + + expect(response).toMatchObject({ directory: expect.any(String) }) + }), + { git: true }, + ) + + worktreeTest( "workspace worktree create does not hang", () => Effect.gen(function* () { const test = yield* TestInstance const server = yield* serverScoped() - const response = yield* withRequestTimeout( - request(server, `${WorkspacePaths.list}?directory=${encodeURIComponent(test.directory)}`, { + const response = yield* createWorktreeScoped({ + server, + directory: test.directory, + path: `${WorkspacePaths.list}?directory=${encodeURIComponent(test.directory)}`, + init: { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ type: "worktree", branch: null }), - }), - "workspace worktree create", - 8_000, - ) + }, + timeoutLabel: "workspace worktree create", + timeoutMs: 8_000, + }) - expect(response.status).toBe(200) - expect(yield* Effect.promise(() => response.json())).toMatchObject({ + expect(response).toMatchObject({ type: "worktree", directory: expect.any(String), }) @@ -114,7 +256,7 @@ describe("worktree endpoint reproduction", () => { { git: true }, ) - it.instance( + worktreeTest( "workspace worktree create returns without waiting for project start command", () => Effect.gen(function* () { @@ -127,17 +269,19 @@ describe("worktree endpoint reproduction", () => { }) const started = Date.now() - const response = yield* withRequestTimeout( - request(server, `${WorkspacePaths.list}?directory=${encodeURIComponent(test.directory)}`, { + yield* createWorktreeScoped({ + server, + directory: test.directory, + path: `${WorkspacePaths.list}?directory=${encodeURIComponent(test.directory)}`, + init: { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ type: "worktree", branch: null }), - }), - "workspace worktree create with project start command", - 6_000, - ) + }, + timeoutLabel: "workspace worktree create with project start command", + timeoutMs: 6_000, + }) - expect(response.status).toBe(200) expect(Date.now() - started).toBeLessThan(1_500) }), { git: true }, diff --git a/packages/opencode/test/session/compaction.test.ts b/packages/opencode/test/session/compaction.test.ts index f734974f44d..f43340e07af 100644 --- a/packages/opencode/test/session/compaction.test.ts +++ b/packages/opencode/test/session/compaction.test.ts @@ -28,6 +28,7 @@ import { testEffect } from "../lib/effect" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { TestConfig } from "../fixture/config" import { SyncEvent } from "@/sync" +import { RuntimeFlags } from "@/effect/runtime-flags" void Log.init({ print: false }) @@ -211,9 +212,8 @@ function layer(result: "continue" | "compact") { } function cfg(compaction?: Config.Info["compaction"]) { - const base = Config.Info.zod.parse({}) return TestConfig.layer({ - get: () => Effect.succeed({ ...base, compaction }), + get: () => Effect.succeed({ compaction }), }) } @@ -225,17 +225,19 @@ const deps = Layer.mergeAll( Bus.layer, Config.defaultLayer, SyncEvent.defaultLayer, -) +).pipe(Layer.provide(RuntimeFlags.layer({ experimentalEventSystem: true }))) const env = Layer.mergeAll( SessionNs.defaultLayer, CrossSpawnSpawner.defaultLayer, SessionCompaction.layer.pipe(Layer.provide(SessionNs.defaultLayer), Layer.provideMerge(deps)), -) +).pipe(Layer.provideMerge(Layer.mergeAll(RuntimeFlags.layer({ experimentalEventSystem: true }), Config.defaultLayer))) const it = testEffect(env) -const compactionEnv = Layer.mergeAll(SessionNs.defaultLayer, CrossSpawnSpawner.defaultLayer) +const compactionEnv = Layer.mergeAll(SessionNs.defaultLayer, CrossSpawnSpawner.defaultLayer).pipe( + Layer.provideMerge(Layer.mergeAll(RuntimeFlags.layer({ experimentalEventSystem: true }), Config.defaultLayer)), +) const itCompaction = testEffect(compactionEnv) type CompactionProcessOptions = { diff --git a/packages/opencode/test/session/llm.test.ts b/packages/opencode/test/session/llm.test.ts index 60ae3d452cd..a6915282c81 100644 --- a/packages/opencode/test/session/llm.test.ts +++ b/packages/opencode/test/session/llm.test.ts @@ -9,7 +9,7 @@ import { Instance } from "../../src/project/instance" import { WithInstance } from "../../src/project/with-instance" import { Provider } from "@/provider/provider" import { ProviderTransform } from "@/provider/transform" -import { ModelsDev } from "@/provider/models" +import { ModelsDev } from "@opencode-ai/core/models" import { ProviderID, ModelID } from "../../src/provider/schema" import { Filesystem } from "@/util/filesystem" import { tmpdir } from "../fixture/fixture" @@ -277,6 +277,25 @@ async function loadFixture(providerID: string, modelID: string) { return { provider, model } } +function configModel(model: ModelsDev.Model) { + return { + id: model.id, + name: model.name, + family: model.family, + release_date: model.release_date, + attachment: model.attachment, + reasoning: model.reasoning, + temperature: model.temperature, + tool_call: model.tool_call, + interleaved: model.interleaved, + cost: model.cost ? { ...model.cost, tiers: undefined } : undefined, + limit: model.limit, + modalities: model.modalities, + status: model.status, + provider: model.provider, + } +} + function createEventStream(chunks: unknown[], includeDone = false) { const lines = chunks.map((chunk) => `data: ${typeof chunk === "string" ? chunk : JSON.stringify(chunk)}`) if (includeDone) { @@ -618,7 +637,7 @@ describe("session.llm.stream", () => { npm: "@ai-sdk/openai", api: "https://api.openai.com/v1", models: { - [model.id]: model, + [model.id]: configModel(model), }, options: { apiKey: "test-openai-key", @@ -734,7 +753,7 @@ describe("session.llm.stream", () => { npm: "@ai-sdk/openai", api: "https://api.openai.com/v1", models: { - [model.id]: model, + [model.id]: configModel(model), }, options: { apiKey: "test-openai-key", @@ -971,7 +990,7 @@ describe("session.llm.stream", () => { npm: "@ai-sdk/anthropic", api: "https://api.anthropic.com/v1", models: { - [model.id]: model, + [model.id]: configModel(model), }, options: { apiKey: "test-anthropic-key", diff --git a/packages/opencode/test/session/message-v2.test.ts b/packages/opencode/test/session/message-v2.test.ts index f742b7afc8e..82bed0e9cc6 100644 --- a/packages/opencode/test/session/message-v2.test.ts +++ b/packages/opencode/test/session/message-v2.test.ts @@ -1547,3 +1547,110 @@ describe("session.message-v2.fromError", () => { expect(result.name).toBe("MessageAbortedError") }) }) + +describe("session.message-v2.latest", () => { + const TAIL_USER = MessageID.make("msg_001") + const OVERFLOW_ASSISTANT = MessageID.make("msg_002") + const COMPACTION_USER = MessageID.make("msg_003") + const SUMMARY_ASSISTANT = MessageID.make("msg_004") + const CONTINUE_USER = MessageID.make("msg_005") + const NEW_COMPACTION_USER = MessageID.make("msg_006") + + const tailUser: MessageV2.WithParts = { + info: userInfo(TAIL_USER), + parts: [{ ...basePart(TAIL_USER, "p1"), type: "text", text: "original prompt" }] as MessageV2.Part[], + } + + const overflowAssistant: MessageV2.WithParts = { + info: { + ...assistantInfo(OVERFLOW_ASSISTANT, TAIL_USER), + finish: "tool-calls", + tokens: { input: 280_000, output: 200, reasoning: 0, cache: { read: 0, write: 0 }, total: 280_200 }, + } as MessageV2.Assistant, + parts: [], + } + + const compactionUser: MessageV2.WithParts = { + info: userInfo(COMPACTION_USER), + parts: [ + { + ...basePart(COMPACTION_USER, "p1"), + type: "compaction", + auto: true, + tail_start_id: TAIL_USER, + }, + ] as MessageV2.Part[], + } + + const summaryAssistant: MessageV2.WithParts = { + info: { + ...assistantInfo(SUMMARY_ASSISTANT, COMPACTION_USER), + summary: true, + finish: "stop", + tokens: { input: 150_000, output: 1_500, reasoning: 0, cache: { read: 0, write: 0 }, total: 151_500 }, + } as MessageV2.Assistant, + parts: [], + } + + const continueUser: MessageV2.WithParts = { + info: userInfo(CONTINUE_USER), + parts: [ + { + ...basePart(CONTINUE_USER, "p1"), + type: "text", + text: "Continue if you have next steps...", + synthetic: true, + metadata: { compaction_continue: true }, + }, + ] as MessageV2.Part[], + } + + // Regression for double auto-compaction. The reorder in filterCompacted + // (#27145) returns [compaction-user, summary, ...tail..., continue-user], + // so picking lastFinished by array position landed on the pre-compaction + // overflow assistant and bypassed the `summary !== true` overflow guard + // in SessionPrompt.runLoop, firing a second compaction.create immediately. + test("finished is the chronologically-latest finished assistant, not the array-latest", () => { + const filtered = MessageV2.filterCompacted([ + continueUser, + summaryAssistant, + compactionUser, + overflowAssistant, + tailUser, + ]) + + const state = MessageV2.latest(filtered) + + expect(state.finished?.id).toBe(SUMMARY_ASSISTANT) + expect(state.finished?.summary).toBe(true) + expect(state.user?.id).toBe(CONTINUE_USER) + expect(state.tasks).toEqual([]) + }) + + test("a fresh compaction-user newer than the latest summary surfaces in tasks", () => { + const newCompactionUser: MessageV2.WithParts = { + info: userInfo(NEW_COMPACTION_USER), + parts: [ + { + ...basePart(NEW_COMPACTION_USER, "p1"), + type: "compaction", + auto: true, + }, + ] as MessageV2.Part[], + } + + const state = MessageV2.latest([ + tailUser, + overflowAssistant, + compactionUser, + summaryAssistant, + continueUser, + newCompactionUser, + ]) + + expect(state.finished?.id).toBe(SUMMARY_ASSISTANT) + expect(state.user?.id).toBe(NEW_COMPACTION_USER) + expect(state.tasks).toHaveLength(1) + expect(state.tasks[0]).toMatchObject({ type: "compaction", auto: true }) + }) +}) diff --git a/packages/opencode/test/session/messages-pagination.test.ts b/packages/opencode/test/session/messages-pagination.test.ts index 05ec2bad494..e558d07b500 100644 --- a/packages/opencode/test/session/messages-pagination.test.ts +++ b/packages/opencode/test/session/messages-pagination.test.ts @@ -1,46 +1,42 @@ import { describe, expect, test } from "bun:test" -import { Effect } from "effect" -import path from "path" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" +import { Effect, Option } from "effect" import { Session as SessionNs } from "@/session/session" import { MessageV2 } from "../../src/session/message-v2" import { MessageID, PartID, type SessionID } from "../../src/session/schema" import { ModelID, ProviderID } from "../../src/provider/schema" +import { NotFoundError } from "@/storage/storage" import * as Log from "@opencode-ai/core/util/log" +import { testEffect } from "../lib/effect" -const root = path.join(__dirname, "../..") void Log.init({ print: false }) -function run(fx: Effect.Effect) { - return Effect.runPromise(fx.pipe(Effect.provide(SessionNs.defaultLayer))) -} - -const svc = { - ...SessionNs, - create(input?: SessionNs.CreateInput) { - return run(SessionNs.Service.use((svc) => svc.create(input))) - }, - remove(id: SessionID) { - return run(SessionNs.Service.use((svc) => svc.remove(id))) - }, - updateMessage(msg: T) { - return run(SessionNs.Service.use((svc) => svc.updateMessage(msg))) - }, - updatePart(part: T) { - return run(SessionNs.Service.use((svc) => svc.updatePart(part))) - }, - fork(input: { sessionID: SessionID; messageID?: MessageID }) { - return run(SessionNs.Service.use((svc) => svc.fork(input))) - }, -} - -async function fill(sessionID: SessionID, count: number, time = (i: number) => Date.now() + i) { +const it = testEffect(SessionNs.defaultLayer) + +const withSession = ( + fn: (input: { session: SessionNs.Interface; sessionID: SessionID }) => Effect.Effect, +) => + Effect.acquireUseRelease( + Effect.gen(function* () { + const session = yield* SessionNs.Service + const created = yield* session.create({}) + return { session, sessionID: created.id } + }), + fn, + (input) => input.session.remove(input.sessionID).pipe(Effect.ignore), + ) + +// Helper functions using Effect.gen +const fill = Effect.fn("Test.fill")(function* ( + sessionID: SessionID, + count: number, + time = (i: number) => Date.now() + i, +) { + const session = yield* SessionNs.Service const ids = [] as MessageID[] for (let i = 0; i < count; i++) { const id = MessageID.ascending() ids.push(id) - await svc.updateMessage({ + yield* session.updateMessage({ id, sessionID, role: "user", @@ -50,7 +46,7 @@ async function fill(sessionID: SessionID, count: number, time = (i: number) => D tools: {}, mode: "", } as unknown as MessageV2.Info) - await svc.updatePart({ + yield* session.updatePart({ id: PartID.ascending(), sessionID, messageID: id, @@ -59,11 +55,12 @@ async function fill(sessionID: SessionID, count: number, time = (i: number) => D }) } return ids -} +}) -async function addUser(sessionID: SessionID, text?: string) { +const addUser = Effect.fn("Test.addUser")(function* (sessionID: SessionID, text?: string) { + const session = yield* SessionNs.Service const id = MessageID.ascending() - await svc.updateMessage({ + yield* session.updateMessage({ id, sessionID, role: "user", @@ -74,7 +71,7 @@ async function addUser(sessionID: SessionID, text?: string) { mode: "", } as unknown as MessageV2.Info) if (text) { - await svc.updatePart({ + yield* session.updatePart({ id: PartID.ascending(), sessionID, messageID: id, @@ -83,15 +80,16 @@ async function addUser(sessionID: SessionID, text?: string) { }) } return id -} +}) -async function addAssistant( +const addAssistant = Effect.fn("Test.addAssistant")(function* ( sessionID: SessionID, parentID: MessageID, opts?: { summary?: boolean; finish?: string; error?: MessageV2.Assistant["error"] }, ) { + const session = yield* SessionNs.Service const id = MessageID.ascending() - await svc.updateMessage({ + yield* session.updateMessage({ id, sessionID, role: "assistant", @@ -109,10 +107,15 @@ async function addAssistant( error: opts?.error, } as unknown as MessageV2.Info) return id -} +}) -async function addCompactionPart(sessionID: SessionID, messageID: MessageID, tailStartID?: MessageID) { - await svc.updatePart({ +const addCompactionPart = Effect.fn("Test.addCompactionPart")(function* ( + sessionID: SessionID, + messageID: MessageID, + tailStartID?: MessageID, +) { + const session = yield* SessionNs.Service + yield* session.updatePart({ id: PartID.ascending(), sessionID, messageID, @@ -120,382 +123,303 @@ async function addCompactionPart(sessionID: SessionID, messageID: MessageID, tai auto: true, tail_start_id: tailStartID, } as any) -} +}) describe("MessageV2.page", () => { - test("returns sync result", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - await fill(session.id, 2) - - const result = MessageV2.page({ sessionID: session.id, limit: 10 }) + it.instance("returns page result", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + yield* fill(sessionID, 2) + + const result = yield* MessageV2.page({ sessionID, limit: 10 }) expect(result).toBeDefined() expect(result.items).toBeArray() + }), + ), + ) - await svc.remove(session.id) - }, - }) - }) - - test("pages backward with opaque cursors", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const ids = await fill(session.id, 6) + it.instance("pages backward with opaque cursors", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const ids = yield* fill(sessionID, 6) - const a = MessageV2.page({ sessionID: session.id, limit: 2 }) + const a = yield* MessageV2.page({ sessionID, limit: 2 }) expect(a.items.map((item) => item.info.id)).toEqual(ids.slice(-2)) expect(a.items.every((item) => item.parts.length === 1)).toBe(true) expect(a.more).toBe(true) expect(a.cursor).toBeTruthy() - const b = MessageV2.page({ sessionID: session.id, limit: 2, before: a.cursor! }) + const b = yield* MessageV2.page({ sessionID, limit: 2, before: a.cursor! }) expect(b.items.map((item) => item.info.id)).toEqual(ids.slice(-4, -2)) expect(b.more).toBe(true) expect(b.cursor).toBeTruthy() - const c = MessageV2.page({ sessionID: session.id, limit: 2, before: b.cursor! }) + const c = yield* MessageV2.page({ sessionID, limit: 2, before: b.cursor! }) expect(c.items.map((item) => item.info.id)).toEqual(ids.slice(0, 2)) expect(c.more).toBe(false) expect(c.cursor).toBeUndefined() + }), + ), + ) - await svc.remove(session.id) - }, - }) - }) + it.instance("returns items in chronological order within a page", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const ids = yield* fill(sessionID, 4) - test("returns items in chronological order within a page", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const ids = await fill(session.id, 4) - - const result = MessageV2.page({ sessionID: session.id, limit: 4 }) + const result = yield* MessageV2.page({ sessionID, limit: 4 }) expect(result.items.map((item) => item.info.id)).toEqual(ids) - - await svc.remove(session.id) - }, - }) - }) - - test("returns empty items for session with no messages", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - - const result = MessageV2.page({ sessionID: session.id, limit: 10 }) + }), + ), + ) + + it.instance("returns empty items for session with no messages", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const result = yield* MessageV2.page({ sessionID, limit: 10 }) expect(result.items).toEqual([]) expect(result.more).toBe(false) expect(result.cursor).toBeUndefined() - - await svc.remove(session.id) - }, - }) - }) - - test("throws NotFoundError for non-existent session", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const fake = "non-existent-session" as SessionID - expect(() => MessageV2.page({ sessionID: fake, limit: 10 })).toThrow("NotFoundError") - }, - }) - }) - - test("handles exact limit boundary", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const ids = await fill(session.id, 3) - - const result = MessageV2.page({ sessionID: session.id, limit: 3 }) + }), + ), + ) + + it.instance("fails with NotFoundError for non-existent session", () => + Effect.gen(function* () { + const fake = "non-existent-session" as SessionID + const error = yield* Effect.flip(MessageV2.page({ sessionID: fake, limit: 10 })) + expect(error).toBeInstanceOf(NotFoundError) + expect(error.message).toBe(`Session not found: ${fake}`) + }), + ) + + it.instance("handles exact limit boundary", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const ids = yield* fill(sessionID, 3) + + const result = yield* MessageV2.page({ sessionID, limit: 3 }) expect(result.items.map((item) => item.info.id)).toEqual(ids) expect(result.more).toBe(false) expect(result.cursor).toBeUndefined() + }), + ), + ) - await svc.remove(session.id) - }, - }) - }) - - test("limit of 1 returns single newest message", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const ids = await fill(session.id, 5) + it.instance("limit of 1 returns single newest message", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const ids = yield* fill(sessionID, 5) - const result = MessageV2.page({ sessionID: session.id, limit: 1 }) + const result = yield* MessageV2.page({ sessionID, limit: 1 }) expect(result.items).toHaveLength(1) expect(result.items[0].info.id).toBe(ids[ids.length - 1]) expect(result.more).toBe(true) + }), + ), + ) - await svc.remove(session.id) - }, - }) - }) - - test("hydrates multiple parts per message", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const [id] = await fill(session.id, 1) + it.instance("hydrates multiple parts per message", () => + withSession(({ session, sessionID }) => + Effect.gen(function* () { + const [id] = yield* fill(sessionID, 1) - await svc.updatePart({ + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: id, type: "text", text: "extra", }) - const result = MessageV2.page({ sessionID: session.id, limit: 10 }) + const result = yield* MessageV2.page({ sessionID, limit: 10 }) expect(result.items).toHaveLength(1) expect(result.items[0].parts).toHaveLength(2) + }), + ), + ) - await svc.remove(session.id) - }, - }) - }) - - test("accepts cursors from fractional timestamps", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const ids = await fill(session.id, 4, (i) => 1000.5 + i) + it.instance("accepts cursors from fractional timestamps", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const ids = yield* fill(sessionID, 4, (i: number) => 1000.5 + i) - const a = MessageV2.page({ sessionID: session.id, limit: 2 }) - const b = MessageV2.page({ sessionID: session.id, limit: 2, before: a.cursor! }) + const a = yield* MessageV2.page({ sessionID, limit: 2 }) + const b = yield* MessageV2.page({ sessionID, limit: 2, before: a.cursor! }) expect(a.items.map((item) => item.info.id)).toEqual(ids.slice(-2)) expect(b.items.map((item) => item.info.id)).toEqual(ids.slice(0, 2)) + }), + ), + ) - await svc.remove(session.id) - }, - }) - }) - - test("messages with same timestamp are ordered by id", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const ids = await fill(session.id, 4, () => 1000) + it.instance("messages with same timestamp are ordered by id", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const ids = yield* fill(sessionID, 4, () => 1000) - const a = MessageV2.page({ sessionID: session.id, limit: 2 }) + const a = yield* MessageV2.page({ sessionID, limit: 2 }) expect(a.items.map((item) => item.info.id)).toEqual(ids.slice(-2)) expect(a.more).toBe(true) - const b = MessageV2.page({ sessionID: session.id, limit: 2, before: a.cursor! }) + const b = yield* MessageV2.page({ sessionID, limit: 2, before: a.cursor! }) expect(b.items.map((item) => item.info.id)).toEqual(ids.slice(0, 2)) expect(b.more).toBe(false) - - await svc.remove(session.id) - }, - }) - }) - - test("does not return messages from other sessions", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const a = await svc.create({}) - const b = await svc.create({}) - await fill(a.id, 3) - await fill(b.id, 2) - - const resultA = MessageV2.page({ sessionID: a.id, limit: 10 }) - const resultB = MessageV2.page({ sessionID: b.id, limit: 10 }) - expect(resultA.items).toHaveLength(3) - expect(resultB.items).toHaveLength(2) - expect(resultA.items.every((item) => item.info.sessionID === a.id)).toBe(true) - expect(resultB.items.every((item) => item.info.sessionID === b.id)).toBe(true) - - await svc.remove(a.id) - await svc.remove(b.id) - }, - }) - }) - - test("large limit returns all messages without cursor", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const ids = await fill(session.id, 10) - - const result = MessageV2.page({ sessionID: session.id, limit: 100 }) + }), + ), + ) + + it.instance("does not return messages from other sessions", () => + Effect.gen(function* () { + const session = yield* SessionNs.Service + const a = yield* session.create({}) + const b = yield* session.create({}) + yield* fill(a.id, 3) + yield* fill(b.id, 2) + + const resultA = yield* MessageV2.page({ sessionID: a.id, limit: 10 }) + const resultB = yield* MessageV2.page({ sessionID: b.id, limit: 10 }) + expect(resultA.items).toHaveLength(3) + expect(resultB.items).toHaveLength(2) + expect(resultA.items.every((item) => item.info.sessionID === a.id)).toBe(true) + expect(resultB.items.every((item) => item.info.sessionID === b.id)).toBe(true) + + yield* session.remove(a.id) + yield* session.remove(b.id) + }), + ) + + it.instance("large limit returns all messages without cursor", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const ids = yield* fill(sessionID, 10) + + const result = yield* MessageV2.page({ sessionID, limit: 100 }) expect(result.items).toHaveLength(10) expect(result.items.map((item) => item.info.id)).toEqual(ids) expect(result.more).toBe(false) expect(result.cursor).toBeUndefined() - - await svc.remove(session.id) - }, - }) - }) + }), + ), + ) }) describe("MessageV2.stream", () => { - test("yields items newest first", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const ids = await fill(session.id, 5) - - const items = Array.from(MessageV2.stream(session.id)) - expect(items.map((item) => item.info.id)).toEqual(ids.slice().reverse()) - - await svc.remove(session.id) - }, - }) - }) - - test("yields nothing for empty session", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) + it.instance("yields items newest first", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const ids = yield* fill(sessionID, 5) - const items = Array.from(MessageV2.stream(session.id)) + const items = Array.from(MessageV2.stream(sessionID)) + expect(items.map((item) => item.info.id)).toEqual(ids.slice().reverse()) + }), + ), + ) + + it.instance("yields nothing for empty session", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const items = Array.from(MessageV2.stream(sessionID)) expect(items).toHaveLength(0) + }), + ), + ) - await svc.remove(session.id) - }, - }) - }) - - test("yields single message", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const ids = await fill(session.id, 1) + it.instance("yields single message", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const ids = yield* fill(sessionID, 1) - const items = Array.from(MessageV2.stream(session.id)) + const items = Array.from(MessageV2.stream(sessionID)) expect(items).toHaveLength(1) expect(items[0].info.id).toBe(ids[0]) + }), + ), + ) - await svc.remove(session.id) - }, - }) - }) + it.instance("hydrates parts for each yielded message", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + yield* fill(sessionID, 3) - test("hydrates parts for each yielded message", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - await fill(session.id, 3) - - const items = Array.from(MessageV2.stream(session.id)) + const items = Array.from(MessageV2.stream(sessionID)) for (const item of items) { expect(item.parts).toHaveLength(1) expect(item.parts[0].type).toBe("text") } + }), + ), + ) - await svc.remove(session.id) - }, - }) - }) - - test("handles sets exceeding internal page size", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const ids = await fill(session.id, 60) + it.instance("handles sets exceeding internal page size", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const ids = yield* fill(sessionID, 60) - const items = Array.from(MessageV2.stream(session.id)) + const items = Array.from(MessageV2.stream(sessionID)) expect(items).toHaveLength(60) expect(items[0].info.id).toBe(ids[ids.length - 1]) expect(items[59].info.id).toBe(ids[0]) + }), + ), + ) - await svc.remove(session.id) - }, - }) - }) + it.instance("is a sync generator", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + yield* fill(sessionID, 1) - test("is a sync generator", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - await fill(session.id, 1) - - const gen = MessageV2.stream(session.id) + const gen = MessageV2.stream(sessionID) const first = gen.next() // sync generator returns { value, done } directly, not a Promise expect(first).toHaveProperty("value") expect(first).toHaveProperty("done") expect(first.done).toBe(false) - - await svc.remove(session.id) - }, - }) - }) + }), + ), + ) }) describe("MessageV2.parts", () => { - test("returns parts for a message", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const [id] = await fill(session.id, 1) + it.instance("returns parts for a message", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const [id] = yield* fill(sessionID, 1) const result = MessageV2.parts(id) expect(result).toHaveLength(1) expect(result[0].type).toBe("text") expect((result[0] as MessageV2.TextPart).text).toBe("m0") + }), + ), + ) - await svc.remove(session.id) - }, - }) - }) - - test("returns empty array for message with no parts", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const id = await addUser(session.id) + it.instance("returns empty array for message with no parts", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const id = yield* addUser(sessionID) const result = MessageV2.parts(id) expect(result).toEqual([]) + }), + ), + ) - await svc.remove(session.id) - }, - }) - }) - - test("returns multiple parts in order", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const [id] = await fill(session.id, 1) + it.instance("returns multiple parts in order", () => + withSession(({ session, sessionID }) => + Effect.gen(function* () { + const [id] = yield* fill(sessionID, 1) - await svc.updatePart({ + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: id, type: "text", text: "second", }) - await svc.updatePart({ + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: id, type: "text", text: "third", @@ -506,547 +430,523 @@ describe("MessageV2.parts", () => { expect((result[0] as MessageV2.TextPart).text).toBe("m0") expect((result[1] as MessageV2.TextPart).text).toBe("second") expect((result[2] as MessageV2.TextPart).text).toBe("third") - - await svc.remove(session.id) - }, - }) - }) - - test("returns empty for non-existent message id", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - await svc.create({}) - const result = MessageV2.parts(MessageID.ascending()) - expect(result).toEqual([]) - }, - }) - }) - - test("parts contain sessionID and messageID", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const [id] = await fill(session.id, 1) + }), + ), + ) + + it.instance("returns empty for non-existent message id", () => + Effect.gen(function* () { + yield* SessionNs.Service + const result = MessageV2.parts(MessageID.ascending()) + expect(result).toEqual([]) + }), + ) + + it.instance("parts contain sessionID and messageID", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const [id] = yield* fill(sessionID, 1) const result = MessageV2.parts(id) - expect(result[0].sessionID).toBe(session.id) + expect(result[0].sessionID).toBe(sessionID) expect(result[0].messageID).toBe(id) - - await svc.remove(session.id) - }, - }) - }) + }), + ), + ) }) describe("MessageV2.get", () => { - test("returns message with hydrated parts", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const [id] = await fill(session.id, 1) - - const result = MessageV2.get({ sessionID: session.id, messageID: id }) + it.instance("returns message with hydrated parts", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const [id] = yield* fill(sessionID, 1) + + const result = yield* MessageV2.get({ sessionID, messageID: id }) expect(result.info.id).toBe(id) - expect(result.info.sessionID).toBe(session.id) + expect(result.info.sessionID).toBe(sessionID) expect(result.info.role).toBe("user") expect(result.parts).toHaveLength(1) expect((result.parts[0] as MessageV2.TextPart).text).toBe("m0") - - await svc.remove(session.id) - }, - }) - }) - - test("throws NotFoundError for non-existent message", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - - expect(() => MessageV2.get({ sessionID: session.id, messageID: MessageID.ascending() })).toThrow( - "NotFoundError", - ) - - await svc.remove(session.id) - }, - }) - }) - - test("scopes by session id", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const a = await svc.create({}) - const b = await svc.create({}) - const [id] = await fill(a.id, 1) - - expect(() => MessageV2.get({ sessionID: b.id, messageID: id })).toThrow("NotFoundError") - const result = MessageV2.get({ sessionID: a.id, messageID: id }) - expect(result.info.id).toBe(id) - - await svc.remove(a.id) - await svc.remove(b.id) - }, - }) - }) - - test("returns message with multiple parts", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const [id] = await fill(session.id, 1) - - await svc.updatePart({ + }), + ), + ) + + it.instance("fails with NotFoundError for non-existent message", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const messageID = MessageID.ascending() + const error = yield* Effect.flip(MessageV2.get({ sessionID, messageID })) + expect(error).toBeInstanceOf(NotFoundError) + expect(error.message).toBe(`Message not found: ${messageID}`) + }), + ), + ) + + it.instance("scopes by session id", () => + Effect.gen(function* () { + const session = yield* SessionNs.Service + const a = yield* session.create({}) + const b = yield* session.create({}) + const [id] = yield* fill(a.id, 1) + + const error = yield* Effect.flip(MessageV2.get({ sessionID: b.id, messageID: id })) + expect(error).toBeInstanceOf(NotFoundError) + expect(error.message).toBe(`Message not found: ${id}`) + const result = yield* MessageV2.get({ sessionID: a.id, messageID: id }) + expect(result.info.id).toBe(id) + + yield* session.remove(a.id) + yield* session.remove(b.id) + }), + ) + + it.instance("returns message with multiple parts", () => + withSession(({ session, sessionID }) => + Effect.gen(function* () { + const [id] = yield* fill(sessionID, 1) + + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: id, type: "text", text: "extra", }) - const result = MessageV2.get({ sessionID: session.id, messageID: id }) + const result = yield* MessageV2.get({ sessionID, messageID: id }) expect(result.parts).toHaveLength(2) + }), + ), + ) - await svc.remove(session.id) - }, - }) - }) - - test("returns assistant message with correct role", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const uid = await addUser(session.id, "hello") - const aid = await addAssistant(session.id, uid) + it.instance("returns assistant message with correct role", () => + withSession(({ session, sessionID }) => + Effect.gen(function* () { + const uid = yield* addUser(sessionID, "hello") + const aid = yield* addAssistant(sessionID, uid) - await svc.updatePart({ + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: aid, type: "text", text: "response", }) - const result = MessageV2.get({ sessionID: session.id, messageID: aid }) + const result = yield* MessageV2.get({ sessionID, messageID: aid }) expect(result.info.role).toBe("assistant") expect(result.parts).toHaveLength(1) expect((result.parts[0] as MessageV2.TextPart).text).toBe("response") + }), + ), + ) - await svc.remove(session.id) - }, - }) - }) - - test("returns message with zero parts", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const id = await addUser(session.id) + it.instance("returns message with zero parts", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const id = yield* addUser(sessionID) - const result = MessageV2.get({ sessionID: session.id, messageID: id }) + const result = yield* MessageV2.get({ sessionID, messageID: id }) expect(result.info.id).toBe(id) expect(result.parts).toEqual([]) + }), + ), + ) +}) - await svc.remove(session.id) - }, - }) - }) +describe("Session.messages", () => { + it.instance("returns all messages in chronological order across pages", () => + withSession(({ session, sessionID }) => + Effect.gen(function* () { + const ids = yield* fill(sessionID, 55) + const result = yield* session.messages({ sessionID }) + expect(result.map((item) => item.info.id)).toEqual(ids) + }), + ), + ) + + it.instance("fails with NotFoundError for non-existent session", () => + Effect.gen(function* () { + const session = yield* SessionNs.Service + const fake = "non-existent-session" as SessionID + const error = yield* Effect.flip(session.messages({ sessionID: fake })) + expect(error).toBeInstanceOf(NotFoundError) + expect(error.message).toBe(`Session not found: ${fake}`) + }), + ) +}) + +describe("Session.findMessage", () => { + it.instance("searches newest-first", () => + withSession(({ session, sessionID }) => + Effect.gen(function* () { + const ids = yield* fill(sessionID, 3) + const result = yield* session.findMessage(sessionID, () => true) + expect(Option.isSome(result) ? result.value.info.id : undefined).toBe(ids.at(-1)) + }), + ), + ) + + it.instance("fails with NotFoundError for non-existent session", () => + Effect.gen(function* () { + const session = yield* SessionNs.Service + const fake = "non-existent-session" as SessionID + const error = yield* Effect.flip(session.findMessage(fake, () => true)) + expect(error).toBeInstanceOf(NotFoundError) + expect(error.message).toBe(`Session not found: ${fake}`) + }), + ) }) describe("MessageV2.filterCompacted", () => { - test("returns all messages when no compaction", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const ids = await fill(session.id, 5) - - const result = MessageV2.filterCompacted(MessageV2.stream(session.id)) + it.instance("returns all messages when no compaction", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const ids = yield* fill(sessionID, 5) + + const result = MessageV2.filterCompacted(MessageV2.stream(sessionID)) expect(result).toHaveLength(5) // reversed from newest-first to chronological expect(result.map((item) => item.info.id)).toEqual(ids) + }), + ), + ) - await svc.remove(session.id) - }, - }) - }) - - test("stops at compaction boundary and returns chronological order", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - + it.instance("stops at compaction boundary and returns chronological order", () => + withSession(({ session, sessionID }) => + Effect.gen(function* () { // Chronological: u1(+compaction part), a1(summary, parentID=u1), u2, a2 // Stream (newest first): a2, u2, a1(adds u1 to completed), u1(in completed + compaction) -> break - const u1 = await addUser(session.id, "first question") - const a1 = await addAssistant(session.id, u1, { summary: true, finish: "end_turn" }) - await svc.updatePart({ + const u1 = yield* addUser(sessionID, "first question") + const a1 = yield* addAssistant(sessionID, u1, { summary: true, finish: "end_turn" }) + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: a1, type: "text", text: "summary", }) - await addCompactionPart(session.id, u1) + yield* addCompactionPart(sessionID, u1) - const u2 = await addUser(session.id, "new question") - const a2 = await addAssistant(session.id, u2) - await svc.updatePart({ + const u2 = yield* addUser(sessionID, "new question") + const a2 = yield* addAssistant(sessionID, u2) + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: a2, type: "text", text: "new response", }) - const result = MessageV2.filterCompacted(MessageV2.stream(session.id)) + const result = MessageV2.filterCompacted(MessageV2.stream(sessionID)) // Includes compaction boundary: u1, a1, u2, a2 expect(result[0].info.id).toBe(u1) expect(result.length).toBe(4) - - await svc.remove(session.id) - }, - }) - }) - - test("handles empty iterable", () => { - const result = MessageV2.filterCompacted([]) - expect(result).toEqual([]) - }) - - test("does not break on compaction part without matching summary", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - - const u1 = await addUser(session.id, "hello") - await addCompactionPart(session.id, u1) - await addUser(session.id, "world") - - const result = MessageV2.filterCompacted(MessageV2.stream(session.id)) + }), + ), + ) + + it.live("handles empty iterable", () => + Effect.sync(() => { + const result = MessageV2.filterCompacted([]) + expect(result).toEqual([]) + }), + ) + + it.instance("does not break on compaction part without matching summary", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const u1 = yield* addUser(sessionID, "hello") + yield* addCompactionPart(sessionID, u1) + yield* addUser(sessionID, "world") + + const result = MessageV2.filterCompacted(MessageV2.stream(sessionID)) expect(result).toHaveLength(2) + }), + ), + ) - await svc.remove(session.id) - }, - }) - }) - - test("skips assistant with error even if marked as summary", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - - const u1 = await addUser(session.id, "hello") - await addCompactionPart(session.id, u1) + it.instance("skips assistant with error even if marked as summary", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const u1 = yield* addUser(sessionID, "hello") + yield* addCompactionPart(sessionID, u1) const error = new MessageV2.APIError({ message: "boom", isRetryable: true, }).toObject() as MessageV2.Assistant["error"] - await addAssistant(session.id, u1, { summary: true, finish: "end_turn", error }) - await addUser(session.id, "retry") + yield* addAssistant(sessionID, u1, { summary: true, finish: "end_turn", error }) + yield* addUser(sessionID, "retry") - const result = MessageV2.filterCompacted(MessageV2.stream(session.id)) + const result = MessageV2.filterCompacted(MessageV2.stream(sessionID)) // Error assistant doesn't add to completed, so compaction boundary never triggers expect(result).toHaveLength(3) + }), + ), + ) - await svc.remove(session.id) - }, - }) - }) - - test("skips assistant without finish even if marked as summary", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - - const u1 = await addUser(session.id, "hello") - await addCompactionPart(session.id, u1) + it.instance("skips assistant without finish even if marked as summary", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const u1 = yield* addUser(sessionID, "hello") + yield* addCompactionPart(sessionID, u1) // summary=true but no finish - await addAssistant(session.id, u1, { summary: true }) - await addUser(session.id, "next") + yield* addAssistant(sessionID, u1, { summary: true }) + yield* addUser(sessionID, "next") - const result = MessageV2.filterCompacted(MessageV2.stream(session.id)) + const result = MessageV2.filterCompacted(MessageV2.stream(sessionID)) expect(result).toHaveLength(3) - - await svc.remove(session.id) - }, - }) - }) - - test("retains original tail when compaction stores tail_start_id", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - - const u1 = await addUser(session.id, "first") - const a1 = await addAssistant(session.id, u1, { finish: "end_turn" }) - await svc.updatePart({ + }), + ), + ) + + it.instance("retains original tail when compaction stores tail_start_id", () => + withSession(({ session, sessionID }) => + Effect.gen(function* () { + const u1 = yield* addUser(sessionID, "first") + const a1 = yield* addAssistant(sessionID, u1, { finish: "end_turn" }) + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: a1, type: "text", text: "first reply", }) - const u2 = await addUser(session.id, "second") - const a2 = await addAssistant(session.id, u2, { finish: "end_turn" }) - await svc.updatePart({ + const u2 = yield* addUser(sessionID, "second") + const a2 = yield* addAssistant(sessionID, u2, { finish: "end_turn" }) + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: a2, type: "text", text: "second reply", }) - const c1 = await addUser(session.id) - await addCompactionPart(session.id, c1, u2) - const s1 = await addAssistant(session.id, c1, { summary: true, finish: "end_turn" }) - await svc.updatePart({ + const c1 = yield* addUser(sessionID) + yield* addCompactionPart(sessionID, c1, u2) + const s1 = yield* addAssistant(sessionID, c1, { summary: true, finish: "end_turn" }) + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: s1, type: "text", text: "summary", }) - const u3 = await addUser(session.id, "third") - const a3 = await addAssistant(session.id, u3, { finish: "end_turn" }) - await svc.updatePart({ + const u3 = yield* addUser(sessionID, "third") + const a3 = yield* addAssistant(sessionID, u3, { finish: "end_turn" }) + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: a3, type: "text", text: "third reply", }) - const result = MessageV2.filterCompacted(MessageV2.stream(session.id)) + const result = MessageV2.filterCompacted(MessageV2.stream(sessionID)) expect(result.map((item) => item.info.id)).toEqual([c1, s1, u2, a2, u3, a3]) - - await svc.remove(session.id) - }, - }) - }) - - test("fork remaps compaction tail_start_id for filterCompacted", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - - const u1 = await addUser(session.id, "first") - const a1 = await addAssistant(session.id, u1, { finish: "end_turn" }) - await svc.updatePart({ - id: PartID.ascending(), - sessionID: session.id, - messageID: a1, - type: "text", - text: "first reply", - }) - - const u2 = await addUser(session.id, "second") - const a2 = await addAssistant(session.id, u2, { finish: "end_turn" }) - await svc.updatePart({ - id: PartID.ascending(), - sessionID: session.id, - messageID: a2, - type: "text", - text: "second reply", - }) - - const c1 = await addUser(session.id) - await addCompactionPart(session.id, c1, u2) - const s1 = await addAssistant(session.id, c1, { summary: true, finish: "end_turn" }) - await svc.updatePart({ - id: PartID.ascending(), - sessionID: session.id, - messageID: s1, - type: "text", - text: "summary", - }) - - const u3 = await addUser(session.id, "third") - const a3 = await addAssistant(session.id, u3, { finish: "end_turn" }) - await svc.updatePart({ - id: PartID.ascending(), - sessionID: session.id, - messageID: a3, - type: "text", - text: "third reply", - }) - - const parentFiltered = MessageV2.filterCompacted(MessageV2.stream(session.id)) - expect(parentFiltered.map((item) => item.info.id)).toEqual([c1, s1, u2, a2, u3, a3]) - - const forked = await svc.fork({ sessionID: session.id }) - const childFiltered = MessageV2.filterCompacted(MessageV2.stream(forked.id)) - expect(childFiltered).toHaveLength(parentFiltered.length) - - const tailPart = childFiltered.flatMap((m) => m.parts).find((p) => p.type === "compaction") - expect(tailPart?.type).toBe("compaction") - if (!tailPart || tailPart.type !== "compaction") throw new Error("Expected forked compaction part") - expect(tailPart.tail_start_id).toBeDefined() - expect(childFiltered.some((m) => m.info.id === tailPart.tail_start_id)).toBe(true) - - await svc.remove(forked.id) - await svc.remove(session.id) - }, - }) - }) - - test("retains an assistant tail when compaction starts inside a turn", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - - const u1 = await addUser(session.id, "first") - const a1 = await addAssistant(session.id, u1, { finish: "end_turn" }) - await svc.updatePart({ + }), + ), + ) + + it.instance("fork remaps compaction tail_start_id for filterCompacted", () => + Effect.gen(function* () { + const session = yield* SessionNs.Service + const created = yield* session.create({}) + + const u1 = yield* addUser(created.id, "first") + const a1 = yield* addAssistant(created.id, u1, { finish: "end_turn" }) + yield* session.updatePart({ + id: PartID.ascending(), + sessionID: created.id, + messageID: a1, + type: "text", + text: "first reply", + }) + + const u2 = yield* addUser(created.id, "second") + const a2 = yield* addAssistant(created.id, u2, { finish: "end_turn" }) + yield* session.updatePart({ + id: PartID.ascending(), + sessionID: created.id, + messageID: a2, + type: "text", + text: "second reply", + }) + + const c1 = yield* addUser(created.id) + yield* addCompactionPart(created.id, c1, u2) + const s1 = yield* addAssistant(created.id, c1, { summary: true, finish: "end_turn" }) + yield* session.updatePart({ + id: PartID.ascending(), + sessionID: created.id, + messageID: s1, + type: "text", + text: "summary", + }) + + const u3 = yield* addUser(created.id, "third") + const a3 = yield* addAssistant(created.id, u3, { finish: "end_turn" }) + yield* session.updatePart({ + id: PartID.ascending(), + sessionID: created.id, + messageID: a3, + type: "text", + text: "third reply", + }) + + const parentFiltered = MessageV2.filterCompacted(MessageV2.stream(created.id)) + expect(parentFiltered.map((item) => item.info.id)).toEqual([c1, s1, u2, a2, u3, a3]) + + const forked = yield* session.fork({ sessionID: created.id }) + const childFiltered = MessageV2.filterCompacted(MessageV2.stream(forked.id)) + expect(childFiltered).toHaveLength(parentFiltered.length) + + const tailPart = childFiltered.flatMap((m) => m.parts).find((p) => p.type === "compaction") + expect(tailPart?.type).toBe("compaction") + if (!tailPart || tailPart.type !== "compaction") throw new Error("Expected forked compaction part") + expect(tailPart.tail_start_id).toBeDefined() + expect(childFiltered.some((m) => m.info.id === tailPart.tail_start_id)).toBe(true) + + yield* session.remove(forked.id) + yield* session.remove(created.id) + }), + ) + + it.instance("retains an assistant tail when compaction starts inside a turn", () => + withSession(({ session, sessionID }) => + Effect.gen(function* () { + const u1 = yield* addUser(sessionID, "first") + const a1 = yield* addAssistant(sessionID, u1, { finish: "end_turn" }) + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: a1, type: "text", text: "first reply", }) - const u2 = await addUser(session.id, "second") - const a2 = await addAssistant(session.id, u2, { finish: "end_turn" }) - await svc.updatePart({ + const u2 = yield* addUser(sessionID, "second") + const a2 = yield* addAssistant(sessionID, u2, { finish: "end_turn" }) + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: a2, type: "text", text: "second reply", }) - const a3 = await addAssistant(session.id, u2, { finish: "end_turn" }) - await svc.updatePart({ + const a3 = yield* addAssistant(sessionID, u2, { finish: "end_turn" }) + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: a3, type: "text", text: "tail reply", }) - const c1 = await addUser(session.id) - await addCompactionPart(session.id, c1, a3) - const s1 = await addAssistant(session.id, c1, { summary: true, finish: "end_turn" }) - await svc.updatePart({ + const c1 = yield* addUser(sessionID) + yield* addCompactionPart(sessionID, c1, a3) + const s1 = yield* addAssistant(sessionID, c1, { summary: true, finish: "end_turn" }) + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: s1, type: "text", text: "summary", }) - const u3 = await addUser(session.id, "third") - const a4 = await addAssistant(session.id, u3, { finish: "end_turn" }) - await svc.updatePart({ + const u3 = yield* addUser(sessionID, "third") + const a4 = yield* addAssistant(sessionID, u3, { finish: "end_turn" }) + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: a4, type: "text", text: "third reply", }) - const result = MessageV2.filterCompacted(MessageV2.stream(session.id)) + const result = MessageV2.filterCompacted(MessageV2.stream(sessionID)) expect(result.map((item) => item.info.id)).toEqual([c1, s1, a3, u3, a4]) - - await svc.remove(session.id) - }, - }) - }) - - test("prefers latest compaction boundary when repeated compactions exist", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - - const u1 = await addUser(session.id, "first") - const a1 = await addAssistant(session.id, u1, { finish: "end_turn" }) - await svc.updatePart({ + }), + ), + ) + + it.instance("prefers latest compaction boundary when repeated compactions exist", () => + withSession(({ session, sessionID }) => + Effect.gen(function* () { + const u1 = yield* addUser(sessionID, "first") + const a1 = yield* addAssistant(sessionID, u1, { finish: "end_turn" }) + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: a1, type: "text", text: "first reply", }) - const u2 = await addUser(session.id, "second") - const a2 = await addAssistant(session.id, u2, { finish: "end_turn" }) - await svc.updatePart({ + const u2 = yield* addUser(sessionID, "second") + const a2 = yield* addAssistant(sessionID, u2, { finish: "end_turn" }) + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: a2, type: "text", text: "second reply", }) - const c1 = await addUser(session.id) - await addCompactionPart(session.id, c1, u2) - const s1 = await addAssistant(session.id, c1, { summary: true, finish: "end_turn" }) - await svc.updatePart({ + const c1 = yield* addUser(sessionID) + yield* addCompactionPart(sessionID, c1, u2) + const s1 = yield* addAssistant(sessionID, c1, { summary: true, finish: "end_turn" }) + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: s1, type: "text", text: "summary one", }) - const u3 = await addUser(session.id, "third") - const a3 = await addAssistant(session.id, u3, { finish: "end_turn" }) - await svc.updatePart({ + const u3 = yield* addUser(sessionID, "third") + const a3 = yield* addAssistant(sessionID, u3, { finish: "end_turn" }) + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: a3, type: "text", text: "third reply", }) - const c2 = await addUser(session.id) - await addCompactionPart(session.id, c2, u3) - const s2 = await addAssistant(session.id, c2, { summary: true, finish: "end_turn" }) - await svc.updatePart({ + const c2 = yield* addUser(sessionID) + yield* addCompactionPart(sessionID, c2, u3) + const s2 = yield* addAssistant(sessionID, c2, { summary: true, finish: "end_turn" }) + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: s2, type: "text", text: "summary two", }) - const u4 = await addUser(session.id, "fourth") - const a4 = await addAssistant(session.id, u4, { finish: "end_turn" }) - await svc.updatePart({ + const u4 = yield* addUser(sessionID, "fourth") + const a4 = yield* addAssistant(sessionID, u4, { finish: "end_turn" }) + yield* session.updatePart({ id: PartID.ascending(), - sessionID: session.id, + sessionID, messageID: a4, type: "text", text: "fourth reply", }) - const result = MessageV2.filterCompacted(MessageV2.stream(session.id)) + const result = MessageV2.filterCompacted(MessageV2.stream(sessionID)) expect(result.map((item) => item.info.id)).toEqual([c2, s2, u3, a3, u4, a4]) - - await svc.remove(session.id) - }, - }) - }) + }), + ), + ) test("works with array input", () => { // filterCompacted accepts any Iterable, not just generators @@ -1093,54 +993,44 @@ describe("MessageV2.cursor", () => { }) describe("MessageV2 consistency", () => { - test("page hydration matches get for each message", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - await fill(session.id, 3) - - const paged = MessageV2.page({ sessionID: session.id, limit: 10 }) + it.instance("page hydration matches get for each message", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + yield* fill(sessionID, 3) + + const paged = yield* MessageV2.page({ sessionID, limit: 10 }) for (const item of paged.items) { - const got = MessageV2.get({ sessionID: session.id, messageID: item.info.id as MessageID }) + const got = yield* MessageV2.get({ sessionID, messageID: item.info.id as MessageID }) expect(got.info).toEqual(item.info) expect(got.parts).toEqual(item.parts) } + }), + ), + ) - await svc.remove(session.id) - }, - }) - }) - - test("parts from get match standalone parts call", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - const [id] = await fill(session.id, 1) + it.instance("parts from get match standalone parts call", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + const [id] = yield* fill(sessionID, 1) - const got = MessageV2.get({ sessionID: session.id, messageID: id }) + const got = yield* MessageV2.get({ sessionID, messageID: id }) const standalone = MessageV2.parts(id) expect(got.parts).toEqual(standalone) + }), + ), + ) - await svc.remove(session.id) - }, - }) - }) - - test("stream collects same messages as exhaustive page iteration", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - await fill(session.id, 7) + it.instance("stream collects same messages as exhaustive page iteration", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + yield* fill(sessionID, 7) - const streamed = Array.from(MessageV2.stream(session.id)) + const streamed = Array.from(MessageV2.stream(sessionID)) const paged = [] as MessageV2.WithParts[] let cursor: string | undefined while (true) { - const result = MessageV2.page({ sessionID: session.id, limit: 3, before: cursor }) + const result = yield* MessageV2.page({ sessionID, limit: 3, before: cursor }) for (let i = result.items.length - 1; i >= 0; i--) { paged.push(result.items[i]) } @@ -1149,26 +1039,20 @@ describe("MessageV2 consistency", () => { } expect(streamed.map((m) => m.info.id)).toEqual(paged.map((m) => m.info.id)) + }), + ), + ) - await svc.remove(session.id) - }, - }) - }) + it.instance("filterCompacted of full stream returns same as Array.from when no compaction", () => + withSession(({ sessionID }) => + Effect.gen(function* () { + yield* fill(sessionID, 4) - test("filterCompacted of full stream returns same as Array.from when no compaction", async () => { - await WithInstance.provide({ - directory: root, - fn: async () => { - const session = await svc.create({}) - await fill(session.id, 4) - - const filtered = MessageV2.filterCompacted(MessageV2.stream(session.id)) - const all = Array.from(MessageV2.stream(session.id)).reverse() + const filtered = MessageV2.filterCompacted(MessageV2.stream(sessionID)) + const all = Array.from(MessageV2.stream(sessionID)).reverse() expect(filtered.map((m) => m.info.id)).toEqual(all.map((m) => m.info.id)) - - await svc.remove(session.id) - }, - }) - }) + }), + ), + ) }) diff --git a/packages/opencode/test/session/processor-effect.test.ts b/packages/opencode/test/session/processor-effect.test.ts index d9406640bb2..0f057b6d139 100644 --- a/packages/opencode/test/session/processor-effect.test.ts +++ b/packages/opencode/test/session/processor-effect.test.ts @@ -26,6 +26,7 @@ import { provideTmpdirServer } from "../fixture/fixture" import { testEffect } from "../lib/effect" import { raw, reply, TestLLMServer } from "../lib/llm-server" import { SyncEvent } from "@/sync" +import { RuntimeFlags } from "@/effect/runtime-flags" void Log.init({ print: false }) @@ -105,6 +106,17 @@ function defer() { return { promise, resolve } } +const waitFor = (check: Effect.Effect, message: string) => + Effect.gen(function* () { + const stop = Date.now() + 500 + while (Date.now() < stop) { + const value = yield* check + if (value !== undefined) return value + yield* Effect.sleep("10 millis") + } + return yield* Effect.fail(new Error(message)) + }) + const user = Effect.fn("TestSession.user")(function* (sessionID: SessionID, text: string) { const session = yield* Session.Service const msg = yield* session.updateMessage({ @@ -172,7 +184,12 @@ const deps = Layer.mergeAll( ).pipe(Layer.provideMerge(infra)) const env = Layer.mergeAll( TestLLMServer.layer, - SessionProcessor.layer.pipe(Layer.provide(summary), Layer.provide(Image.defaultLayer), Layer.provideMerge(deps)), + SessionProcessor.layer.pipe( + Layer.provide(summary), + Layer.provide(Image.defaultLayer), + Layer.provide(RuntimeFlags.layer({ experimentalEventSystem: true })), + Layer.provideMerge(deps), + ), ) const it = testEffect(env) @@ -296,15 +313,10 @@ it.live("session.processor effect tests preserve text start time", () => }) .pipe(Effect.forkChild) - yield* Effect.promise(async () => { - const stop = Date.now() + 500 - while (Date.now() < stop) { - const text = MessageV2.parts(msg.id).find((part): part is MessageV2.TextPart => part.type === "text") - if (text?.time?.start) return - await Bun.sleep(10) - } - throw new Error("timed out waiting for text part") - }) + yield* waitFor( + Effect.sync(() => MessageV2.parts(msg.id).find((part): part is MessageV2.TextPart => part.type === "text")), + "timed out waiting for text part", + ) yield* Effect.sleep("20 millis") gate.resolve() @@ -692,14 +704,10 @@ it.live("session.processor effect tests mark pending tools as aborted on cleanup .pipe(Effect.forkChild) yield* llm.wait(1) - yield* Effect.promise(async () => { - const end = Date.now() + 500 - while (Date.now() < end) { - const parts = await MessageV2.parts(msg.id) - if (parts.some((part) => part.type === "tool")) return - await Bun.sleep(10) - } - }) + yield* waitFor( + Effect.sync(() => MessageV2.parts(msg.id).find((part): part is MessageV2.ToolPart => part.type === "tool")), + "timed out waiting for tool part", + ) yield* Fiber.interrupt(run) const exit = yield* Fiber.await(run) @@ -774,7 +782,7 @@ it.live("session.processor effect tests record aborted errors and idle state", ( const exit = yield* Fiber.await(run) yield* Effect.promise(() => seen.promise) - const stored = MessageV2.get({ sessionID: chat.id, messageID: msg.id }) + const stored = yield* MessageV2.get({ sessionID: chat.id, messageID: msg.id }) const state = yield* sts.get(chat.id) off() @@ -836,7 +844,7 @@ it.live("session.processor effect tests mark interruptions aborted without manua yield* Fiber.interrupt(run) const exit = yield* Fiber.await(run) - const stored = MessageV2.get({ sessionID: chat.id, messageID: msg.id }) + const stored = yield* MessageV2.get({ sessionID: chat.id, messageID: msg.id }) const state = yield* sts.get(chat.id) expect(Exit.isFailure(exit)).toBe(true) diff --git a/packages/opencode/test/session/prompt.test.ts b/packages/opencode/test/session/prompt.test.ts index 7d53395556b..82836741ce5 100644 --- a/packages/opencode/test/session/prompt.test.ts +++ b/packages/opencode/test/session/prompt.test.ts @@ -48,6 +48,7 @@ import { Truncate } from "@/tool/truncate" import * as Log from "@opencode-ai/core/util/log" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import * as Database from "../../src/storage/db" +import { Storage } from "../../src/storage/storage" import { Ripgrep } from "../../src/file/ripgrep" import { Format } from "../../src/format" import { Reference } from "../../src/reference/reference" @@ -55,6 +56,8 @@ import { provideTmpdirInstance, provideTmpdirServer } from "../fixture/fixture" import { testEffect } from "../lib/effect" import { reply, TestLLMServer } from "../lib/llm-server" import { SyncEvent } from "@/sync" +import { RuntimeFlags } from "@/effect/runtime-flags" +import { BackgroundJob } from "@/background/job" void Log.init({ print: false }) const summary = Layer.succeed( @@ -178,6 +181,7 @@ const infra = Layer.mergeAll(NodeFileSystem.layer, CrossSpawnSpawner.defaultLaye function makeHttp() { const deps = Layer.mergeAll( Session.defaultLayer, + BackgroundJob.defaultLayer, Snapshot.defaultLayer, LLM.defaultLayer, Env.defaultLayer, @@ -192,7 +196,7 @@ function makeHttp() { AppFileSystem.defaultLayer, status, SyncEvent.defaultLayer, - ).pipe(Layer.provideMerge(infra)) + ).pipe(Layer.provideMerge(infra), Layer.provide(RuntimeFlags.layer({ experimentalEventSystem: true }))) const question = Question.layer.pipe(Layer.provideMerge(deps)) const todo = Todo.layer.pipe(Layer.provideMerge(deps)) const registry = ToolRegistry.layer.pipe( @@ -216,6 +220,7 @@ function makeHttp() { const compact = SessionCompaction.layer.pipe(Layer.provideMerge(proc), Layer.provideMerge(deps)) return Layer.mergeAll( TestLLMServer.layer, + BackgroundJob.defaultLayer, SessionPrompt.layer.pipe( Layer.provide(SessionRevert.defaultLayer), Layer.provide(Image.defaultLayer), @@ -230,7 +235,21 @@ function makeHttp() { Layer.provide(SystemPrompt.defaultLayer), Layer.provideMerge(deps), ), - ).pipe(Layer.provide(summary)) + ).pipe( + Layer.provide( + Layer.mergeAll( + summary, + deps, + Config.defaultLayer, + RuntimeFlags.layer({ experimentalEventSystem: true }), + BackgroundJob.defaultLayer, + Bus.layer, + infra, + Storage.defaultLayer, + Reference.defaultLayer, + ), + ), + ) } const it = testEffect(makeHttp()) @@ -1132,9 +1151,13 @@ it.live( yield* sessions.updateMessage(childAssistant) yield* ctx.metadata({ title: "done", - metadata: { sessionId: child.id, model: ref, variant: undefined }, + metadata: { parentSessionId: ctx.sessionID, sessionId: child.id, model: ref, variant: undefined }, }) - return { title: "done", metadata: { sessionId: child.id, model: ref, variant: undefined }, output: "done" } + return { + title: "done", + metadata: { parentSessionId: ctx.sessionID, sessionId: child.id, model: ref, variant: undefined }, + output: "done", + } }) yield* Effect.addFinalizer(() => Effect.sync(() => void (task.execute = original))) @@ -2164,7 +2187,7 @@ it.live("keeps stored part order stable when file resolution is async", () => if (msg.info.role !== "user") throw new Error("expected user message") - const stored = MessageV2.get({ + const stored = yield* MessageV2.get({ sessionID: session.id, messageID: msg.info.id, }) @@ -2206,7 +2229,7 @@ it.live("handles filenames with # character", () => parts, noReply: true, }) - const stored = MessageV2.get({ sessionID: session.id, messageID: message.info.id }) + const stored = yield* MessageV2.get({ sessionID: session.id, messageID: message.info.id }) const textParts = stored.parts.filter((part) => part.type === "text") const hasContent = textParts.some((part) => part.text.includes("special content")) expect(hasContent).toBe(true) diff --git a/packages/opencode/test/session/retry.test.ts b/packages/opencode/test/session/retry.test.ts index 88b61747548..9ecd9ba72f3 100644 --- a/packages/opencode/test/session/retry.test.ts +++ b/packages/opencode/test/session/retry.test.ts @@ -2,7 +2,7 @@ import { describe, expect, test } from "bun:test" import type { NamedError } from "@opencode-ai/core/util/error" import { APICallError } from "ai" import { setTimeout as sleep } from "node:timers/promises" -import { Effect, Layer, Schedule } from "effect" +import { Effect, Layer, Schedule, Schema } from "effect" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { SessionRetry } from "../../src/session/retry" import { MessageV2 } from "../../src/session/message-v2" @@ -17,7 +17,7 @@ const retryProvider = "test" const it = testEffect(Layer.mergeAll(SessionStatus.defaultLayer, CrossSpawnSpawner.defaultLayer)) function apiError(headers?: Record): MessageV2.APIError { - return MessageV2.APIError.Schema.parse( + return Schema.decodeUnknownSync(MessageV2.APIError.Schema)( new MessageV2.APIError({ message: "boom", isRetryable: true, @@ -94,7 +94,7 @@ describe("session.retry.delay", () => { const step = yield* Schedule.toStepWithMetadata( SessionRetry.policy({ provider: "test", - parse: (err) => MessageV2.APIError.Schema.parse(err), + parse: (err) => Schema.decodeUnknownSync(MessageV2.APIError.Schema)(err), set: (info) => status.set(sessionID, { type: "retry", @@ -173,7 +173,7 @@ describe("session.retry.retryable", () => { }) test("retries 500 errors even when isRetryable is false", () => { - const error = MessageV2.APIError.Schema.parse( + const error = Schema.decodeUnknownSync(MessageV2.APIError.Schema)( new MessageV2.APIError({ message: "Internal server error", isRetryable: false, @@ -186,7 +186,7 @@ describe("session.retry.retryable", () => { }) test("retries 502 bad gateway errors", () => { - const error = MessageV2.APIError.Schema.parse( + const error = Schema.decodeUnknownSync(MessageV2.APIError.Schema)( new MessageV2.APIError({ message: "Bad gateway", isRetryable: false, @@ -198,7 +198,7 @@ describe("session.retry.retryable", () => { }) test("retries 503 service unavailable errors", () => { - const error = MessageV2.APIError.Schema.parse( + const error = Schema.decodeUnknownSync(MessageV2.APIError.Schema)( new MessageV2.APIError({ message: "Service unavailable", isRetryable: false, @@ -210,7 +210,7 @@ describe("session.retry.retryable", () => { }) test("does not retry 4xx errors when isRetryable is false", () => { - const error = MessageV2.APIError.Schema.parse( + const error = Schema.decodeUnknownSync(MessageV2.APIError.Schema)( new MessageV2.APIError({ message: "Bad request", isRetryable: false, @@ -222,7 +222,7 @@ describe("session.retry.retryable", () => { }) test("retries ZlibError decompression failures", () => { - const error = MessageV2.APIError.Schema.parse( + const error = Schema.decodeUnknownSync(MessageV2.APIError.Schema)( new MessageV2.APIError({ message: "Response decompression failed", isRetryable: true, @@ -237,7 +237,7 @@ describe("session.retry.retryable", () => { // kilocode_change start - Kilo does not support OpenCode Go upsells test("does not retry free usage limits", () => { - const error = MessageV2.APIError.Schema.parse( + const error = Schema.decodeUnknownSync(MessageV2.APIError.Schema)( new MessageV2.APIError({ message: "Free usage exceeded", isRetryable: true, @@ -293,7 +293,7 @@ describe("session.message-v2.fromError", () => { ) test("ECONNRESET socket error is retryable", () => { - const error = MessageV2.APIError.Schema.parse( + const error = Schema.decodeUnknownSync(MessageV2.APIError.Schema)( new MessageV2.APIError({ message: "Connection reset by server", isRetryable: true, diff --git a/packages/opencode/test/session/schema-decoding.test.ts b/packages/opencode/test/session/schema-decoding.test.ts index 191c507f081..3180dbacf6a 100644 --- a/packages/opencode/test/session/schema-decoding.test.ts +++ b/packages/opencode/test/session/schema-decoding.test.ts @@ -14,12 +14,7 @@ import { WorkspaceID } from "../../src/control-plane/schema" // Covers the session-domain Effect Schema migration. For each migrated // schema we assert: // 1. The Effect decoder (`Schema.decodeUnknownSync`) accepts valid input. -// 2. The derived Zod (`X.zod.parse`) accepts the same input and returns the -// same shape for schemas that still expose Zod statics. -// 3. Clearly-invalid input is rejected by both paths where both exist. -// -// The point is to lock down the Schema <-> Zod bridge so a future edit to -// any input schema can't silently drop or widen a field on one side. +// 2. Clearly-invalid input is rejected. // Representative valid IDs — the branded schemas require the right prefix // (see src/id/id.ts). @@ -27,7 +22,7 @@ const sessionID = Schema.decodeUnknownSync(SessionID)("ses_01J5Y5H0AH4Q4NXJ6P4C3 const sessionIDChild = Schema.decodeUnknownSync(SessionID)("ses_01J5Y5H0AH4Q4NXJ6P4C3P5V2L") const messageID = Schema.decodeUnknownSync(MessageID)("msg_01J5Y5H0AH4Q4NXJ6P4C3P5V2M") const partID = Schema.decodeUnknownSync(PartID)("prt_01J5Y5H0AH4Q4NXJ6P4C3P5V2N") -const projectID = ProjectID.zod.parse("proj-alpha") +const projectID = ProjectID.make("proj-alpha") const workspaceID = Schema.decodeUnknownSync(WorkspaceID)("wrk-primary") function decodeUnknown(schema: S) { @@ -49,7 +44,6 @@ describe("Session.Info", () => { time: { created: 1, updated: 2 }, } expect(decode(input)).toEqual(input) - expect(Session.Info.zod.parse(input)).toEqual(input) }) test("round-trips every optional field", () => { @@ -80,7 +74,6 @@ describe("Session.Info", () => { }, } expect(decode(input)).toEqual(input) - expect(Session.Info.zod.parse(input)).toEqual(input) }) test("accepts migrated summary diffs without file details", () => { @@ -100,19 +93,16 @@ describe("Session.Info", () => { time: { created: 1, updated: 2 }, } expect(decode(input)).toEqual(input) - expect(Session.Info.zod.parse(input)).toEqual(input) }) test("rejects unbranded session id", () => { const bad = { id: "not-a-session-id" } as unknown expect(() => decode(bad)).toThrow() - expect(() => Session.Info.zod.parse(bad)).toThrow() }) test("rejects missing required fields", () => { const bad = { id: sessionID } as unknown expect(() => decode(bad)).toThrow() - expect(() => Session.Info.zod.parse(bad)).toThrow() }) }) @@ -124,8 +114,6 @@ describe("Session.ProjectInfo", () => { const withName = { ...noName, name: "alpha" } expect(decode(noName)).toEqual(noName) expect(decode(withName)).toEqual(withName) - expect(Session.ProjectInfo.zod.parse(noName)).toEqual(noName) - expect(Session.ProjectInfo.zod.parse(withName)).toEqual(withName) }) }) @@ -144,7 +132,6 @@ describe("Session.GlobalInfo", () => { project: null, } expect(decode(input)).toEqual(input) - expect(Session.GlobalInfo.zod.parse(input)).toEqual(input) }) test("accepts populated project", () => { @@ -159,7 +146,6 @@ describe("Session.GlobalInfo", () => { project: { id: projectID, worktree: "/tmp/wt", name: "alpha" }, } expect(decode(input)).toEqual(input) - expect(Session.GlobalInfo.zod.parse(input)).toEqual(input) }) }) @@ -167,7 +153,6 @@ describe("Session input schemas", () => { test("CreateInput accepts undefined and populated forms", () => { const decode = decodeUnknown(Session.CreateInput) expect(decode(undefined)).toBeUndefined() - expect(Session.CreateInput.zod.parse(undefined)).toBeUndefined() const populated = { parentID: sessionID, @@ -176,23 +161,19 @@ describe("Session input schemas", () => { workspaceID, } expect(decode(populated)).toEqual(populated) - expect(Session.CreateInput.zod.parse(populated)).toEqual(populated) }) test("ForkInput round-trips", () => { const decode = decodeUnknown(Session.ForkInput) const input = { sessionID, messageID } expect(decode(input)).toEqual(input) - expect(Session.ForkInput.zod.parse(input)).toEqual(input) // messageID is optional const bare = { sessionID } expect(decode(bare)).toEqual(bare) - expect(Session.ForkInput.zod.parse(bare)).toEqual(bare) }) test("SetTitleInput rejects missing title", () => { expect(() => decodeUnknown(Session.SetTitleInput)({ sessionID })).toThrow() - expect(() => Session.SetTitleInput.zod.parse({ sessionID })).toThrow() }) test("SetArchivedInput accepts both with and without time", () => { @@ -221,14 +202,11 @@ describe("SessionRevert.RevertInput", () => { test("messageID is required, partID is optional", () => { const withPart = { sessionID, messageID, partID } expect(decode(withPart)).toEqual(withPart) - expect(SessionRevert.RevertInput.zod.parse(withPart)).toEqual(withPart) const noPart = { sessionID, messageID } expect(decode(noPart)).toEqual(noPart) - expect(SessionRevert.RevertInput.zod.parse(noPart)).toEqual(noPart) expect(() => decode({ sessionID })).toThrow() - expect(() => SessionRevert.RevertInput.zod.parse({ sessionID })).toThrow() }) }) @@ -247,7 +225,6 @@ describe("SessionStatus.Info", () => { test("idle / busy discriminators", () => { expect(decode({ type: "idle" })).toEqual({ type: "idle" }) expect(decode({ type: "busy" })).toEqual({ type: "busy" }) - expect(SessionStatus.Info.zod.parse({ type: "idle" })).toEqual({ type: "idle" }) }) test("retry carries attempt/message/action/next", () => { @@ -266,12 +243,10 @@ describe("SessionStatus.Info", () => { next: 500, } expect(decode(input)).toEqual(input) - expect(SessionStatus.Info.zod.parse(input)).toEqual(input) }) test("rejects unknown type", () => { expect(() => decode({ type: "bogus" })).toThrow() - expect(() => SessionStatus.Info.zod.parse({ type: "bogus" })).toThrow() }) }) @@ -281,7 +256,6 @@ describe("Todo.Info", () => { test("three-field round-trip", () => { const input = { content: "do a thing", status: "pending", priority: "high" } expect(decode(input)).toEqual(input) - expect(Todo.Info.zod.parse(input)).toEqual(input) }) }) @@ -289,7 +263,6 @@ describe("SessionPrompt input schemas", () => { test("LoopInput is just sessionID", () => { const decode = decodeUnknown(SessionPrompt.LoopInput) expect(decode({ sessionID })).toEqual({ sessionID }) - expect(SessionPrompt.LoopInput.zod.parse({ sessionID } as unknown)).toEqual({ sessionID }) }) test("ShellInput requires agent + command", () => { @@ -297,7 +270,6 @@ describe("SessionPrompt input schemas", () => { const expected = { sessionID, agent: "build", command: "echo hi" } const input: unknown = expected expect(decode(input)).toEqual(expected) - expect(SessionPrompt.ShellInput.zod.parse(input as unknown)).toEqual(expected) expect(() => decode({ sessionID })).toThrow() }) @@ -315,9 +287,6 @@ describe("SessionPrompt input schemas", () => { expect(decoded.parts).toHaveLength(2) expect(decoded.parts[0]).toMatchObject({ type: "text", text: "hello" }) expect(decoded.parts[1]).toMatchObject({ type: "file", mime: "image/png" }) - - const viaZod = SessionPrompt.PromptInput.zod.parse(input) - expect(viaZod.parts).toHaveLength(2) }) test("PromptInput rejects unknown part type", () => { @@ -327,7 +296,6 @@ describe("SessionPrompt input schemas", () => { parts: [{ type: "nonsense", payload: 42 }], } expect(() => decode(bad)).toThrow() - expect(() => SessionPrompt.PromptInput.zod.parse(bad)).toThrow() }) test("CommandInput round-trips core fields", () => { @@ -339,6 +307,5 @@ describe("SessionPrompt input schemas", () => { } const input: unknown = expected expect(decode(input)).toEqual(expected) - expect(SessionPrompt.CommandInput.zod.parse(input)).toEqual(expected) }) }) diff --git a/packages/opencode/test/session/session-schema.test.ts b/packages/opencode/test/session/session-schema.test.ts index 38531d15b49..906414fdbe5 100644 --- a/packages/opencode/test/session/session-schema.test.ts +++ b/packages/opencode/test/session/session-schema.test.ts @@ -12,6 +12,8 @@ const info = { directory: "/tmp/opencode", parentID: undefined, summary: undefined, + cost: 0, + tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }, share: undefined, title: "Test session", version: "1.0.0", diff --git a/packages/opencode/test/session/snapshot-tool-race.test.ts b/packages/opencode/test/session/snapshot-tool-race.test.ts index 774b90bd8da..e4476a20e98 100644 --- a/packages/opencode/test/session/snapshot-tool-race.test.ts +++ b/packages/opencode/test/session/snapshot-tool-race.test.ts @@ -30,6 +30,7 @@ import { TestLLMServer } from "../lib/llm-server" // Same layer setup as prompt-effect.test.ts import { NodeFileSystem } from "@effect/platform-node" import { Agent as AgentSvc } from "../../src/agent/agent" +import { BackgroundJob } from "@/background/job" import { Git } from "../../src/git" import { Bus } from "../../src/bus" import { Command } from "../../src/command" @@ -59,6 +60,7 @@ import { Ripgrep } from "../../src/file/ripgrep" import { Format } from "../../src/format" import { Reference } from "../../src/reference/reference" import { SyncEvent } from "@/sync" +import { RuntimeFlags } from "@/effect/runtime-flags" void Log.init({ print: false }) @@ -124,6 +126,7 @@ function makeHttp() { lsp, mcp, AppFileSystem.defaultLayer, + BackgroundJob.defaultLayer, status, SyncEvent.defaultLayer, ).pipe(Layer.provideMerge(infra)) @@ -137,6 +140,7 @@ function makeHttp() { Layer.provide(Reference.defaultLayer), Layer.provide(Ripgrep.defaultLayer), Layer.provide(Format.defaultLayer), + Layer.provide(RuntimeFlags.layer({ experimentalEventSystem: true })), Layer.provideMerge(todo), Layer.provideMerge(question), Layer.provideMerge(deps), @@ -145,15 +149,21 @@ function makeHttp() { const proc = SessionProcessor.layer.pipe( Layer.provide(SessionSummary.defaultLayer), Layer.provide(Image.defaultLayer), + Layer.provide(RuntimeFlags.layer({ experimentalEventSystem: true })), + Layer.provideMerge(deps), + ) + const compact = SessionCompaction.layer.pipe( + Layer.provide(RuntimeFlags.layer({ experimentalEventSystem: true })), + Layer.provideMerge(proc), Layer.provideMerge(deps), ) - const compact = SessionCompaction.layer.pipe(Layer.provideMerge(proc), Layer.provideMerge(deps)) return Layer.mergeAll( TestLLMServer.layer, SessionSummary.defaultLayer, SessionPrompt.layer.pipe( Layer.provide(SessionRevert.defaultLayer), Layer.provide(Image.defaultLayer), + Layer.provide(Reference.defaultLayer), Layer.provide(SessionSummary.defaultLayer), Layer.provideMerge(run), Layer.provideMerge(compact), @@ -163,6 +173,7 @@ function makeHttp() { Layer.provideMerge(question), // kilocode_change - SessionPrompt now dismisses questions via its service dependency Layer.provide(Instruction.defaultLayer), Layer.provide(SystemPrompt.defaultLayer), + Layer.provide(RuntimeFlags.layer({ experimentalEventSystem: true })), Layer.provideMerge(deps), ), ) diff --git a/packages/opencode/test/session/structured-output-integration.test.ts b/packages/opencode/test/session/structured-output-integration.test.ts index da2ffb79373..125c63c0f9d 100644 --- a/packages/opencode/test/session/structured-output-integration.test.ts +++ b/packages/opencode/test/session/structured-output-integration.test.ts @@ -1,250 +1,219 @@ import { describe, expect, test } from "bun:test" -import path from "path" import { Effect, Layer } from "effect" import { Session } from "@/session/session" import { SessionPrompt } from "../../src/session/prompt" import * as Log from "@opencode-ai/core/util/log" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" import { MessageV2 } from "../../src/session/message-v2" +import { testEffect } from "../lib/effect" -const projectRoot = path.join(__dirname, "../..") void Log.init({ print: false }) // Skip tests if no API key is available const hasApiKey = !!process.env.ANTHROPIC_API_KEY - -// Helper to run test within Instance context -async function withInstance(fn: () => Promise): Promise { - return WithInstance.provide({ - directory: projectRoot, - fn, - }) -} - -function run(fx: Effect.Effect) { - return Effect.runPromise( - fx.pipe(Effect.scoped, Effect.provide(Layer.mergeAll(SessionPrompt.defaultLayer, Session.defaultLayer))), - ) -} +const it = testEffect(Layer.mergeAll(SessionPrompt.defaultLayer, Session.defaultLayer)) +const live = hasApiKey ? it.instance : it.instance.skip describe("StructuredOutput Integration", () => { - test.skipIf(!hasApiKey)( + live( "produces structured output with simple schema", - async () => { - await withInstance(() => - run( - Effect.gen(function* () { - const prompt = yield* SessionPrompt.Service - const sessions = yield* Session.Service - const session = yield* sessions.create({ title: "Structured Output Test" }) - - const result = yield* prompt.prompt({ - sessionID: session.id, - parts: [ - { - type: "text", - text: "What is 2 + 2? Provide a simple answer.", - }, - ], - format: { - type: "json_schema", - schema: { - type: "object", - properties: { - answer: { type: "number", description: "The numerical answer" }, - explanation: { type: "string", description: "Brief explanation" }, - }, - required: ["answer"], - }, - retryCount: 0, + () => + Effect.gen(function* () { + const prompt = yield* SessionPrompt.Service + const sessions = yield* Session.Service + const session = yield* sessions.create({ title: "Structured Output Test" }) + + const result = yield* prompt.prompt({ + sessionID: session.id, + parts: [ + { + type: "text", + text: "What is 2 + 2? Provide a simple answer.", + }, + ], + format: { + type: "json_schema", + schema: { + type: "object", + properties: { + answer: { type: "number", description: "The numerical answer" }, + explanation: { type: "string", description: "Brief explanation" }, }, - }) - - // Verify structured output was captured (only on assistant messages) - expect(result.info.role).toBe("assistant") - if (result.info.role === "assistant") { - expect(result.info.structured).toBeDefined() - expect(typeof result.info.structured).toBe("object") - - const output = result.info.structured as any - expect(output.answer).toBe(4) - - // Verify no error was set - expect(result.info.error).toBeUndefined() - } - - // Clean up - // Note: Not removing session to avoid race with background SessionSummary.summarize - }), - ), - ) - }, + required: ["answer"], + }, + retryCount: 0, + }, + }) + + // Verify structured output was captured (only on assistant messages) + expect(result.info.role).toBe("assistant") + if (result.info.role === "assistant") { + expect(result.info.structured).toBeDefined() + expect(typeof result.info.structured).toBe("object") + + const output = result.info.structured as any + expect(output.answer).toBe(4) + + // Verify no error was set + expect(result.info.error).toBeUndefined() + } + + // Clean up + // Note: Not removing session to avoid race with background SessionSummary.summarize + }), + { git: true }, 60000, ) - test.skipIf(!hasApiKey)( + live( "produces structured output with nested objects", - async () => { - await withInstance(() => - run( - Effect.gen(function* () { - const prompt = yield* SessionPrompt.Service - const sessions = yield* Session.Service - const session = yield* sessions.create({ title: "Nested Schema Test" }) - - const result = yield* prompt.prompt({ - sessionID: session.id, - parts: [ - { - type: "text", - text: "Tell me about Anthropic company in a structured format.", - }, - ], - format: { - type: "json_schema", - schema: { + () => + Effect.gen(function* () { + const prompt = yield* SessionPrompt.Service + const sessions = yield* Session.Service + const session = yield* sessions.create({ title: "Nested Schema Test" }) + + const result = yield* prompt.prompt({ + sessionID: session.id, + parts: [ + { + type: "text", + text: "Tell me about Anthropic company in a structured format.", + }, + ], + format: { + type: "json_schema", + schema: { + type: "object", + properties: { + company: { type: "object", properties: { - company: { - type: "object", - properties: { - name: { type: "string" }, - founded: { type: "number" }, - }, - required: ["name", "founded"], - }, - products: { - type: "array", - items: { type: "string" }, - }, + name: { type: "string" }, + founded: { type: "number" }, }, - required: ["company"], + required: ["name", "founded"], + }, + products: { + type: "array", + items: { type: "string" }, }, - retryCount: 0, }, - }) - - // Verify structured output was captured (only on assistant messages) - expect(result.info.role).toBe("assistant") - if (result.info.role === "assistant") { - expect(result.info.structured).toBeDefined() - const output = result.info.structured as any - - expect(output.company).toBeDefined() - expect(output.company.name).toBe("Anthropic") - expect(typeof output.company.founded).toBe("number") - - if (output.products) { - expect(Array.isArray(output.products)).toBe(true) - } - - // Verify no error was set - expect(result.info.error).toBeUndefined() - } - - // Clean up - // Note: Not removing session to avoid race with background SessionSummary.summarize - }), - ), - ) - }, + required: ["company"], + }, + retryCount: 0, + }, + }) + + // Verify structured output was captured (only on assistant messages) + expect(result.info.role).toBe("assistant") + if (result.info.role === "assistant") { + expect(result.info.structured).toBeDefined() + const output = result.info.structured as any + + expect(output.company).toBeDefined() + expect(output.company.name).toBe("Anthropic") + expect(typeof output.company.founded).toBe("number") + + if (output.products) { + expect(Array.isArray(output.products)).toBe(true) + } + + // Verify no error was set + expect(result.info.error).toBeUndefined() + } + + // Clean up + // Note: Not removing session to avoid race with background SessionSummary.summarize + }), + { git: true }, 60000, ) - test.skipIf(!hasApiKey)( + live( "works with text outputFormat (default)", - async () => { - await withInstance(() => - run( - Effect.gen(function* () { - const prompt = yield* SessionPrompt.Service - const sessions = yield* Session.Service - const session = yield* sessions.create({ title: "Text Output Test" }) - - const result = yield* prompt.prompt({ - sessionID: session.id, - parts: [ - { - type: "text", - text: "Say hello.", - }, - ], - format: { - type: "text", - }, - }) - - // Verify no structured output (text mode) and no error - expect(result.info.role).toBe("assistant") - if (result.info.role === "assistant") { - expect(result.info.structured).toBeUndefined() - expect(result.info.error).toBeUndefined() - } - - // Verify we got a response with parts - expect(result.parts.length).toBeGreaterThan(0) - - // Clean up - // Note: Not removing session to avoid race with background SessionSummary.summarize - }), - ), - ) - }, + () => + Effect.gen(function* () { + const prompt = yield* SessionPrompt.Service + const sessions = yield* Session.Service + const session = yield* sessions.create({ title: "Text Output Test" }) + + const result = yield* prompt.prompt({ + sessionID: session.id, + parts: [ + { + type: "text", + text: "Say hello.", + }, + ], + format: { + type: "text", + }, + }) + + // Verify no structured output (text mode) and no error + expect(result.info.role).toBe("assistant") + if (result.info.role === "assistant") { + expect(result.info.structured).toBeUndefined() + expect(result.info.error).toBeUndefined() + } + + // Verify we got a response with parts + expect(result.parts.length).toBeGreaterThan(0) + + // Clean up + // Note: Not removing session to avoid race with background SessionSummary.summarize + }), + { git: true }, 60000, ) - test.skipIf(!hasApiKey)( + live( "stores outputFormat on user message", - async () => { - await withInstance(() => - run( - Effect.gen(function* () { - const prompt = yield* SessionPrompt.Service - const sessions = yield* Session.Service - const session = yield* sessions.create({ title: "OutputFormat Storage Test" }) - - yield* prompt.prompt({ - sessionID: session.id, - parts: [ - { - type: "text", - text: "What is 1 + 1?", - }, - ], - format: { - type: "json_schema", - schema: { - type: "object", - properties: { - result: { type: "number" }, - }, - required: ["result"], - }, - retryCount: 3, + () => + Effect.gen(function* () { + const prompt = yield* SessionPrompt.Service + const sessions = yield* Session.Service + const session = yield* sessions.create({ title: "OutputFormat Storage Test" }) + + yield* prompt.prompt({ + sessionID: session.id, + parts: [ + { + type: "text", + text: "What is 1 + 1?", + }, + ], + format: { + type: "json_schema", + schema: { + type: "object", + properties: { + result: { type: "number" }, }, - }) - - // Get all messages from session - const messages = yield* sessions.messages({ sessionID: session.id }) - const userMessage = messages.find((m) => m.info.role === "user") - - // Verify outputFormat was stored on user message - expect(userMessage).toBeDefined() - if (userMessage?.info.role === "user") { - expect(userMessage.info.format).toBeDefined() - expect(userMessage.info.format?.type).toBe("json_schema") - if (userMessage.info.format?.type === "json_schema") { - expect(userMessage.info.format.retryCount).toBe(3) - } - } - - // Clean up - // Note: Not removing session to avoid race with background SessionSummary.summarize - }), - ), - ) - }, + required: ["result"], + }, + retryCount: 3, + }, + }) + + // Get all messages from session + const messages = yield* sessions.messages({ sessionID: session.id }) + const userMessage = messages.find((m) => m.info.role === "user") + + // Verify outputFormat was stored on user message + expect(userMessage).toBeDefined() + if (userMessage?.info.role === "user") { + expect(userMessage.info.format).toBeDefined() + expect(userMessage.info.format?.type).toBe("json_schema") + if (userMessage.info.format?.type === "json_schema") { + expect(userMessage.info.format.retryCount).toBe(3) + } + } + + // Clean up + // Note: Not removing session to avoid race with background SessionSummary.summarize + }), + { git: true }, 60000, ) diff --git a/packages/opencode/test/session/structured-output.test.ts b/packages/opencode/test/session/structured-output.test.ts index c734a182aee..806c5748344 100644 --- a/packages/opencode/test/session/structured-output.test.ts +++ b/packages/opencode/test/session/structured-output.test.ts @@ -1,60 +1,65 @@ import { describe, expect, test } from "bun:test" +import { Exit, Schema } from "effect" import { MessageV2 } from "../../src/session/message-v2" import { SessionPrompt } from "../../src/session/prompt" import { SessionID, MessageID } from "../../src/session/schema" +const decodeFormat = Schema.decodeUnknownExit(MessageV2.Format) +const decodeUser = Schema.decodeUnknownExit(MessageV2.User) +const decodeAssistant = Schema.decodeUnknownExit(MessageV2.Assistant) + describe("structured-output.OutputFormat", () => { test("parses text format", () => { - const result = MessageV2.Format.zod.safeParse({ type: "text" }) - expect(result.success).toBe(true) - if (result.success) { - expect(result.data.type).toBe("text") + const result = decodeFormat({ type: "text" }) + expect(Exit.isSuccess(result)).toBe(true) + if (Exit.isSuccess(result)) { + expect(result.value.type).toBe("text") } }) test("parses json_schema format with defaults", () => { - const result = MessageV2.Format.zod.safeParse({ + const result = decodeFormat({ type: "json_schema", schema: { type: "object", properties: { name: { type: "string" } } }, }) - expect(result.success).toBe(true) - if (result.success) { - expect(result.data.type).toBe("json_schema") - if (result.data.type === "json_schema") { - expect(result.data.retryCount).toBe(2) // default value + expect(Exit.isSuccess(result)).toBe(true) + if (Exit.isSuccess(result)) { + expect(result.value.type).toBe("json_schema") + if (result.value.type === "json_schema") { + expect(result.value.retryCount).toBe(2) // default value } } }) test("parses json_schema format with custom retryCount", () => { - const result = MessageV2.Format.zod.safeParse({ + const result = decodeFormat({ type: "json_schema", schema: { type: "object" }, retryCount: 5, }) - expect(result.success).toBe(true) - if (result.success && result.data.type === "json_schema") { - expect(result.data.retryCount).toBe(5) + expect(Exit.isSuccess(result)).toBe(true) + if (Exit.isSuccess(result) && result.value.type === "json_schema") { + expect(result.value.retryCount).toBe(5) } }) test("rejects invalid type", () => { - const result = MessageV2.Format.zod.safeParse({ type: "invalid" }) - expect(result.success).toBe(false) + const result = decodeFormat({ type: "invalid" }) + expect(Exit.isFailure(result)).toBe(true) }) test("rejects json_schema without schema", () => { - const result = MessageV2.Format.zod.safeParse({ type: "json_schema" }) - expect(result.success).toBe(false) + const result = decodeFormat({ type: "json_schema" }) + expect(Exit.isFailure(result)).toBe(true) }) test("rejects negative retryCount", () => { - const result = MessageV2.Format.zod.safeParse({ + const result = decodeFormat({ type: "json_schema", schema: { type: "object" }, retryCount: -1, }) - expect(result.success).toBe(false) + expect(Exit.isFailure(result)).toBe(true) }) }) @@ -95,7 +100,7 @@ describe("structured-output.StructuredOutputError", () => { describe("structured-output.UserMessage", () => { test("user message accepts outputFormat", () => { - const result = MessageV2.User.zod.safeParse({ + const result = decodeUser({ id: MessageID.ascending(), sessionID: SessionID.descending(), role: "user", @@ -107,11 +112,11 @@ describe("structured-output.UserMessage", () => { schema: { type: "object" }, }, }) - expect(result.success).toBe(true) + expect(Exit.isSuccess(result)).toBe(true) }) test("user message works without outputFormat (optional)", () => { - const result = MessageV2.User.zod.safeParse({ + const result = decodeUser({ id: MessageID.ascending(), sessionID: SessionID.descending(), role: "user", @@ -119,7 +124,7 @@ describe("structured-output.UserMessage", () => { agent: "default", model: { providerID: "anthropic", modelID: "claude-3" }, }) - expect(result.success).toBe(true) + expect(Exit.isSuccess(result)).toBe(true) }) }) @@ -140,19 +145,19 @@ describe("structured-output.AssistantMessage", () => { } test("assistant message accepts structured", () => { - const result = MessageV2.Assistant.zod.safeParse({ + const result = decodeAssistant({ ...baseAssistantMessage, structured: { company: "Anthropic", founded: 2021 }, }) - expect(result.success).toBe(true) - if (result.success) { - expect(result.data.structured).toEqual({ company: "Anthropic", founded: 2021 }) + expect(Exit.isSuccess(result)).toBe(true) + if (Exit.isSuccess(result)) { + expect(result.value.structured).toEqual({ company: "Anthropic", founded: 2021 }) } }) test("assistant message works without structured_output (optional)", () => { - const result = MessageV2.Assistant.zod.safeParse(baseAssistantMessage) - expect(result.success).toBe(true) + const result = decodeAssistant(baseAssistantMessage) + expect(Exit.isSuccess(result)).toBe(true) }) }) diff --git a/packages/opencode/test/skill/discovery.test.ts b/packages/opencode/test/skill/discovery.test.ts index f4a17f25ce9..074992c56cd 100644 --- a/packages/opencode/test/skill/discovery.test.ts +++ b/packages/opencode/test/skill/discovery.test.ts @@ -1,10 +1,12 @@ -import { describe, test, expect, beforeAll, afterAll } from "bun:test" -import { Effect } from "effect" +import { describe, expect, beforeAll, afterAll } from "bun:test" +import { AppFileSystem } from "@opencode-ai/core/filesystem" +import { Effect, Layer } from "effect" import { Discovery } from "../../src/skill/discovery" import { Global } from "@opencode-ai/core/global" import { Filesystem } from "@/util/filesystem" import { rm } from "fs/promises" import path from "path" +import { testEffect } from "../lib/effect" let CLOUDFLARE_SKILLS_URL: string let server: ReturnType @@ -12,6 +14,7 @@ let downloadCount = 0 const fixturePath = path.join(import.meta.dir, "../fixture/skills") const cacheDir = path.join(Global.Path.cache, "skills") +const it = testEffect(Layer.mergeAll(Discovery.defaultLayer, AppFileSystem.defaultLayer)) beforeAll(async () => { await rm(cacheDir, { recursive: true, force: true }) @@ -47,70 +50,90 @@ afterAll(async () => { }) describe("Discovery.pull", () => { - const pull = (url: string) => - Effect.runPromise(Discovery.Service.use((s) => s.pull(url)).pipe(Effect.provide(Discovery.defaultLayer))) - - test("downloads skills from cloudflare url", async () => { - const dirs = await pull(CLOUDFLARE_SKILLS_URL) - expect(dirs.length).toBeGreaterThan(0) - for (const dir of dirs) { - expect(dir).toStartWith(cacheDir) - const md = path.join(dir, "SKILL.md") - expect(await Filesystem.exists(md)).toBe(true) - } - }) - - test("url without trailing slash works", async () => { - const dirs = await pull(CLOUDFLARE_SKILLS_URL.replace(/\/$/, "")) - expect(dirs.length).toBeGreaterThan(0) - for (const dir of dirs) { - const md = path.join(dir, "SKILL.md") - expect(await Filesystem.exists(md)).toBe(true) - } - }) - - test("returns empty array for invalid url", async () => { - const dirs = await pull(`http://localhost:${server.port}/invalid-url/`) - expect(dirs).toEqual([]) - }) - - test("returns empty array for non-json response", async () => { - // any url not explicitly handled in server returns 404 text "Not Found" - const dirs = await pull(`http://localhost:${server.port}/some-other-path/`) - expect(dirs).toEqual([]) - }) - - test("downloads reference files alongside SKILL.md", async () => { - const dirs = await pull(CLOUDFLARE_SKILLS_URL) - // find a skill dir that should have reference files (e.g. agents-sdk) - const agentsSdk = dirs.find((d) => d.endsWith(path.sep + "agents-sdk")) - expect(agentsSdk).toBeDefined() - if (agentsSdk) { - const refs = path.join(agentsSdk, "references") - expect(await Filesystem.exists(path.join(agentsSdk, "SKILL.md"))).toBe(true) - // agents-sdk has reference files per the index - const refDir = await Array.fromAsync(new Bun.Glob("**/*.md").scan({ cwd: refs, onlyFiles: true })) - expect(refDir.length).toBeGreaterThan(0) - } - }) - - test("caches downloaded files on second pull", async () => { - // clear dir and downloadCount - await rm(cacheDir, { recursive: true, force: true }) - downloadCount = 0 - - // first pull to populate cache - const first = await pull(CLOUDFLARE_SKILLS_URL) - expect(first.length).toBeGreaterThan(0) - const firstCount = downloadCount - expect(firstCount).toBeGreaterThan(0) - - // second pull should return same results from cache - const second = await pull(CLOUDFLARE_SKILLS_URL) - expect(second.length).toBe(first.length) - expect(second.sort()).toEqual(first.sort()) - - // second pull should NOT increment download count - expect(downloadCount).toBe(firstCount) - }) + it.live("downloads skills from cloudflare url", () => + Effect.gen(function* () { + const fsys = yield* AppFileSystem.Service + const discovery = yield* Discovery.Service + const dirs = yield* discovery.pull(CLOUDFLARE_SKILLS_URL) + expect(dirs.length).toBeGreaterThan(0) + for (const dir of dirs) { + expect(dir).toStartWith(cacheDir) + const md = path.join(dir, "SKILL.md") + expect(yield* fsys.existsSafe(md)).toBe(true) + } + }), + ) + + it.live("url without trailing slash works", () => + Effect.gen(function* () { + const fsys = yield* AppFileSystem.Service + const discovery = yield* Discovery.Service + const dirs = yield* discovery.pull(CLOUDFLARE_SKILLS_URL.replace(/\/$/, "")) + expect(dirs.length).toBeGreaterThan(0) + for (const dir of dirs) { + const md = path.join(dir, "SKILL.md") + expect(yield* fsys.existsSafe(md)).toBe(true) + } + }), + ) + + it.live("returns empty array for invalid url", () => + Effect.gen(function* () { + const discovery = yield* Discovery.Service + const dirs = yield* discovery.pull(`http://localhost:${server.port}/invalid-url/`) + expect(dirs).toEqual([]) + }), + ) + + it.live("returns empty array for non-json response", () => + Effect.gen(function* () { + // any url not explicitly handled in server returns 404 text "Not Found" + const discovery = yield* Discovery.Service + const dirs = yield* discovery.pull(`http://localhost:${server.port}/some-other-path/`) + expect(dirs).toEqual([]) + }), + ) + + it.live("downloads reference files alongside SKILL.md", () => + Effect.gen(function* () { + const fsys = yield* AppFileSystem.Service + const discovery = yield* Discovery.Service + const dirs = yield* discovery.pull(CLOUDFLARE_SKILLS_URL) + // find a skill dir that should have reference files (e.g. agents-sdk) + const agentsSdk = dirs.find((d) => d.endsWith(path.sep + "agents-sdk")) + expect(agentsSdk).toBeDefined() + if (agentsSdk) { + const refs = path.join(agentsSdk, "references") + expect(yield* fsys.existsSafe(path.join(agentsSdk, "SKILL.md"))).toBe(true) + // agents-sdk has reference files per the index + const refDir = yield* Effect.promise(() => + Array.fromAsync(new Bun.Glob("**/*.md").scan({ cwd: refs, onlyFiles: true })), + ) + expect(refDir.length).toBeGreaterThan(0) + } + }), + ) + + it.live("caches downloaded files on second pull", () => + Effect.gen(function* () { + // clear dir and downloadCount + yield* Effect.promise(() => rm(cacheDir, { recursive: true, force: true })) + downloadCount = 0 + const discovery = yield* Discovery.Service + + // first pull to populate cache + const first = yield* discovery.pull(CLOUDFLARE_SKILLS_URL) + expect(first.length).toBeGreaterThan(0) + const firstCount = downloadCount + expect(firstCount).toBeGreaterThan(0) + + // second pull should return same results from cache + const second = yield* discovery.pull(CLOUDFLARE_SKILLS_URL) + expect(second.length).toBe(first.length) + expect(second.sort()).toEqual(first.sort()) + + // second pull should NOT increment download count + expect(downloadCount).toBe(firstCount) + }), + ) }) diff --git a/packages/opencode/test/skill/skill.test.ts b/packages/opencode/test/skill/skill.test.ts index 63aa472b48d..4bcf8d54e5a 100644 --- a/packages/opencode/test/skill/skill.test.ts +++ b/packages/opencode/test/skill/skill.test.ts @@ -1,8 +1,13 @@ -import { afterAll, beforeAll, describe, expect } from "bun:test" +import { describe, expect } from "bun:test" import { Effect, Layer } from "effect" import { Skill } from "../../src/skill" +import { Discovery } from "../../src/skill/discovery" +import { RuntimeFlags } from "../../src/effect/runtime-flags" +import { Bus } from "../../src/bus" +import { Config } from "../../src/config/config" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" -import { Flag } from "@opencode-ai/core/flag/flag" +import { AppFileSystem } from "@opencode-ai/core/filesystem" +import { Global } from "@opencode-ai/core/global" import { provideInstance, provideTmpdirInstance, tmpdir } from "../fixture/fixture" import { testEffect } from "../lib/effect" import path from "path" @@ -10,7 +15,18 @@ import fs from "fs/promises" const node = CrossSpawnSpawner.defaultLayer -const it = testEffect(Layer.mergeAll(Skill.defaultLayer, node)) +const skills = (disableClaudeCodeSkills: boolean) => + Skill.layer.pipe( + Layer.provide(Discovery.defaultLayer), + Layer.provide(Config.defaultLayer), + Layer.provide(Bus.layer), + Layer.provide(AppFileSystem.defaultLayer), + Layer.provide(Global.layer), + Layer.provide(RuntimeFlags.layer({ disableClaudeCodeSkills })), + ) + +const it = testEffect(Layer.mergeAll(skills(false), node)) +const itWithoutClaudeCodeSkills = testEffect(Layer.mergeAll(skills(true), node)) async function createGlobalSkill(homeDir: string) { const skillDir = path.join(homeDir, ".claude", "skills", "global-test-skill") @@ -44,17 +60,7 @@ const withHome = (home: string, self: Effect.Effect) => ) const discovered = (list: readonly T[]) => - list.filter((s) => s.location !== Skill.BUILTIN_LOCATION) // kilocode_change - -const disabled = Flag.KILO_DISABLE_CLAUDE_CODE_SKILLS - -beforeAll(() => { - Flag.KILO_DISABLE_CLAUDE_CODE_SKILLS = false -}) - -afterAll(() => { - Flag.KILO_DISABLE_CLAUDE_CODE_SKILLS = disabled -}) + list.filter((skill) => ![Skill.BUILTIN_LOCATION, ""].includes(skill.location)) // kilocode_change describe("skill", () => { // kilocode_change start @@ -382,6 +388,43 @@ description: A skill in the .agents/skills directory. ), ) + itWithoutClaudeCodeSkills.live("skips Claude Code skills when disabled", () => + provideTmpdirInstance( + (dir) => + Effect.gen(function* () { + yield* Effect.promise(() => + Promise.all([ + Bun.write( + path.join(dir, ".claude", "skills", "claude-skill", "SKILL.md"), + `--- +name: claude-skill +description: A skill in the .claude/skills directory. +--- + +# Claude Skill +`, + ), + Bun.write( + path.join(dir, ".agents", "skills", "agent-skill", "SKILL.md"), + `--- +name: agent-skill +description: A skill in the .agents/skills directory. +--- + +# Agent Skill +`, + ), + ]), + ) + + const skill = yield* Skill.Service + const list = discovered(yield* skill.all()) // kilocode_change + expect(list.map((s) => s.name)).toEqual(["agent-skill"]) + }), + { git: true }, + ), + ) + it.live("properly resolves directories that skills live in", () => provideTmpdirInstance( (dir) => diff --git a/packages/opencode/test/snapshot/snapshot.test.ts b/packages/opencode/test/snapshot/snapshot.test.ts index 99ddfe72d45..de60d58b2df 100644 --- a/packages/opencode/test/snapshot/snapshot.test.ts +++ b/packages/opencode/test/snapshot/snapshot.test.ts @@ -1,13 +1,15 @@ -import { afterEach, test, expect } from "bun:test" +import { afterEach, expect } from "bun:test" import { $ } from "bun" +import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" +import { AppFileSystem } from "@opencode-ai/core/filesystem" import fs from "fs/promises" import path from "path" -import { Effect } from "effect" +import { Effect, Fiber, Layer } from "effect" import { Snapshot } from "../../src/snapshot" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" -import { Filesystem } from "@/util/filesystem" -import { disposeAllInstances, provideInstance, tmpdir } from "../fixture/fixture" +import { disposeAllInstances, provideInstance, TestInstance, tmpdirScoped } from "../fixture/fixture" +import { testEffect } from "../lib/effect" + +const it = testEffect(Layer.mergeAll(Snapshot.defaultLayer, AppFileSystem.defaultLayer)) // Git always outputs /-separated paths internally. Snapshot.patch() joins them // with path.join (which produces \ on Windows) then normalizes back to /. @@ -18,1515 +20,1095 @@ afterEach(async () => { await disposeAllInstances() }) -async function bootstrap() { - return tmpdir({ - git: true, - init: async (dir) => { - const unique = Math.random().toString(36).slice(2) - const aContent = `A${unique}` - const bContent = `B${unique}` - await Filesystem.write(`${dir}/a.txt`, aContent) - await Filesystem.write(`${dir}/b.txt`, bContent) - await $`git add .`.cwd(dir).quiet() - await $`git commit -m init`.cwd(dir).quiet() - return { - aContent, - bContent, - } - }, +const exec = (cwd: string, command: string[]) => + Effect.promise(async () => { + const proc = Bun.spawn(command, { cwd, stdout: "ignore", stderr: "pipe" }) + const code = await proc.exited + if (code !== 0) throw new Error(`${command.join(" ")} failed: ${await new Response(proc.stderr).text()}`) }) -} - -function run(dir: string, body: (snapshot: Snapshot.Interface) => Effect.Effect) { - return Effect.runPromise( - Effect.gen(function* () { - const snapshot = yield* Snapshot.Service - return yield* body(snapshot) - }).pipe(provideInstance(dir), Effect.provide(Snapshot.defaultLayer)), - ) -} - -test("tracks deleted files correctly", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await $`rm ${tmp.path}/a.txt`.quiet() - expect((await run(tmp.path, (snapshot) => snapshot.patch(before!))).files).toContain(fwd(tmp.path, "a.txt")) - }, - }) +const write = (file: string, content: string | Uint8Array) => + AppFileSystem.Service.use((fs) => fs.writeWithDirs(file, content)) +const readText = (file: string) => AppFileSystem.Service.use((fs) => fs.readFileString(file)) +const exists = (file: string) => AppFileSystem.Service.use((fs) => fs.existsSafe(file)) +const mkdirp = (dir: string) => AppFileSystem.Service.use((fs) => fs.ensureDir(dir)) +const rm = (file: string) => + AppFileSystem.Service.use((fs) => fs.remove(file, { recursive: true, force: true }).pipe(Effect.ignore)) + +const initialize = Effect.fn("SnapshotTest.initialize")(function* (dir: string) { + const unique = Math.random().toString(36).slice(2) + const aContent = `A${unique}` + const bContent = `B${unique}` + yield* write(`${dir}/a.txt`, aContent) + yield* write(`${dir}/b.txt`, bContent) + yield* exec(dir, ["git", "add", "."]) + yield* exec(dir, ["git", "commit", "-m", "init"]) + return { aContent, bContent } }) -test("revert should remove new files", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await Filesystem.write(`${tmp.path}/new.txt`, "NEW") +type Bootstrapped = { path: string; extra: { aContent: string; bContent: string } } - const patch = await run(tmp.path, (snapshot) => snapshot.patch(before!)) - - await run(tmp.path, (snapshot) => snapshot.revert([patch])) - - expect( - await fs - .access(`${tmp.path}/new.txt`) - .then(() => true) - .catch(() => false), - ).toBe(false) - }, - }) +const bootstrap = Effect.fn("SnapshotTest.bootstrap")(function* () { + const tmp = yield* TestInstance + return { path: tmp.directory, extra: yield* initialize(tmp.directory) } }) -test("revert in subdirectory", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await $`mkdir -p ${tmp.path}/sub`.quiet() - await Filesystem.write(`${tmp.path}/sub/file.txt`, "SUB") - - const patch = await run(tmp.path, (snapshot) => snapshot.patch(before!)) - - await run(tmp.path, (snapshot) => snapshot.revert([patch])) - - expect( - await fs - .access(`${tmp.path}/sub/file.txt`) - .then(() => true) - .catch(() => false), - ).toBe(false) - // Note: revert currently only removes files, not directories - // The empty subdirectory will remain - }, +const withTrackedSnapshot = ( + fn: (input: { tmp: Bootstrapped; snapshot: Snapshot.Interface; before: string }) => Effect.Effect, +) => + Effect.gen(function* () { + const tmp = yield* bootstrap() + const snapshot = yield* Snapshot.Service + const before = yield* snapshot.track() + expect(before).toBeTruthy() + return yield* fn({ tmp, snapshot, before: before! }) }) -}) -test("multiple file operations", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await $`rm ${tmp.path}/a.txt`.quiet() - await Filesystem.write(`${tmp.path}/c.txt`, "C") - await $`mkdir -p ${tmp.path}/dir`.quiet() - await Filesystem.write(`${tmp.path}/dir/d.txt`, "D") - await Filesystem.write(`${tmp.path}/b.txt`, "MODIFIED") - - const patch = await run(tmp.path, (snapshot) => snapshot.patch(before!)) - - await run(tmp.path, (snapshot) => snapshot.revert([patch])) - - expect(await fs.readFile(`${tmp.path}/a.txt`, "utf-8")).toBe(tmp.extra.aContent) - expect( - await fs - .access(`${tmp.path}/c.txt`) - .then(() => true) - .catch(() => false), - ).toBe(false) - // Note: revert currently only removes files, not directories - // The empty directory will remain - expect(await fs.readFile(`${tmp.path}/b.txt`, "utf-8")).toBe(tmp.extra.bContent) - }, - }) +const bootstrapScoped = Effect.fn("SnapshotTest.bootstrapScoped")(function* () { + const dir = yield* tmpdirScoped({ git: true }).pipe(Effect.provide(CrossSpawnSpawner.defaultLayer)) + return { path: dir, extra: yield* initialize(dir) } }) -test("empty directory handling", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await $`mkdir ${tmp.path}/empty`.quiet() +const scopedGitTmpdir = () => tmpdirScoped({ git: true }).pipe(Effect.provide(CrossSpawnSpawner.defaultLayer)) - expect((await run(tmp.path, (snapshot) => snapshot.patch(before!))).files.length).toBe(0) - }, +const cleanupWorktree = (repo: string, worktree: string, files: string[] = []) => + Effect.promise(async () => { + await $`git worktree remove --force ${worktree}`.cwd(repo).quiet().nothrow() + await fs.rm(worktree, { recursive: true, force: true }).catch(() => undefined) + await Promise.all(files.map((file) => fs.rm(file, { recursive: true, force: true }).catch(() => undefined))) }) -}) - -test("binary file handling", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - await Filesystem.write(`${tmp.path}/image.png`, new Uint8Array([0x89, 0x50, 0x4e, 0x47])) +const withGitConfigGlobal = (config: string, self: Effect.Effect) => + Effect.acquireUseRelease( + Effect.sync(() => { + const previous = process.env.GIT_CONFIG_GLOBAL + process.env.GIT_CONFIG_GLOBAL = config + return previous + }), + () => self, + (previous) => + Effect.sync(() => { + if (previous) process.env.GIT_CONFIG_GLOBAL = previous + else delete process.env.GIT_CONFIG_GLOBAL + }), + ) - const patch = await run(tmp.path, (snapshot) => snapshot.patch(before!)) +it.instance( + "tracks deleted files correctly", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* rm(`${tmp.path}/a.txt`) + expect((yield* snapshot.patch(before)).files).toContain(fwd(tmp.path, "a.txt")) + }), + ), + { git: true }, +) + +it.instance( + "revert should remove new files", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* write(`${tmp.path}/new.txt`, "NEW") + const patch = yield* snapshot.patch(before) + yield* snapshot.revert([patch]) + expect(yield* exists(`${tmp.path}/new.txt`)).toBe(false) + }), + ), + { git: true }, +) + +it.instance( + "revert in subdirectory", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* mkdirp(`${tmp.path}/sub`) + yield* write(`${tmp.path}/sub/file.txt`, "SUB") + const patch = yield* snapshot.patch(before) + yield* snapshot.revert([patch]) + expect(yield* exists(`${tmp.path}/sub/file.txt`)).toBe(false) + }), + ), + { git: true }, +) + +it.instance( + "multiple file operations", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* rm(`${tmp.path}/a.txt`) + yield* write(`${tmp.path}/c.txt`, "C") + yield* mkdirp(`${tmp.path}/dir`) + yield* write(`${tmp.path}/dir/d.txt`, "D") + yield* write(`${tmp.path}/b.txt`, "MODIFIED") + const patch = yield* snapshot.patch(before) + yield* snapshot.revert([patch]) + expect(yield* readText(`${tmp.path}/a.txt`)).toBe(tmp.extra.aContent) + expect(yield* exists(`${tmp.path}/c.txt`)).toBe(false) + expect(yield* readText(`${tmp.path}/b.txt`)).toBe(tmp.extra.bContent) + }), + ), + { git: true }, +) + +it.instance( + "empty directory handling", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* mkdirp(`${tmp.path}/empty`) + expect((yield* snapshot.patch(before)).files.length).toBe(0) + }), + ), + { git: true }, +) + +it.instance( + "binary file handling", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* write(`${tmp.path}/image.png`, new Uint8Array([0x89, 0x50, 0x4e, 0x47])) + const patch = yield* snapshot.patch(before) expect(patch.files).toContain(fwd(tmp.path, "image.png")) - - await run(tmp.path, (snapshot) => snapshot.revert([patch])) - expect( - await fs - .access(`${tmp.path}/image.png`) - .then(() => true) - .catch(() => false), - ).toBe(false) - }, - }) -}) - -test("symlink handling", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await fs.symlink(`${tmp.path}/a.txt`, `${tmp.path}/link.txt`, "file") - - expect((await run(tmp.path, (snapshot) => snapshot.patch(before!))).files).toContain(fwd(tmp.path, "link.txt")) - }, - }) -}) - -test("file under size limit handling", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await Filesystem.write(`${tmp.path}/large.txt`, "x".repeat(1024 * 1024)) - - expect((await run(tmp.path, (snapshot) => snapshot.patch(before!))).files).toContain(fwd(tmp.path, "large.txt")) - }, - }) -}) - -test("large added files are skipped", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await Filesystem.write(`${tmp.path}/huge.txt`, new Uint8Array(2 * 1024 * 1024 + 1)) - - expect((await run(tmp.path, (snapshot) => snapshot.patch(before!))).files).toEqual([]) - expect(await run(tmp.path, (snapshot) => snapshot.diff(before!))).toBe("") - expect(await run(tmp.path, (snapshot) => snapshot.track())).toBe(before) - }, - }) -}) - -test("nested directory revert", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await $`mkdir -p ${tmp.path}/level1/level2/level3`.quiet() - await Filesystem.write(`${tmp.path}/level1/level2/level3/deep.txt`, "DEEP") - - const patch = await run(tmp.path, (snapshot) => snapshot.patch(before!)) - - await run(tmp.path, (snapshot) => snapshot.revert([patch])) - - expect( - await fs - .access(`${tmp.path}/level1/level2/level3/deep.txt`) - .then(() => true) - .catch(() => false), - ).toBe(false) - }, - }) -}) - -test("special characters in filenames", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await Filesystem.write(`${tmp.path}/file with spaces.txt`, "SPACES") - await Filesystem.write(`${tmp.path}/file-with-dashes.txt`, "DASHES") - await Filesystem.write(`${tmp.path}/file_with_underscores.txt`, "UNDERSCORES") - - const files = (await run(tmp.path, (snapshot) => snapshot.patch(before!))).files + yield* snapshot.revert([patch]) + expect(yield* exists(`${tmp.path}/image.png`)).toBe(false) + }), + ), + { git: true }, +) + +it.instance( + "symlink handling", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* Effect.promise(() => fs.symlink(`${tmp.path}/a.txt`, `${tmp.path}/link.txt`, "file")) + expect((yield* snapshot.patch(before)).files).toContain(fwd(tmp.path, "link.txt")) + }), + ), + { git: true }, +) + +it.instance( + "file under size limit handling", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* write(`${tmp.path}/large.txt`, "x".repeat(1024 * 1024)) + expect((yield* snapshot.patch(before)).files).toContain(fwd(tmp.path, "large.txt")) + }), + ), + { git: true }, +) + +it.instance( + "large added files are skipped", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* write(`${tmp.path}/huge.txt`, new Uint8Array(2 * 1024 * 1024 + 1)) + expect((yield* snapshot.patch(before)).files).toEqual([]) + expect(yield* snapshot.diff(before)).toBe("") + expect(yield* snapshot.track()).toBe(before) + }), + ), + { git: true }, +) + +it.instance( + "nested directory revert", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* mkdirp(`${tmp.path}/level1/level2/level3`) + yield* write(`${tmp.path}/level1/level2/level3/deep.txt`, "DEEP") + const patch = yield* snapshot.patch(before) + yield* snapshot.revert([patch]) + expect(yield* exists(`${tmp.path}/level1/level2/level3/deep.txt`)).toBe(false) + }), + ), + { git: true }, +) + +it.instance( + "special characters in filenames", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* write(`${tmp.path}/file with spaces.txt`, "SPACES") + yield* write(`${tmp.path}/file-with-dashes.txt`, "DASHES") + yield* write(`${tmp.path}/file_with_underscores.txt`, "UNDERSCORES") + const files = (yield* snapshot.patch(before)).files expect(files).toContain(fwd(tmp.path, "file with spaces.txt")) expect(files).toContain(fwd(tmp.path, "file-with-dashes.txt")) expect(files).toContain(fwd(tmp.path, "file_with_underscores.txt")) - }, - }) -}) - -test("revert with empty patches", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // Should not crash with empty patches - expect(run(tmp.path, (snapshot) => snapshot.revert([]))).resolves.toBeUndefined() - - // Should not crash with patches that have empty file lists - expect(run(tmp.path, (snapshot) => snapshot.revert([{ hash: "dummy", files: [] }]))).resolves.toBeUndefined() - }, - }) -}) - -test("patch with invalid hash", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - // Create a change - await Filesystem.write(`${tmp.path}/test.txt`, "TEST") - - // Try to patch with invalid hash - should handle gracefully - const patch = await run(tmp.path, (snapshot) => snapshot.patch("invalid-hash-12345")) + }), + ), + { git: true }, +) + +it.instance( + "revert with empty patches", + Effect.gen(function* () { + yield* bootstrap() + const snapshot = yield* Snapshot.Service + yield* snapshot.revert([]) + yield* snapshot.revert([{ hash: "dummy", files: [] }]) + }), + { git: true }, +) + +it.instance( + "patch with invalid hash", + withTrackedSnapshot(({ tmp, snapshot }) => + Effect.gen(function* () { + yield* write(`${tmp.path}/test.txt`, "TEST") + const patch = yield* snapshot.patch("invalid-hash-12345") expect(patch.files).toEqual([]) expect(patch.hash).toBe("invalid-hash-12345") - }, - }) -}) - -test("revert non-existent file", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - // Try to revert a file that doesn't exist in the snapshot - // This should not crash - expect( - run(tmp.path, (snapshot) => - snapshot.revert([ - { - hash: before!, - files: [`${tmp.path}/nonexistent.txt`], - }, - ]), - ), - ).resolves.toBeUndefined() - }, - }) -}) - -test("unicode filenames", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - + }), + ), + { git: true }, +) + +it.instance( + "revert non-existent file", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* snapshot.revert([{ hash: before, files: [`${tmp.path}/nonexistent.txt`] }]) + }), + ), + { git: true }, +) + +it.instance( + "unicode filenames", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { const unicodeFiles = [ { path: fwd(tmp.path, "文件.txt"), content: "chinese content" }, { path: fwd(tmp.path, "🚀rocket.txt"), content: "emoji content" }, { path: fwd(tmp.path, "café.txt"), content: "accented content" }, { path: fwd(tmp.path, "файл.txt"), content: "cyrillic content" }, ] - - for (const file of unicodeFiles) { - await Filesystem.write(file.path, file.content) - } - - const patch = await run(tmp.path, (snapshot) => snapshot.patch(before!)) + yield* Effect.all( + unicodeFiles.map((file) => write(file.path, file.content)), + { concurrency: "unbounded" }, + ) + const patch = yield* snapshot.patch(before) expect(patch.files.length).toBe(4) - - for (const file of unicodeFiles) { - expect(patch.files).toContain(file.path) - } - - await run(tmp.path, (snapshot) => snapshot.revert([patch])) - - for (const file of unicodeFiles) { - expect( - await fs - .access(file.path) - .then(() => true) - .catch(() => false), - ).toBe(false) - } - }, - }) -}) - -test.skip("unicode filenames modification and restore", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const chineseFile = fwd(tmp.path, "文件.txt") - const cyrillicFile = fwd(tmp.path, "файл.txt") - - await Filesystem.write(chineseFile, "original chinese") - await Filesystem.write(cyrillicFile, "original cyrillic") - - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await Filesystem.write(chineseFile, "modified chinese") - await Filesystem.write(cyrillicFile, "modified cyrillic") - - const patch = await run(tmp.path, (snapshot) => snapshot.patch(before!)) - expect(patch.files).toContain(chineseFile) - expect(patch.files).toContain(cyrillicFile) - - await run(tmp.path, (snapshot) => snapshot.revert([patch])) - - expect(await fs.readFile(chineseFile, "utf-8")).toBe("original chinese") - expect(await fs.readFile(cyrillicFile, "utf-8")).toBe("original cyrillic") - }, - }) -}) - -test("unicode filenames in subdirectories", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await $`mkdir -p "${tmp.path}/目录/подкаталог"`.quiet() + for (const file of unicodeFiles) expect(patch.files).toContain(file.path) + yield* snapshot.revert([patch]) + for (const file of unicodeFiles) expect(yield* exists(file.path)).toBe(false) + }), + ), + { git: true }, +) + +it.instance.skip( + "unicode filenames modification and restore", + Effect.gen(function* () { + const tmp = yield* bootstrap() + const snapshot = yield* Snapshot.Service + const chineseFile = fwd(tmp.path, "文件.txt") + const cyrillicFile = fwd(tmp.path, "файл.txt") + yield* write(chineseFile, "original chinese") + yield* write(cyrillicFile, "original cyrillic") + const before = yield* snapshot.track() + expect(before).toBeTruthy() + yield* write(chineseFile, "modified chinese") + yield* write(cyrillicFile, "modified cyrillic") + const patch = yield* snapshot.patch(before!) + expect(patch.files).toContain(chineseFile) + expect(patch.files).toContain(cyrillicFile) + yield* snapshot.revert([patch]) + expect(yield* readText(chineseFile)).toBe("original chinese") + expect(yield* readText(cyrillicFile)).toBe("original cyrillic") + }), + { git: true }, +) + +it.instance( + "unicode filenames in subdirectories", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* mkdirp(`${tmp.path}/目录/подкаталог`) const deepFile = fwd(tmp.path, "目录", "подкаталог", "文件.txt") - await Filesystem.write(deepFile, "deep unicode content") - - const patch = await run(tmp.path, (snapshot) => snapshot.patch(before!)) + yield* write(deepFile, "deep unicode content") + const patch = yield* snapshot.patch(before) expect(patch.files).toContain(deepFile) - - await run(tmp.path, (snapshot) => snapshot.revert([patch])) - expect( - await fs - .access(deepFile) - .then(() => true) - .catch(() => false), - ).toBe(false) - }, - }) -}) - -test("very long filenames", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - const longName = "a".repeat(200) + ".txt" - const longFile = fwd(tmp.path, longName) - - await Filesystem.write(longFile, "long filename content") - - const patch = await run(tmp.path, (snapshot) => snapshot.patch(before!)) + yield* snapshot.revert([patch]) + expect(yield* exists(deepFile)).toBe(false) + }), + ), + { git: true }, +) + +it.instance( + "very long filenames", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + const longFile = fwd(tmp.path, `${"a".repeat(200)}.txt`) + yield* write(longFile, "long filename content") + const patch = yield* snapshot.patch(before) expect(patch.files).toContain(longFile) - - await run(tmp.path, (snapshot) => snapshot.revert([patch])) - expect( - await fs - .access(longFile) - .then(() => true) - .catch(() => false), - ).toBe(false) - }, - }) -}) - -test("hidden files", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await Filesystem.write(`${tmp.path}/.hidden`, "hidden content") - await Filesystem.write(`${tmp.path}/.gitignore`, "*.log") - await Filesystem.write(`${tmp.path}/.config`, "config content") - - const patch = await run(tmp.path, (snapshot) => snapshot.patch(before!)) + yield* snapshot.revert([patch]) + expect(yield* exists(longFile)).toBe(false) + }), + ), + { git: true }, +) + +it.instance( + "hidden files", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* write(`${tmp.path}/.hidden`, "hidden content") + yield* write(`${tmp.path}/.gitignore`, "*.log") + yield* write(`${tmp.path}/.config`, "config content") + const patch = yield* snapshot.patch(before) expect(patch.files).toContain(fwd(tmp.path, ".hidden")) expect(patch.files).toContain(fwd(tmp.path, ".gitignore")) expect(patch.files).toContain(fwd(tmp.path, ".config")) - }, - }) -}) - -test("nested symlinks", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await $`mkdir -p ${tmp.path}/sub/dir`.quiet() - await Filesystem.write(`${tmp.path}/sub/dir/target.txt`, "target content") - await fs.symlink(`${tmp.path}/sub/dir/target.txt`, `${tmp.path}/sub/dir/link.txt`, "file") - await fs.symlink(`${tmp.path}/sub`, `${tmp.path}/sub-link`, "dir") - - const patch = await run(tmp.path, (snapshot) => snapshot.patch(before!)) + }), + ), + { git: true }, +) + +it.instance( + "nested symlinks", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* mkdirp(`${tmp.path}/sub/dir`) + yield* write(`${tmp.path}/sub/dir/target.txt`, "target content") + yield* Effect.promise(() => fs.symlink(`${tmp.path}/sub/dir/target.txt`, `${tmp.path}/sub/dir/link.txt`, "file")) + yield* Effect.promise(() => fs.symlink(`${tmp.path}/sub`, `${tmp.path}/sub-link`, "dir")) + const patch = yield* snapshot.patch(before) expect(patch.files).toContain(fwd(tmp.path, "sub", "dir", "link.txt")) expect(patch.files).toContain(fwd(tmp.path, "sub-link")) - }, - }) -}) - -test("file permissions and ownership changes", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - // Change permissions multiple times - await $`chmod 600 ${tmp.path}/a.txt`.quiet() - await $`chmod 755 ${tmp.path}/a.txt`.quiet() - await $`chmod 644 ${tmp.path}/a.txt`.quiet() - - const patch = await run(tmp.path, (snapshot) => snapshot.patch(before!)) - // Note: git doesn't track permission changes on existing files by default - // Only tracks executable bit when files are first added - expect(patch.files.length).toBe(0) - }, - }) -}) - -test("circular symlinks", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - // Create circular symlink - await fs.symlink(`${tmp.path}/circular`, `${tmp.path}/circular`, "dir").catch(() => {}) - - const patch = await run(tmp.path, (snapshot) => snapshot.patch(before!)) - expect(patch.files.length).toBeGreaterThanOrEqual(0) // Should not crash - }, - }) -}) - -test("source project gitignore is respected - ignored files are not snapshotted", async () => { - await using tmp = await tmpdir({ - git: true, - init: async (dir) => { - // Create gitignore BEFORE any tracking - await Filesystem.write(`${dir}/.gitignore`, "*.ignored\nbuild/\nnode_modules/\n") - await Filesystem.write(`${dir}/tracked.txt`, "tracked content") - await Filesystem.write(`${dir}/ignored.ignored`, "ignored content") - await $`mkdir -p ${dir}/build`.quiet() - await Filesystem.write(`${dir}/build/output.js`, "build output") - await Filesystem.write(`${dir}/normal.js`, "normal js") - await $`git add .`.cwd(dir).quiet() - await $`git commit -m init`.cwd(dir).quiet() - }, - }) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - // Modify tracked files and create new ones - some ignored, some not - await Filesystem.write(`${tmp.path}/tracked.txt`, "modified tracked") - await Filesystem.write(`${tmp.path}/new.ignored`, "new ignored") - await Filesystem.write(`${tmp.path}/new-tracked.txt`, "new tracked") - await Filesystem.write(`${tmp.path}/build/new-build.js`, "new build file") - - const patch = await run(tmp.path, (snapshot) => snapshot.patch(before!)) - - // Modified and new tracked files should be in snapshot - expect(patch.files).toContain(fwd(tmp.path, "new-tracked.txt")) - expect(patch.files).toContain(fwd(tmp.path, "tracked.txt")) - - // Ignored files should NOT be in snapshot - expect(patch.files).not.toContain(fwd(tmp.path, "new.ignored")) - expect(patch.files).not.toContain(fwd(tmp.path, "ignored.ignored")) - expect(patch.files).not.toContain(fwd(tmp.path, "build/output.js")) - expect(patch.files).not.toContain(fwd(tmp.path, "build/new-build.js")) - }, - }) -}) - -test("gitignore changes", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) + }), + ), + { git: true }, +) + +it.instance( + "file permissions and ownership changes", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* Effect.promise(() => fs.chmod(`${tmp.path}/a.txt`, 0o600)) + yield* Effect.promise(() => fs.chmod(`${tmp.path}/a.txt`, 0o755)) + yield* Effect.promise(() => fs.chmod(`${tmp.path}/a.txt`, 0o644)) + expect((yield* snapshot.patch(before)).files.length).toBe(0) + }), + ), + { git: true }, +) + +it.instance( + "circular symlinks", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* Effect.promise(() => + fs.symlink(`${tmp.path}/circular`, `${tmp.path}/circular`, "dir").catch(() => undefined), + ) + expect((yield* snapshot.patch(before)).files.length).toBeGreaterThanOrEqual(0) + }), + ), + { git: true }, +) + +it.live( + "source project gitignore is respected - ignored files are not snapshotted", + Effect.gen(function* () { + const dir = yield* scopedGitTmpdir() + yield* write(`${dir}/.gitignore`, "*.ignored\nbuild/\nnode_modules/\n") + yield* write(`${dir}/tracked.txt`, "tracked content") + yield* write(`${dir}/ignored.ignored`, "ignored content") + yield* mkdirp(`${dir}/build`) + yield* write(`${dir}/build/output.js`, "build output") + yield* write(`${dir}/normal.js`, "normal js") + yield* exec(dir, ["git", "add", "."]) + yield* exec(dir, ["git", "commit", "-m", "init"]) + yield* Effect.gen(function* () { + const snapshot = yield* Snapshot.Service + const before = yield* snapshot.track() expect(before).toBeTruthy() - - await Filesystem.write(`${tmp.path}/.gitignore`, "*.ignored") - await Filesystem.write(`${tmp.path}/test.ignored`, "ignored content") - await Filesystem.write(`${tmp.path}/normal.txt`, "normal content") - - const patch = await run(tmp.path, (snapshot) => snapshot.patch(before!)) - - // Should track gitignore itself + yield* write(`${dir}/tracked.txt`, "modified tracked") + yield* write(`${dir}/new.ignored`, "new ignored") + yield* write(`${dir}/new-tracked.txt`, "new tracked") + yield* write(`${dir}/build/new-build.js`, "new build file") + const patch = yield* snapshot.patch(before!) + expect(patch.files).toContain(fwd(dir, "new-tracked.txt")) + expect(patch.files).toContain(fwd(dir, "tracked.txt")) + expect(patch.files).not.toContain(fwd(dir, "new.ignored")) + expect(patch.files).not.toContain(fwd(dir, "ignored.ignored")) + expect(patch.files).not.toContain(fwd(dir, "build/output.js")) + expect(patch.files).not.toContain(fwd(dir, "build/new-build.js")) + }).pipe(provideInstance(dir)) + }), +) + +it.instance( + "gitignore changes", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* write(`${tmp.path}/.gitignore`, "*.ignored") + yield* write(`${tmp.path}/test.ignored`, "ignored content") + yield* write(`${tmp.path}/normal.txt`, "normal content") + const patch = yield* snapshot.patch(before) expect(patch.files).toContain(fwd(tmp.path, ".gitignore")) - // Should track normal files expect(patch.files).toContain(fwd(tmp.path, "normal.txt")) - // Should not track ignored files (git won't see them) expect(patch.files).not.toContain(fwd(tmp.path, "test.ignored")) - }, - }) -}) - -test("files tracked in snapshot but now gitignored are filtered out", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // First, create a file and snapshot it - await Filesystem.write(`${tmp.path}/later-ignored.txt`, "initial content") - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - // Modify the file (so it appears in diff-files) - await Filesystem.write(`${tmp.path}/later-ignored.txt`, "modified content") - - // Now add gitignore that would exclude this file - await Filesystem.write(`${tmp.path}/.gitignore`, "later-ignored.txt\n") - - // Also create another tracked file - await Filesystem.write(`${tmp.path}/still-tracked.txt`, "new tracked file") - - const patch = await run(tmp.path, (snapshot) => snapshot.patch(before!)) - - // The file that is now gitignored should NOT appear, even though it was - // previously tracked and modified - expect(patch.files).not.toContain(fwd(tmp.path, "later-ignored.txt")) - - // The gitignore file itself should appear - expect(patch.files).toContain(fwd(tmp.path, ".gitignore")) - - // Other tracked files should appear - expect(patch.files).toContain(fwd(tmp.path, "still-tracked.txt")) - }, - }) -}) - -test("gitignore updated between track calls filters from diff", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // a.txt is already committed from bootstrap - track it in snapshot - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - // Modify a.txt (so it appears in diff-files) - await Filesystem.write(`${tmp.path}/a.txt`, "modified content") - - // Now add gitignore that would exclude a.txt - await Filesystem.write(`${tmp.path}/.gitignore`, "a.txt\n") - - // Also modify b.txt which is not gitignored - await Filesystem.write(`${tmp.path}/b.txt`, "also modified") - - // Second track - should not include a.txt even though it changed - const after = await run(tmp.path, (snapshot) => snapshot.track()) + }), + ), + { git: true }, +) + +it.instance( + "files tracked in snapshot but now gitignored are filtered out", + Effect.gen(function* () { + const tmp = yield* bootstrap() + const snapshot = yield* Snapshot.Service + yield* write(`${tmp.path}/later-ignored.txt`, "initial content") + const before = yield* snapshot.track() + expect(before).toBeTruthy() + yield* write(`${tmp.path}/later-ignored.txt`, "modified content") + yield* write(`${tmp.path}/.gitignore`, "later-ignored.txt\n") + yield* write(`${tmp.path}/still-tracked.txt`, "new tracked file") + const patch = yield* snapshot.patch(before!) + expect(patch.files).not.toContain(fwd(tmp.path, "later-ignored.txt")) + expect(patch.files).toContain(fwd(tmp.path, ".gitignore")) + expect(patch.files).toContain(fwd(tmp.path, "still-tracked.txt")) + }), + { git: true }, +) + +it.instance( + "gitignore updated between track calls filters from diff", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* write(`${tmp.path}/a.txt`, "modified content") + yield* write(`${tmp.path}/.gitignore`, "a.txt\n") + yield* write(`${tmp.path}/b.txt`, "also modified") + const after = yield* snapshot.track() expect(after).toBeTruthy() - - // Verify a.txt is NOT in the diff between snapshots - const diffs = await run(tmp.path, (snapshot) => snapshot.diffFull(before!, after!)) + const diffs = yield* snapshot.diffFull(before, after!) expect(diffs.some((x) => x.file === "a.txt")).toBe(false) - - // But .gitignore should be in the diff expect(diffs.some((x) => x.file === ".gitignore")).toBe(true) - - // b.txt should be in the diff (not gitignored) expect(diffs.some((x) => x.file === "b.txt")).toBe(true) - }, - }) -}) - -test("git info exclude changes", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - + }), + ), + { git: true }, +) + +it.instance( + "git info exclude changes", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { const file = `${tmp.path}/.git/info/exclude` - const text = await Bun.file(file).text() - await Bun.write(file, `${text.trimEnd()}\nignored.txt\n`) - await Bun.write(`${tmp.path}/ignored.txt`, "ignored content") - await Bun.write(`${tmp.path}/normal.txt`, "normal content") - - const patch = await run(tmp.path, (snapshot) => snapshot.patch(before!)) + yield* write(file, `${(yield* Effect.promise(() => Bun.file(file).text())).trimEnd()}\nignored.txt\n`) + yield* write(`${tmp.path}/ignored.txt`, "ignored content") + yield* write(`${tmp.path}/normal.txt`, "normal content") + const patch = yield* snapshot.patch(before) expect(patch.files).toContain(fwd(tmp.path, "normal.txt")) expect(patch.files).not.toContain(fwd(tmp.path, "ignored.txt")) - - const after = await run(tmp.path, (snapshot) => snapshot.track()) - const diffs = await run(tmp.path, (snapshot) => snapshot.diffFull(before!, after!)) + const after = yield* snapshot.track() + const diffs = yield* snapshot.diffFull(before, after!) expect(diffs.some((x) => x.file === "normal.txt")).toBe(true) expect(diffs.some((x) => x.file === "ignored.txt")).toBe(false) - }, - }) -}) - -test("git info exclude keeps global excludes", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const global = `${tmp.path}/global.ignore` - const config = `${tmp.path}/global.gitconfig` - await Bun.write(global, "global.tmp\n") - await Bun.write(config, `[core]\n\texcludesFile = ${global.replaceAll("\\", "/")}\n`) - - const prev = process.env.GIT_CONFIG_GLOBAL - process.env.GIT_CONFIG_GLOBAL = config - try { - const before = await run(tmp.path, (snapshot) => snapshot.track()) + }), + ), + { git: true }, +) + +it.instance( + "git info exclude keeps global excludes", + Effect.gen(function* () { + const tmp = yield* bootstrap() + const global = `${tmp.path}/global.ignore` + const config = `${tmp.path}/global.gitconfig` + yield* write(global, "global.tmp\n") + yield* write(config, `[core]\n\texcludesFile = ${global.replaceAll("\\", "/")}\n`) + yield* withGitConfigGlobal( + config, + Effect.gen(function* () { + const snapshot = yield* Snapshot.Service + const before = yield* snapshot.track() expect(before).toBeTruthy() - const file = `${tmp.path}/.git/info/exclude` - const text = await Bun.file(file).text() - await Bun.write(file, `${text.trimEnd()}\ninfo.tmp\n`) - - await Bun.write(`${tmp.path}/global.tmp`, "global content") - await Bun.write(`${tmp.path}/info.tmp`, "info content") - await Bun.write(`${tmp.path}/normal.txt`, "normal content") - - const patch = await run(tmp.path, (snapshot) => snapshot.patch(before!)) + yield* write(file, `${(yield* Effect.promise(() => Bun.file(file).text())).trimEnd()}\ninfo.tmp\n`) + yield* write(`${tmp.path}/global.tmp`, "global content") + yield* write(`${tmp.path}/info.tmp`, "info content") + yield* write(`${tmp.path}/normal.txt`, "normal content") + const patch = yield* snapshot.patch(before!) expect(patch.files).toContain(fwd(tmp.path, "normal.txt")) expect(patch.files).not.toContain(fwd(tmp.path, "global.tmp")) expect(patch.files).not.toContain(fwd(tmp.path, "info.tmp")) - } finally { - if (prev) process.env.GIT_CONFIG_GLOBAL = prev - else delete process.env.GIT_CONFIG_GLOBAL - } - }, - }) -}) - -test("concurrent file operations during patch", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - // Start creating files - const createPromise = (async () => { + }), + ) + }), + { git: true }, +) + +it.instance( + "concurrent file operations during patch", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + const fiber = yield* Effect.gen(function* () { for (let i = 0; i < 10; i++) { - await Filesystem.write(`${tmp.path}/concurrent${i}.txt`, `concurrent${i}`) - // Small delay to simulate concurrent operations - await new Promise((resolve) => setTimeout(resolve, 1)) + yield* write(`${tmp.path}/concurrent${i}.txt`, `concurrent${i}`) + yield* Effect.sleep("1 millis") } - })() - - // Get patch while files are being created - const patchPromise = run(tmp.path, (snapshot) => snapshot.patch(before!)) - - await createPromise - const patch = await patchPromise - - // Should capture some or all of the concurrent files + }).pipe(Effect.forkScoped) + const patch = yield* snapshot.patch(before) + yield* Fiber.join(fiber) expect(patch.files.length).toBeGreaterThanOrEqual(0) - }, - }) -}) - -test("snapshot state isolation between projects", async () => { - // Test that different projects don't interfere with each other - await using tmp1 = await bootstrap() - await using tmp2 = await bootstrap() - - await WithInstance.provide({ - directory: tmp1.path, - fn: async () => { - const before1 = await run(tmp1.path, (snapshot) => snapshot.track()) - await Filesystem.write(`${tmp1.path}/project1.txt`, "project1 content") - const patch1 = await run(tmp1.path, (snapshot) => snapshot.patch(before1!)) + }), + ), + { git: true }, +) + +it.live( + "snapshot state isolation between projects", + Effect.gen(function* () { + const tmp1 = yield* bootstrapScoped() + const tmp2 = yield* bootstrapScoped() + yield* Effect.gen(function* () { + const snapshot = yield* Snapshot.Service + const before1 = yield* snapshot.track() + yield* write(`${tmp1.path}/project1.txt`, "project1 content") + const patch1 = yield* snapshot.patch(before1!) expect(patch1.files).toContain(fwd(tmp1.path, "project1.txt")) - }, - }) - - await WithInstance.provide({ - directory: tmp2.path, - fn: async () => { - const before2 = await run(tmp2.path, (snapshot) => snapshot.track()) - await Filesystem.write(`${tmp2.path}/project2.txt`, "project2 content") - const patch2 = await run(tmp2.path, (snapshot) => snapshot.patch(before2!)) + }).pipe(provideInstance(tmp1.path)) + yield* Effect.gen(function* () { + const snapshot = yield* Snapshot.Service + const before2 = yield* snapshot.track() + yield* write(`${tmp2.path}/project2.txt`, "project2 content") + const patch2 = yield* snapshot.patch(before2!) expect(patch2.files).toContain(fwd(tmp2.path, "project2.txt")) - - // Ensure project1 files don't appear in project2 - expect(patch2.files).not.toContain(fwd(tmp1?.path ?? "", "project1.txt")) - }, - }) -}) - -test("patch detects changes in secondary worktree", async () => { - await using tmp = await bootstrap() - const worktreePath = `${tmp.path}-worktree` - await $`git worktree add ${worktreePath} HEAD`.cwd(tmp.path).quiet() - - try { - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - expect(await run(tmp.path, (snapshot) => snapshot.track())).toBeTruthy() - }, - }) - - await WithInstance.provide({ - directory: worktreePath, - fn: async () => { - const before = await run(worktreePath, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - const worktreeFile = fwd(worktreePath, "worktree.txt") - await Filesystem.write(worktreeFile, "worktree content") - - const patch = await run(worktreePath, (snapshot) => snapshot.patch(before!)) - expect(patch.files).toContain(worktreeFile) - }, - }) - } finally { - await $`git worktree remove --force ${worktreePath}`.cwd(tmp.path).quiet().nothrow() - await $`rm -rf ${worktreePath}`.quiet() - } -}) - -test("revert only removes files in invoking worktree", async () => { - await using tmp = await bootstrap() - const worktreePath = `${tmp.path}-worktree` - await $`git worktree add ${worktreePath} HEAD`.cwd(tmp.path).quiet() - - try { - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - expect(await run(tmp.path, (snapshot) => snapshot.track())).toBeTruthy() - }, - }) + expect(patch2.files).not.toContain(fwd(tmp1.path, "project1.txt")) + }).pipe(provideInstance(tmp2.path)) + }), +) + +it.live( + "patch detects changes in secondary worktree", + Effect.gen(function* () { + const tmp = yield* bootstrapScoped() + const worktreePath = `${tmp.path}-worktree` + yield* exec(tmp.path, ["git", "worktree", "add", worktreePath, "HEAD"]) + yield* Effect.addFinalizer(() => cleanupWorktree(tmp.path, worktreePath)) + yield* Effect.gen(function* () { + const snapshot = yield* Snapshot.Service + expect(yield* snapshot.track()).toBeTruthy() + }).pipe(provideInstance(tmp.path)) + yield* Effect.gen(function* () { + const snapshot = yield* Snapshot.Service + const before = yield* snapshot.track() + expect(before).toBeTruthy() + const worktreeFile = fwd(worktreePath, "worktree.txt") + yield* write(worktreeFile, "worktree content") + expect((yield* snapshot.patch(before!)).files).toContain(worktreeFile) + }).pipe(provideInstance(worktreePath)) + }), +) + +it.live( + "revert only removes files in invoking worktree", + Effect.gen(function* () { + const tmp = yield* bootstrapScoped() + const worktreePath = `${tmp.path}-worktree` const primaryFile = `${tmp.path}/worktree.txt` - await Filesystem.write(primaryFile, "primary content") - - await WithInstance.provide({ - directory: worktreePath, - fn: async () => { - const before = await run(worktreePath, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - const worktreeFile = fwd(worktreePath, "worktree.txt") - await Filesystem.write(worktreeFile, "worktree content") - - const patch = await run(worktreePath, (snapshot) => snapshot.patch(before!)) - await run(worktreePath, (snapshot) => snapshot.revert([patch])) - - expect( - await fs - .access(worktreeFile) - .then(() => true) - .catch(() => false), - ).toBe(false) - }, - }) - - expect(await fs.readFile(primaryFile, "utf-8")).toBe("primary content") - } finally { - await $`git worktree remove --force ${worktreePath}`.cwd(tmp.path).quiet().nothrow() - await $`rm -rf ${worktreePath}`.quiet() - await $`rm -f ${tmp.path}/worktree.txt`.quiet() - } -}) - -test("diff reports worktree-only/shared edits and ignores primary-only", async () => { - await using tmp = await bootstrap() - const worktreePath = `${tmp.path}-worktree` - await $`git worktree add ${worktreePath} HEAD`.cwd(tmp.path).quiet() - - try { - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - expect(await run(tmp.path, (snapshot) => snapshot.track())).toBeTruthy() - }, - }) - - await WithInstance.provide({ - directory: worktreePath, - fn: async () => { - const before = await run(worktreePath, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await Filesystem.write(`${worktreePath}/worktree-only.txt`, "worktree diff content") - await Filesystem.write(`${worktreePath}/shared.txt`, "worktree edit") - await Filesystem.write(`${tmp.path}/shared.txt`, "primary edit") - await Filesystem.write(`${tmp.path}/primary-only.txt`, "primary change") - - const diff = await run(worktreePath, (snapshot) => snapshot.diff(before!)) - expect(diff).toContain("worktree-only.txt") - expect(diff).toContain("shared.txt") - expect(diff).not.toContain("primary-only.txt") - }, - }) - } finally { - await $`git worktree remove --force ${worktreePath}`.cwd(tmp.path).quiet().nothrow() - await $`rm -rf ${worktreePath}`.quiet() - await $`rm -f ${tmp.path}/shared.txt`.quiet() - await $`rm -f ${tmp.path}/primary-only.txt`.quiet() - } -}) - -test("track with no changes returns same hash", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const hash1 = await run(tmp.path, (snapshot) => snapshot.track()) - expect(hash1).toBeTruthy() - - // Track again with no changes - const hash2 = await run(tmp.path, (snapshot) => snapshot.track()) - expect(hash2).toBe(hash1!) - - // Track again - const hash3 = await run(tmp.path, (snapshot) => snapshot.track()) - expect(hash3).toBe(hash1!) - }, - }) -}) - -test("diff function with various changes", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) + yield* exec(tmp.path, ["git", "worktree", "add", worktreePath, "HEAD"]) + yield* Effect.addFinalizer(() => cleanupWorktree(tmp.path, worktreePath, [primaryFile])) + yield* Effect.gen(function* () { + const snapshot = yield* Snapshot.Service + expect(yield* snapshot.track()).toBeTruthy() + }).pipe(provideInstance(tmp.path)) + yield* write(primaryFile, "primary content") + yield* Effect.gen(function* () { + const snapshot = yield* Snapshot.Service + const before = yield* snapshot.track() expect(before).toBeTruthy() - - // Make various changes - await $`rm ${tmp.path}/a.txt`.quiet() - await Filesystem.write(`${tmp.path}/new.txt`, "new content") - await Filesystem.write(`${tmp.path}/b.txt`, "modified content") - - const diff = await run(tmp.path, (snapshot) => snapshot.diff(before!)) + const worktreeFile = fwd(worktreePath, "worktree.txt") + yield* write(worktreeFile, "worktree content") + const patch = yield* snapshot.patch(before!) + yield* snapshot.revert([patch]) + expect(yield* exists(worktreeFile)).toBe(false) + }).pipe(provideInstance(worktreePath)) + expect(yield* readText(primaryFile)).toBe("primary content") + }), +) + +it.live( + "diff reports worktree-only/shared edits and ignores primary-only", + Effect.gen(function* () { + const tmp = yield* bootstrapScoped() + const worktreePath = `${tmp.path}-worktree` + yield* exec(tmp.path, ["git", "worktree", "add", worktreePath, "HEAD"]) + yield* Effect.addFinalizer(() => + cleanupWorktree(tmp.path, worktreePath, [`${tmp.path}/shared.txt`, `${tmp.path}/primary-only.txt`]), + ) + yield* Effect.gen(function* () { + const snapshot = yield* Snapshot.Service + expect(yield* snapshot.track()).toBeTruthy() + }).pipe(provideInstance(tmp.path)) + yield* Effect.gen(function* () { + const snapshot = yield* Snapshot.Service + const before = yield* snapshot.track() + expect(before).toBeTruthy() + yield* write(`${worktreePath}/worktree-only.txt`, "worktree diff content") + yield* write(`${worktreePath}/shared.txt`, "worktree edit") + yield* write(`${tmp.path}/shared.txt`, "primary edit") + yield* write(`${tmp.path}/primary-only.txt`, "primary change") + const diff = yield* snapshot.diff(before!) + expect(diff).toContain("worktree-only.txt") + expect(diff).toContain("shared.txt") + expect(diff).not.toContain("primary-only.txt") + }).pipe(provideInstance(worktreePath)) + }), +) + +it.instance( + "track with no changes returns same hash", + withTrackedSnapshot(({ snapshot, before }) => + Effect.gen(function* () { + expect(yield* snapshot.track()).toBe(before) + expect(yield* snapshot.track()).toBe(before) + }), + ), + { git: true }, +) + +it.instance( + "diff function with various changes", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* rm(`${tmp.path}/a.txt`) + yield* write(`${tmp.path}/new.txt`, "new content") + yield* write(`${tmp.path}/b.txt`, "modified content") + const diff = yield* snapshot.diff(before) expect(diff).toContain("a.txt") expect(diff).toContain("b.txt") expect(diff).toContain("new.txt") - }, - }) -}) - -test("restore function", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - // Make changes - await $`rm ${tmp.path}/a.txt`.quiet() - await Filesystem.write(`${tmp.path}/new.txt`, "new content") - await Filesystem.write(`${tmp.path}/b.txt`, "modified") - - // Restore to original state - await run(tmp.path, (snapshot) => snapshot.restore(before!)) - - expect( - await fs - .access(`${tmp.path}/a.txt`) - .then(() => true) - .catch(() => false), - ).toBe(true) - expect(await fs.readFile(`${tmp.path}/a.txt`, "utf-8")).toBe(tmp.extra.aContent) - expect( - await fs - .access(`${tmp.path}/new.txt`) - .then(() => true) - .catch(() => false), - ).toBe(true) // New files should remain - expect(await fs.readFile(`${tmp.path}/b.txt`, "utf-8")).toBe(tmp.extra.bContent) - }, - }) -}) - -test("revert should not delete files that existed but were deleted in snapshot", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const snapshot1 = await run(tmp.path, (snapshot) => snapshot.track()) - expect(snapshot1).toBeTruthy() - - await $`rm ${tmp.path}/a.txt`.quiet() - - const snapshot2 = await run(tmp.path, (snapshot) => snapshot.track()) - expect(snapshot2).toBeTruthy() - - await Filesystem.write(`${tmp.path}/a.txt`, "recreated content") - - const patch = await run(tmp.path, (snapshot) => snapshot.patch(snapshot2!)) - expect(patch.files).toContain(fwd(tmp.path, "a.txt")) - - await run(tmp.path, (snapshot) => snapshot.revert([patch])) - - expect( - await fs - .access(`${tmp.path}/a.txt`) - .then(() => true) - .catch(() => false), - ).toBe(false) - }, - }) -}) - -test("revert preserves file that existed in snapshot when deleted then recreated", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await Filesystem.write(`${tmp.path}/existing.txt`, "original content") - - const hash = await run(tmp.path, (snapshot) => snapshot.track()) - expect(hash).toBeTruthy() - - await $`rm ${tmp.path}/existing.txt`.quiet() - await Filesystem.write(`${tmp.path}/existing.txt`, "recreated") - await Filesystem.write(`${tmp.path}/newfile.txt`, "new") - - const patch = await run(tmp.path, (snapshot) => snapshot.patch(hash!)) - expect(patch.files).toContain(fwd(tmp.path, "existing.txt")) - expect(patch.files).toContain(fwd(tmp.path, "newfile.txt")) - - await run(tmp.path, (snapshot) => snapshot.revert([patch])) - - expect( - await fs - .access(`${tmp.path}/newfile.txt`) - .then(() => true) - .catch(() => false), - ).toBe(false) - expect( - await fs - .access(`${tmp.path}/existing.txt`) - .then(() => true) - .catch(() => false), - ).toBe(true) - expect(await fs.readFile(`${tmp.path}/existing.txt`, "utf-8")).toBe("original content") - }, - }) -}) - -test("diffFull sets status based on git change type", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await Filesystem.write(`${tmp.path}/grow.txt`, "one\n") - await Filesystem.write(`${tmp.path}/trim.txt`, "line1\nline2\n") - await Filesystem.write(`${tmp.path}/delete.txt`, "gone") - - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await Filesystem.write(`${tmp.path}/grow.txt`, "one\ntwo\n") - await Filesystem.write(`${tmp.path}/trim.txt`, "line1\n") - await $`rm ${tmp.path}/delete.txt`.quiet() - await Filesystem.write(`${tmp.path}/added.txt`, "new") - - const after = await run(tmp.path, (snapshot) => snapshot.track()) - expect(after).toBeTruthy() - - const diffs = await run(tmp.path, (snapshot) => snapshot.diffFull(before!, after!)) - expect(diffs.length).toBe(4) - - const added = diffs.find((d) => d.file === "added.txt") - expect(added).toBeDefined() - expect(added!.status).toBe("added") - - const deleted = diffs.find((d) => d.file === "delete.txt") - expect(deleted).toBeDefined() - expect(deleted!.status).toBe("deleted") - - const grow = diffs.find((d) => d.file === "grow.txt") - expect(grow).toBeDefined() - expect(grow!.status).toBe("modified") - expect(grow!.additions).toBeGreaterThan(0) - expect(grow!.deletions).toBe(0) - - const trim = diffs.find((d) => d.file === "trim.txt") - expect(trim).toBeDefined() - expect(trim!.status).toBe("modified") - expect(trim!.additions).toBe(0) - expect(trim!.deletions).toBeGreaterThan(0) - }, - }) -}) - -test("diffFull with new file additions", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await Filesystem.write(`${tmp.path}/new.txt`, "new content") - - const after = await run(tmp.path, (snapshot) => snapshot.track()) + }), + ), + { git: true }, +) + +it.instance( + "restore function", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* rm(`${tmp.path}/a.txt`) + yield* write(`${tmp.path}/new.txt`, "new content") + yield* write(`${tmp.path}/b.txt`, "modified") + yield* snapshot.restore(before) + expect(yield* exists(`${tmp.path}/a.txt`)).toBe(true) + expect(yield* readText(`${tmp.path}/a.txt`)).toBe(tmp.extra.aContent) + expect(yield* exists(`${tmp.path}/new.txt`)).toBe(true) + expect(yield* readText(`${tmp.path}/b.txt`)).toBe(tmp.extra.bContent) + }), + ), + { git: true }, +) + +it.instance( + "revert should not delete files that existed but were deleted in snapshot", + Effect.gen(function* () { + const tmp = yield* bootstrap() + const snapshot = yield* Snapshot.Service + const snapshot1 = yield* snapshot.track() + expect(snapshot1).toBeTruthy() + yield* rm(`${tmp.path}/a.txt`) + const snapshot2 = yield* snapshot.track() + expect(snapshot2).toBeTruthy() + yield* write(`${tmp.path}/a.txt`, "recreated content") + const patch = yield* snapshot.patch(snapshot2!) + expect(patch.files).toContain(fwd(tmp.path, "a.txt")) + yield* snapshot.revert([patch]) + expect(yield* exists(`${tmp.path}/a.txt`)).toBe(false) + }), + { git: true }, +) + +it.instance( + "revert preserves file that existed in snapshot when deleted then recreated", + Effect.gen(function* () { + const tmp = yield* bootstrap() + const snapshot = yield* Snapshot.Service + yield* write(`${tmp.path}/existing.txt`, "original content") + const hash = yield* snapshot.track() + expect(hash).toBeTruthy() + yield* rm(`${tmp.path}/existing.txt`) + yield* write(`${tmp.path}/existing.txt`, "recreated") + yield* write(`${tmp.path}/newfile.txt`, "new") + const patch = yield* snapshot.patch(hash!) + expect(patch.files).toContain(fwd(tmp.path, "existing.txt")) + expect(patch.files).toContain(fwd(tmp.path, "newfile.txt")) + yield* snapshot.revert([patch]) + expect(yield* exists(`${tmp.path}/newfile.txt`)).toBe(false) + expect(yield* exists(`${tmp.path}/existing.txt`)).toBe(true) + expect(yield* readText(`${tmp.path}/existing.txt`)).toBe("original content") + }), + { git: true }, +) + +it.instance( + "diffFull sets status based on git change type", + Effect.gen(function* () { + const tmp = yield* bootstrap() + const snapshot = yield* Snapshot.Service + yield* write(`${tmp.path}/grow.txt`, "one\n") + yield* write(`${tmp.path}/trim.txt`, "line1\nline2\n") + yield* write(`${tmp.path}/delete.txt`, "gone") + const before = yield* snapshot.track() + expect(before).toBeTruthy() + yield* write(`${tmp.path}/grow.txt`, "one\ntwo\n") + yield* write(`${tmp.path}/trim.txt`, "line1\n") + yield* rm(`${tmp.path}/delete.txt`) + yield* write(`${tmp.path}/added.txt`, "new") + const after = yield* snapshot.track() + expect(after).toBeTruthy() + const diffs = yield* snapshot.diffFull(before!, after!) + expect(diffs.length).toBe(4) + expect(diffs.find((d) => d.file === "added.txt")!.status).toBe("added") + expect(diffs.find((d) => d.file === "delete.txt")!.status).toBe("deleted") + const grow = diffs.find((d) => d.file === "grow.txt")! + expect(grow.status).toBe("modified") + expect(grow.additions).toBeGreaterThan(0) + expect(grow.deletions).toBe(0) + const trim = diffs.find((d) => d.file === "trim.txt")! + expect(trim.status).toBe("modified") + expect(trim.additions).toBe(0) + expect(trim.deletions).toBeGreaterThan(0) + }), + { git: true }, +) + +it.instance( + "diffFull with new file additions", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* write(`${tmp.path}/new.txt`, "new content") + const after = yield* snapshot.track() expect(after).toBeTruthy() - - const diffs = await run(tmp.path, (snapshot) => snapshot.diffFull(before!, after!)) + const diffs = yield* snapshot.diffFull(before, after!) expect(diffs.length).toBe(1) - - const newFileDiff = diffs[0] - expect(newFileDiff.file).toBe("new.txt") - expect(newFileDiff.patch).toContain("+new content") - expect(newFileDiff.additions).toBe(1) - expect(newFileDiff.deletions).toBe(0) - }, - }) -}) - -test("diffFull with a large interleaved mixed diff", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const ids = Array.from({ length: 60 }, (_, i) => i.toString().padStart(3, "0")) - const mod = ids.map((id) => fwd(tmp.path, "mix", `${id}-mod.txt`)) - const del = ids.map((id) => fwd(tmp.path, "mix", `${id}-del.txt`)) - const add = ids.map((id) => fwd(tmp.path, "mix", `${id}-add.txt`)) - const bin = ids.map((id) => fwd(tmp.path, "mix", `${id}-bin.bin`)) - - await $`mkdir -p ${tmp.path}/mix`.quiet() - await Promise.all([ - ...mod.map((file, i) => Filesystem.write(file, `before-${ids[i]}-é\n🙂\nline`)), - ...del.map((file, i) => Filesystem.write(file, `gone-${ids[i]}\n你好`)), - ...bin.map((file, i) => Filesystem.write(file, new Uint8Array([0, i, 255, i % 251]))), - ]) - - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await Promise.all([ - ...mod.map((file, i) => Filesystem.write(file, `after-${ids[i]}-é\n🚀\nline`)), - ...add.map((file, i) => Filesystem.write(file, `new-${ids[i]}\nこんにちは`)), - ...bin.map((file, i) => Filesystem.write(file, new Uint8Array([9, i, 8, i % 251]))), - ...del.map((file) => fs.rm(file)), - ]) - - const after = await run(tmp.path, (snapshot) => snapshot.track()) - expect(after).toBeTruthy() - - const diffs = await run(tmp.path, (snapshot) => snapshot.diffFull(before!, after!)) - expect(diffs).toHaveLength(ids.length * 4) - - const map = new Map(diffs.map((item) => [item.file, item])) - for (let i = 0; i < ids.length; i++) { - const m = map.get(fwd("mix", `${ids[i]}-mod.txt`)) - expect(m).toBeDefined() - expect(m!.patch).toContain(`-before-${ids[i]}-é`) - expect(m!.patch).toContain(`+after-${ids[i]}-é`) - expect(m!.status).toBe("modified") - - const d = map.get(fwd("mix", `${ids[i]}-del.txt`)) - expect(d).toBeDefined() - expect(d!.patch).toContain(`-gone-${ids[i]}`) - expect(d!.status).toBe("deleted") - - const a = map.get(fwd("mix", `${ids[i]}-add.txt`)) - expect(a).toBeDefined() - expect(a!.patch).toContain(`+new-${ids[i]}`) - expect(a!.status).toBe("added") - - const b = map.get(fwd("mix", `${ids[i]}-bin.bin`)) - expect(b).toBeDefined() - expect(b!.patch).toBe("") - expect(b!.additions).toBe(0) - expect(b!.deletions).toBe(0) - expect(b!.status).toBe("modified") - } - }, - }) -}) - -test("diffFull preserves git diff order across batch boundaries", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const ids = Array.from({ length: 140 }, (_, i) => i.toString().padStart(3, "0")) - - await $`mkdir -p ${tmp.path}/order`.quiet() - await Promise.all(ids.map((id) => Filesystem.write(`${tmp.path}/order/${id}.txt`, `before-${id}`))) - - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await Promise.all(ids.map((id) => Filesystem.write(`${tmp.path}/order/${id}.txt`, `after-${id}`))) - - const after = await run(tmp.path, (snapshot) => snapshot.track()) - expect(after).toBeTruthy() - - const expected = ids.map((id) => `order/${id}.txt`) - - const diffs = await run(tmp.path, (snapshot) => snapshot.diffFull(before!, after!)) - expect(diffs.map((item) => item.file)).toEqual(expected) - }, - }) -}) - -test("diffFull with file modifications", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await Filesystem.write(`${tmp.path}/b.txt`, "modified content") - - const after = await run(tmp.path, (snapshot) => snapshot.track()) + expect(diffs[0].file).toBe("new.txt") + expect(diffs[0].patch).toContain("+new content") + expect(diffs[0].additions).toBe(1) + expect(diffs[0].deletions).toBe(0) + }), + ), + { git: true }, +) + +it.instance( + "diffFull with a large interleaved mixed diff", + Effect.gen(function* () { + const tmp = yield* bootstrap() + const snapshot = yield* Snapshot.Service + const ids = Array.from({ length: 60 }, (_, i) => i.toString().padStart(3, "0")) + const mod = ids.map((id) => fwd(tmp.path, "mix", `${id}-mod.txt`)) + const del = ids.map((id) => fwd(tmp.path, "mix", `${id}-del.txt`)) + const add = ids.map((id) => fwd(tmp.path, "mix", `${id}-add.txt`)) + const bin = ids.map((id) => fwd(tmp.path, "mix", `${id}-bin.bin`)) + yield* mkdirp(`${tmp.path}/mix`) + yield* Effect.all( + [ + ...mod.map((file, i) => write(file, `before-${ids[i]}-é\n🙂\nline`)), + ...del.map((file, i) => write(file, `gone-${ids[i]}\n你好`)), + ...bin.map((file, i) => write(file, new Uint8Array([0, i, 255, i % 251]))), + ], + { concurrency: "unbounded" }, + ) + const before = yield* snapshot.track() + expect(before).toBeTruthy() + yield* Effect.all( + [ + ...mod.map((file, i) => write(file, `after-${ids[i]}-é\n🚀\nline`)), + ...add.map((file, i) => write(file, `new-${ids[i]}\nこんにちは`)), + ...bin.map((file, i) => write(file, new Uint8Array([9, i, 8, i % 251]))), + ...del.map((file) => rm(file)), + ], + { concurrency: "unbounded" }, + ) + const after = yield* snapshot.track() + expect(after).toBeTruthy() + const diffs = yield* snapshot.diffFull(before!, after!) + expect(diffs).toHaveLength(ids.length * 4) + const map = new Map(diffs.map((item) => [item.file, item])) + for (let i = 0; i < ids.length; i++) { + const m = map.get(fwd("mix", `${ids[i]}-mod.txt`)) + expect(m).toBeDefined() + expect(m!.patch).toContain(`-before-${ids[i]}-é`) + expect(m!.patch).toContain(`+after-${ids[i]}-é`) + expect(m!.status).toBe("modified") + const d = map.get(fwd("mix", `${ids[i]}-del.txt`)) + expect(d).toBeDefined() + expect(d!.patch).toContain(`-gone-${ids[i]}`) + expect(d!.status).toBe("deleted") + const a = map.get(fwd("mix", `${ids[i]}-add.txt`)) + expect(a).toBeDefined() + expect(a!.patch).toContain(`+new-${ids[i]}`) + expect(a!.status).toBe("added") + const b = map.get(fwd("mix", `${ids[i]}-bin.bin`)) + expect(b).toBeDefined() + expect(b!.patch).toBe("") + expect(b!.additions).toBe(0) + expect(b!.deletions).toBe(0) + expect(b!.status).toBe("modified") + } + }), + { git: true }, +) + +it.instance( + "diffFull preserves git diff order across batch boundaries", + Effect.gen(function* () { + const tmp = yield* bootstrap() + const snapshot = yield* Snapshot.Service + const ids = Array.from({ length: 140 }, (_, i) => i.toString().padStart(3, "0")) + yield* mkdirp(`${tmp.path}/order`) + yield* Effect.all( + ids.map((id) => write(`${tmp.path}/order/${id}.txt`, `before-${id}`)), + { concurrency: "unbounded" }, + ) + const before = yield* snapshot.track() + expect(before).toBeTruthy() + yield* Effect.all( + ids.map((id) => write(`${tmp.path}/order/${id}.txt`, `after-${id}`)), + { concurrency: "unbounded" }, + ) + const after = yield* snapshot.track() + expect(after).toBeTruthy() + expect((yield* snapshot.diffFull(before!, after!)).map((item) => item.file)).toEqual( + ids.map((id) => `order/${id}.txt`), + ) + }), + { git: true }, +) + +it.instance( + "diffFull with file modifications", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* write(`${tmp.path}/b.txt`, "modified content") + const after = yield* snapshot.track() expect(after).toBeTruthy() - - const diffs = await run(tmp.path, (snapshot) => snapshot.diffFull(before!, after!)) + const diffs = yield* snapshot.diffFull(before, after!) expect(diffs.length).toBe(1) - - const modifiedFileDiff = diffs[0] - expect(modifiedFileDiff.file).toBe("b.txt") - expect(modifiedFileDiff.patch).toContain(`-${tmp.extra.bContent}`) - expect(modifiedFileDiff.patch).toContain("+modified content") - expect(modifiedFileDiff.additions).toBeGreaterThan(0) - expect(modifiedFileDiff.deletions).toBeGreaterThan(0) - }, - }) -}) - -test("diffFull with file deletions", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await $`rm ${tmp.path}/a.txt`.quiet() - - const after = await run(tmp.path, (snapshot) => snapshot.track()) + expect(diffs[0].file).toBe("b.txt") + expect(diffs[0].patch).toContain(`-${tmp.extra.bContent}`) + expect(diffs[0].patch).toContain("+modified content") + expect(diffs[0].additions).toBeGreaterThan(0) + expect(diffs[0].deletions).toBeGreaterThan(0) + }), + ), + { git: true }, +) + +it.instance( + "diffFull with file deletions", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* rm(`${tmp.path}/a.txt`) + const after = yield* snapshot.track() expect(after).toBeTruthy() - - const diffs = await run(tmp.path, (snapshot) => snapshot.diffFull(before!, after!)) + const diffs = yield* snapshot.diffFull(before, after!) expect(diffs.length).toBe(1) - - const removedFileDiff = diffs[0] - expect(removedFileDiff.file).toBe("a.txt") - expect(removedFileDiff.patch).toContain(`-${tmp.extra.aContent}`) - expect(removedFileDiff.additions).toBe(0) - expect(removedFileDiff.deletions).toBe(1) - }, - }) -}) - -test("diffFull with multiple line additions", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await Filesystem.write(`${tmp.path}/multi.txt`, "line1\nline2\nline3") - - const after = await run(tmp.path, (snapshot) => snapshot.track()) + expect(diffs[0].file).toBe("a.txt") + expect(diffs[0].patch).toContain(`-${tmp.extra.aContent}`) + expect(diffs[0].additions).toBe(0) + expect(diffs[0].deletions).toBe(1) + }), + ), + { git: true }, +) + +it.instance( + "diffFull with multiple line additions", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* write(`${tmp.path}/multi.txt`, "line1\nline2\nline3") + const after = yield* snapshot.track() expect(after).toBeTruthy() - - const diffs = await run(tmp.path, (snapshot) => snapshot.diffFull(before!, after!)) + const diffs = yield* snapshot.diffFull(before, after!) expect(diffs.length).toBe(1) - - const multiDiff = diffs[0] - expect(multiDiff.file).toBe("multi.txt") - expect(multiDiff.patch).toContain("+line1") - expect(multiDiff.patch).toContain("+line3") - expect(multiDiff.additions).toBe(3) - expect(multiDiff.deletions).toBe(0) - }, - }) -}) - -test("diffFull with addition and deletion", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await Filesystem.write(`${tmp.path}/added.txt`, "added content") - await $`rm ${tmp.path}/a.txt`.quiet() - - const after = await run(tmp.path, (snapshot) => snapshot.track()) + expect(diffs[0].file).toBe("multi.txt") + expect(diffs[0].patch).toContain("+line1") + expect(diffs[0].patch).toContain("+line3") + expect(diffs[0].additions).toBe(3) + expect(diffs[0].deletions).toBe(0) + }), + ), + { git: true }, +) + +it.instance( + "diffFull with addition and deletion", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* write(`${tmp.path}/added.txt`, "added content") + yield* rm(`${tmp.path}/a.txt`) + const after = yield* snapshot.track() expect(after).toBeTruthy() - - const diffs = await run(tmp.path, (snapshot) => snapshot.diffFull(before!, after!)) + const diffs = yield* snapshot.diffFull(before, after!) expect(diffs.length).toBe(2) - - const addedFileDiff = diffs.find((d) => d.file === "added.txt") - expect(addedFileDiff).toBeDefined() - expect(addedFileDiff!.patch).toContain("+added content") - expect(addedFileDiff!.additions).toBe(1) - expect(addedFileDiff!.deletions).toBe(0) - - const removedFileDiff = diffs.find((d) => d.file === "a.txt") - expect(removedFileDiff).toBeDefined() - expect(removedFileDiff!.patch).toContain(`-${tmp.extra.aContent}`) - expect(removedFileDiff!.additions).toBe(0) - expect(removedFileDiff!.deletions).toBe(1) - }, - }) -}) - -test("diffFull with multiple additions and deletions", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await Filesystem.write(`${tmp.path}/multi1.txt`, "line1\nline2\nline3") - await Filesystem.write(`${tmp.path}/multi2.txt`, "single line") - await $`rm ${tmp.path}/a.txt`.quiet() - await $`rm ${tmp.path}/b.txt`.quiet() - - const after = await run(tmp.path, (snapshot) => snapshot.track()) + const added = diffs.find((d) => d.file === "added.txt")! + expect(added.patch).toContain("+added content") + expect(added.additions).toBe(1) + expect(added.deletions).toBe(0) + const removed = diffs.find((d) => d.file === "a.txt")! + expect(removed.patch).toContain(`-${tmp.extra.aContent}`) + expect(removed.additions).toBe(0) + expect(removed.deletions).toBe(1) + }), + ), + { git: true }, +) + +it.instance( + "diffFull with multiple additions and deletions", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* write(`${tmp.path}/multi1.txt`, "line1\nline2\nline3") + yield* write(`${tmp.path}/multi2.txt`, "single line") + yield* rm(`${tmp.path}/a.txt`) + yield* rm(`${tmp.path}/b.txt`) + const after = yield* snapshot.track() expect(after).toBeTruthy() - - const diffs = await run(tmp.path, (snapshot) => snapshot.diffFull(before!, after!)) + const diffs = yield* snapshot.diffFull(before, after!) expect(diffs.length).toBe(4) - - const multi1Diff = diffs.find((d) => d.file === "multi1.txt") - expect(multi1Diff).toBeDefined() - expect(multi1Diff!.additions).toBe(3) - expect(multi1Diff!.deletions).toBe(0) - - const multi2Diff = diffs.find((d) => d.file === "multi2.txt") - expect(multi2Diff).toBeDefined() - expect(multi2Diff!.additions).toBe(1) - expect(multi2Diff!.deletions).toBe(0) - - const removedADiff = diffs.find((d) => d.file === "a.txt") - expect(removedADiff).toBeDefined() - expect(removedADiff!.additions).toBe(0) - expect(removedADiff!.deletions).toBe(1) - - const removedBDiff = diffs.find((d) => d.file === "b.txt") - expect(removedBDiff).toBeDefined() - expect(removedBDiff!.additions).toBe(0) - expect(removedBDiff!.deletions).toBe(1) - }, - }) -}) - -test("diffFull with no changes", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - const after = await run(tmp.path, (snapshot) => snapshot.track()) - expect(after).toBeTruthy() - - const diffs = await run(tmp.path, (snapshot) => snapshot.diffFull(before!, after!)) - expect(diffs.length).toBe(0) - }, - }) -}) - -test("diffFull with binary file changes", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await Filesystem.write(`${tmp.path}/binary.bin`, new Uint8Array([0x00, 0x01, 0x02, 0x03])) - - const after = await run(tmp.path, (snapshot) => snapshot.track()) + expect(diffs.find((d) => d.file === "multi1.txt")!.additions).toBe(3) + expect(diffs.find((d) => d.file === "multi1.txt")!.deletions).toBe(0) + expect(diffs.find((d) => d.file === "multi2.txt")!.additions).toBe(1) + expect(diffs.find((d) => d.file === "multi2.txt")!.deletions).toBe(0) + expect(diffs.find((d) => d.file === "a.txt")!.additions).toBe(0) + expect(diffs.find((d) => d.file === "a.txt")!.deletions).toBe(1) + expect(diffs.find((d) => d.file === "b.txt")!.additions).toBe(0) + expect(diffs.find((d) => d.file === "b.txt")!.deletions).toBe(1) + }), + ), + { git: true }, +) + +it.instance( + "diffFull with no changes", + withTrackedSnapshot(({ snapshot, before }) => + Effect.gen(function* () { + const after = yield* snapshot.track() expect(after).toBeTruthy() - - const diffs = await run(tmp.path, (snapshot) => snapshot.diffFull(before!, after!)) - expect(diffs.length).toBe(1) - - const binaryDiff = diffs[0] - expect(binaryDiff.file).toBe("binary.bin") - expect(binaryDiff.patch).toBe("") - }, - }) -}) - -test("diffFull with whitespace changes", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await Filesystem.write(`${tmp.path}/whitespace.txt`, "line1\nline2") - const before = await run(tmp.path, (snapshot) => snapshot.track()) - expect(before).toBeTruthy() - - await Filesystem.write(`${tmp.path}/whitespace.txt`, "line1\n\nline2\n") - - const after = await run(tmp.path, (snapshot) => snapshot.track()) + expect((yield* snapshot.diffFull(before, after!)).length).toBe(0) + }), + ), + { git: true }, +) + +it.instance( + "diffFull with binary file changes", + withTrackedSnapshot(({ tmp, snapshot, before }) => + Effect.gen(function* () { + yield* write(`${tmp.path}/binary.bin`, new Uint8Array([0x00, 0x01, 0x02, 0x03])) + const after = yield* snapshot.track() expect(after).toBeTruthy() - - const diffs = await run(tmp.path, (snapshot) => snapshot.diffFull(before!, after!)) + const diffs = yield* snapshot.diffFull(before, after!) expect(diffs.length).toBe(1) - - const whitespaceDiff = diffs[0] - expect(whitespaceDiff.file).toBe("whitespace.txt") - expect(whitespaceDiff.additions).toBeGreaterThan(0) - }, - }) -}) - -test("revert with overlapping files across patches uses first patch hash", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - // Write initial content and snapshot - await Filesystem.write(`${tmp.path}/shared.txt`, "v1") - const snap1 = await run(tmp.path, (snapshot) => snapshot.track()) - expect(snap1).toBeTruthy() - - // Modify and snapshot again - await Filesystem.write(`${tmp.path}/shared.txt`, "v2") - const snap2 = await run(tmp.path, (snapshot) => snapshot.track()) - expect(snap2).toBeTruthy() - - // Modify once more so both patches include shared.txt - await Filesystem.write(`${tmp.path}/shared.txt`, "v3") - - const patch1 = await run(tmp.path, (snapshot) => snapshot.patch(snap1!)) - const patch2 = await run(tmp.path, (snapshot) => snapshot.patch(snap2!)) - - // Both patches should include shared.txt - expect(patch1.files).toContain(fwd(tmp.path, "shared.txt")) - expect(patch2.files).toContain(fwd(tmp.path, "shared.txt")) - - // Revert with patch1 first — should use snap1's hash (restoring "v1") - await run(tmp.path, (snapshot) => snapshot.revert([patch1, patch2])) - - const content = await fs.readFile(`${tmp.path}/shared.txt`, "utf-8") - expect(content).toBe("v1") - }, - }) -}) - -test("revert preserves patch order when the same hash appears again", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await $`mkdir -p ${tmp.path}/foo`.quiet() - await Filesystem.write(`${tmp.path}/foo/bar`, "v1") - await Filesystem.write(`${tmp.path}/a.txt`, "v1") - - const snap1 = await run(tmp.path, (snapshot) => snapshot.track()) - expect(snap1).toBeTruthy() - - await $`rm -rf ${tmp.path}/foo`.quiet() - await Filesystem.write(`${tmp.path}/foo`, "v2") - await Filesystem.write(`${tmp.path}/a.txt`, "v2") - - const snap2 = await run(tmp.path, (snapshot) => snapshot.track()) - expect(snap2).toBeTruthy() - - await $`rm -rf ${tmp.path}/foo`.quiet() - await Filesystem.write(`${tmp.path}/a.txt`, "v3") - - await run(tmp.path, (snapshot) => - snapshot.revert([ - { hash: snap1!, files: [fwd(tmp.path, "a.txt")] }, - { hash: snap2!, files: [fwd(tmp.path, "foo")] }, - { hash: snap1!, files: [fwd(tmp.path, "foo", "bar")] }, - ]), - ) - - expect(await fs.readFile(`${tmp.path}/a.txt`, "utf-8")).toBe("v1") - expect((await fs.stat(`${tmp.path}/foo`)).isDirectory()).toBe(true) - expect(await fs.readFile(`${tmp.path}/foo/bar`, "utf-8")).toBe("v1") - }, - }) -}) - -test("revert handles large mixed batches across chunk boundaries", async () => { - await using tmp = await bootstrap() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const base = Array.from({ length: 140 }, (_, i) => fwd(tmp.path, "batch", `${i}.txt`)) - const fresh = Array.from({ length: 140 }, (_, i) => fwd(tmp.path, "fresh", `${i}.txt`)) - - await $`mkdir -p ${tmp.path}/batch ${tmp.path}/fresh`.quiet() - await Promise.all(base.map((file, i) => Filesystem.write(file, `base-${i}`))) - - const snap = await run(tmp.path, (snapshot) => snapshot.track()) - expect(snap).toBeTruthy() - - await Promise.all(base.map((file, i) => Filesystem.write(file, `next-${i}`))) - await Promise.all(fresh.map((file, i) => Filesystem.write(file, `fresh-${i}`))) - - const patch = await run(tmp.path, (snapshot) => snapshot.patch(snap!)) - expect(patch.files.length).toBe(base.length + fresh.length) - - await run(tmp.path, (snapshot) => snapshot.revert([patch])) - - await Promise.all( - base.map(async (file, i) => { - expect(await fs.readFile(file, "utf-8")).toBe(`base-${i}`) - }), - ) - - await Promise.all( - fresh.map(async (file) => { - expect( - await fs - .access(file) - .then(() => true) - .catch(() => false), - ).toBe(false) - }), - ) - }, - }) -}) + expect(diffs[0].file).toBe("binary.bin") + expect(diffs[0].patch).toBe("") + }), + ), + { git: true }, +) + +it.instance( + "diffFull with whitespace changes", + Effect.gen(function* () { + const tmp = yield* bootstrap() + const snapshot = yield* Snapshot.Service + yield* write(`${tmp.path}/whitespace.txt`, "line1\nline2") + const before = yield* snapshot.track() + expect(before).toBeTruthy() + yield* write(`${tmp.path}/whitespace.txt`, "line1\n\nline2\n") + const after = yield* snapshot.track() + expect(after).toBeTruthy() + const diffs = yield* snapshot.diffFull(before!, after!) + expect(diffs.length).toBe(1) + expect(diffs[0].file).toBe("whitespace.txt") + expect(diffs[0].additions).toBeGreaterThan(0) + }), + { git: true }, +) + +it.instance( + "revert with overlapping files across patches uses first patch hash", + Effect.gen(function* () { + const tmp = yield* bootstrap() + const snapshot = yield* Snapshot.Service + yield* write(`${tmp.path}/shared.txt`, "v1") + const snap1 = yield* snapshot.track() + expect(snap1).toBeTruthy() + yield* write(`${tmp.path}/shared.txt`, "v2") + const snap2 = yield* snapshot.track() + expect(snap2).toBeTruthy() + yield* write(`${tmp.path}/shared.txt`, "v3") + const patch1 = yield* snapshot.patch(snap1!) + const patch2 = yield* snapshot.patch(snap2!) + expect(patch1.files).toContain(fwd(tmp.path, "shared.txt")) + expect(patch2.files).toContain(fwd(tmp.path, "shared.txt")) + yield* snapshot.revert([patch1, patch2]) + expect(yield* readText(`${tmp.path}/shared.txt`)).toBe("v1") + }), + { git: true }, +) + +it.instance( + "revert preserves patch order when the same hash appears again", + Effect.gen(function* () { + const tmp = yield* bootstrap() + const snapshot = yield* Snapshot.Service + yield* mkdirp(`${tmp.path}/foo`) + yield* write(`${tmp.path}/foo/bar`, "v1") + yield* write(`${tmp.path}/a.txt`, "v1") + const snap1 = yield* snapshot.track() + expect(snap1).toBeTruthy() + yield* rm(`${tmp.path}/foo`) + yield* write(`${tmp.path}/foo`, "v2") + yield* write(`${tmp.path}/a.txt`, "v2") + const snap2 = yield* snapshot.track() + expect(snap2).toBeTruthy() + yield* rm(`${tmp.path}/foo`) + yield* write(`${tmp.path}/a.txt`, "v3") + yield* snapshot.revert([ + { hash: snap1!, files: [fwd(tmp.path, "a.txt")] }, + { hash: snap2!, files: [fwd(tmp.path, "foo")] }, + { hash: snap1!, files: [fwd(tmp.path, "foo", "bar")] }, + ]) + expect(yield* readText(`${tmp.path}/a.txt`)).toBe("v1") + expect((yield* Effect.promise(() => fs.stat(`${tmp.path}/foo`))).isDirectory()).toBe(true) + expect(yield* readText(`${tmp.path}/foo/bar`)).toBe("v1") + }), + { git: true }, +) + +it.instance( + "revert handles large mixed batches across chunk boundaries", + Effect.gen(function* () { + const tmp = yield* bootstrap() + const snapshot = yield* Snapshot.Service + const base = Array.from({ length: 140 }, (_, i) => fwd(tmp.path, "batch", `${i}.txt`)) + const fresh = Array.from({ length: 140 }, (_, i) => fwd(tmp.path, "fresh", `${i}.txt`)) + yield* mkdirp(`${tmp.path}/batch`) + yield* mkdirp(`${tmp.path}/fresh`) + yield* Effect.all( + base.map((file, i) => write(file, `base-${i}`)), + { concurrency: "unbounded" }, + ) + const snap = yield* snapshot.track() + expect(snap).toBeTruthy() + yield* Effect.all( + [...base.map((file, i) => write(file, `next-${i}`)), ...fresh.map((file, i) => write(file, `fresh-${i}`))], + { concurrency: "unbounded" }, + ) + const patch = yield* snapshot.patch(snap!) + expect(patch.files.length).toBe(base.length + fresh.length) + yield* snapshot.revert([patch]) + for (let i = 0; i < base.length; i++) expect(yield* readText(base[i])).toBe(`base-${i}`) + for (const file of fresh) expect(yield* exists(file)).toBe(false) + }), + { git: true }, +) diff --git a/packages/opencode/test/storage/db.test.ts b/packages/opencode/test/storage/db.test.ts index 15bd4d0d6f2..d56c0bd1ec7 100644 --- a/packages/opencode/test/storage/db.test.ts +++ b/packages/opencode/test/storage/db.test.ts @@ -1,25 +1,29 @@ -import { describe, expect, test } from "bun:test" +import { describe, expect } from "bun:test" import path from "path" -import { Flag } from "@opencode-ai/core/flag/flag" // kilocode_change +import { Effect } from "effect" import { Global } from "@opencode-ai/core/global" import { InstallationChannel } from "@opencode-ai/core/installation/version" +import { RuntimeFlags } from "@/effect/runtime-flags" import { Database } from "@/storage/db" +import { it } from "../lib/effect" -describe("Database.Path", () => { - test("returns database path for the current channel", () => { - // kilocode_change start — test preload sets KILO_DB=:memory: - if (Flag.KILO_DB) { - const expected = - Flag.KILO_DB === ":memory:" || path.isAbsolute(Flag.KILO_DB) - ? Flag.KILO_DB - : path.join(Global.Path.data, Flag.KILO_DB) - expect(Database.Path).toBe(expected) - return - } - // kilocode_change end - const expected = ["latest", "beta"].includes(InstallationChannel) - ? path.join(Global.Path.data, "kilo.db") - : path.join(Global.Path.data, `opencode-${InstallationChannel.replace(/[^a-zA-Z0-9._-]/g, "-")}.db`) - expect(Database.getChannelPath()).toBe(expected) - }) +describe("Database.getChannelPath", () => { + it.effect("returns database path for the current channel", () => + Effect.gen(function* () { + const flags = yield* RuntimeFlags.Service + const expected = ["latest", "beta", "prod"].includes(InstallationChannel) + ? path.join(Global.Path.data, "kilo.db") + : path.join(Global.Path.data, `opencode-${InstallationChannel.replace(/[^a-zA-Z0-9._-]/g, "-")}.db`) + + expect(Database.getChannelPath(flags)).toBe(expected) + }).pipe(Effect.provide(RuntimeFlags.layer())), + ) + + it.effect("uses the shared database path when channel databases are disabled", () => + Effect.gen(function* () { + const flags = yield* RuntimeFlags.Service + + expect(Database.getChannelPath(flags)).toBe(path.join(Global.Path.data, "kilo.db")) + }).pipe(Effect.provide(RuntimeFlags.layer({ disableChannelDb: true }))), + ) }) diff --git a/packages/opencode/test/storage/storage.test.ts b/packages/opencode/test/storage/storage.test.ts index f0aff4ba78f..d0fe5dd34ce 100644 --- a/packages/opencode/test/storage/storage.test.ts +++ b/packages/opencode/test/storage/storage.test.ts @@ -74,20 +74,23 @@ describe("Storage", () => { it.live("maps missing reads to NotFoundError", () => Effect.gen(function* () { const { root, svc } = yield* scope() - const exit = yield* svc.read([...root, "missing", "value"]).pipe(Effect.exit) - expect(Exit.isFailure(exit)).toBe(true) + const error = yield* Effect.flip(svc.read([...root, "missing", "value"])) + expect(error).toBeInstanceOf(Storage.NotFoundError) + expect(error._tag).toBe("NotFoundError") + expect(error.message).toContain(path.join(...root, "missing", "value") + ".json") }), ) it.live("update on missing key throws NotFoundError", () => Effect.gen(function* () { const { root, svc } = yield* scope() - const exit = yield* svc - .update<{ value: number }>([...root, "missing", "key"], (draft) => { + const error = yield* Effect.flip( + svc.update<{ value: number }>([...root, "missing", "key"], (draft) => { draft.value += 1 - }) - .pipe(Effect.exit) - expect(Exit.isFailure(exit)).toBe(true) + }), + ) + expect(error).toBeInstanceOf(Storage.NotFoundError) + expect(error._tag).toBe("NotFoundError") }), ) diff --git a/packages/opencode/test/sync/index.test.ts b/packages/opencode/test/sync/index.test.ts index aa26fab8a6a..c4e5b86062b 100644 --- a/packages/opencode/test/sync/index.test.ts +++ b/packages/opencode/test/sync/index.test.ts @@ -1,27 +1,25 @@ -import { describe, expect, beforeEach, afterEach, afterAll } from "bun:test" +import { describe, expect, beforeEach, afterAll } from "bun:test" import { provideTmpdirInstance } from "../fixture/fixture" import { Effect, Layer, Schema } from "effect" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { Bus } from "../../src/bus" import { SyncEvent } from "../../src/sync" -import { Database } from "@/storage/db" +import { Database, eq } from "@/storage/db" import { EventSequenceTable, EventTable } from "../../src/sync/event.sql" import { MessageID } from "../../src/session/schema" -import { Flag } from "@opencode-ai/core/flag/flag" import { initProjectors } from "../../src/server/projectors" import { testEffect } from "../lib/effect" +import { RuntimeFlags } from "@/effect/runtime-flags" -const original = Flag.KILO_EXPERIMENTAL_WORKSPACES -const it = testEffect(Layer.mergeAll(SyncEvent.defaultLayer, CrossSpawnSpawner.defaultLayer)) +const it = testEffect( + Layer.mergeAll( + SyncEvent.layer.pipe(Layer.provide(RuntimeFlags.layer({ experimentalWorkspaces: true }))), + CrossSpawnSpawner.defaultLayer, + ), +) beforeEach(() => { Database.close() - - Flag.KILO_EXPERIMENTAL_WORKSPACES = true -}) - -afterEach(() => { - Flag.KILO_EXPERIMENTAL_WORKSPACES = original }) describe("SyncEvent", () => { @@ -323,5 +321,28 @@ describe("SyncEvent", () => { }), ), ) + + it.live( + "claim updates the event sequence owner", + provideTmpdirInstance(() => + Effect.gen(function* () { + const { Created } = setup() + const id = MessageID.ascending() + + yield* SyncEvent.use.run(Created, { id, name: "claimed" }, { publish: false }) + yield* SyncEvent.use.claim(id, "owner-1") + yield* SyncEvent.use.claim(id, "owner-2") + + const row = Database.use((db) => + db + .select({ seq: EventSequenceTable.seq, ownerID: EventSequenceTable.owner_id }) + .from(EventSequenceTable) + .where(eq(EventSequenceTable.aggregate_id, id)) + .get(), + ) + expect(row).toEqual({ seq: 0, ownerID: "owner-2" }) + }), + ), + ) }) }) diff --git a/packages/opencode/test/tool/__snapshots__/parameters.test.ts.snap b/packages/opencode/test/tool/__snapshots__/parameters.test.ts.snap index d72487a9c05..6953bf4adde 100644 --- a/packages/opencode/test/tool/__snapshots__/parameters.test.ts.snap +++ b/packages/opencode/test/tool/__snapshots__/parameters.test.ts.snap @@ -45,6 +45,7 @@ Output: Creates directory 'foo'" "description": "Optional timeout in milliseconds", "exclusiveMinimum": 0, "maximum": 9007199254740991, + "minimum": -9007199254740991, "type": "integer", }, "workdir": { @@ -260,7 +261,6 @@ exports[`tool parameters JSON Schema (wire shape) question 1`] = ` "type": "string", }, }, - "ref": "QuestionOption", "required": [ "label", "description", @@ -278,7 +278,6 @@ exports[`tool parameters JSON Schema (wire shape) question 1`] = ` "type": "string", }, }, - "ref": "QuestionPrompt", "required": [ "question", "header", @@ -344,6 +343,10 @@ exports[`tool parameters JSON Schema (wire shape) task 1`] = ` { "$schema": "https://json-schema.org/draft/2020-12/schema", "properties": { + "background": { + "description": "When true, launch the subagent in the background and return immediately", + "type": "boolean", + }, "command": { "description": "The command that triggered this task", "type": "string", @@ -417,14 +420,21 @@ exports[`tool parameters JSON Schema (wire shape) webfetch 1`] = ` "$schema": "https://json-schema.org/draft/2020-12/schema", "properties": { "format": { - "default": "markdown", - "description": "The format to return the content in (text, markdown, or html). Defaults to markdown.", - "enum": [ - "text", - "markdown", - "html", + "anyOf": [ + { + "default": "markdown", + "description": "The format to return the content in (text, markdown, or html). Defaults to markdown.", + "enum": [ + "text", + "markdown", + "html", + ], + "type": "string", + }, + { + "type": "null", + }, ], - "type": "string", }, "timeout": { "description": "Optional timeout in seconds (max 120)", diff --git a/packages/opencode/test/tool/apply_patch.test.ts b/packages/opencode/test/tool/apply_patch.test.ts index 9d1d6374798..cd58c248b94 100644 --- a/packages/opencode/test/tool/apply_patch.test.ts +++ b/packages/opencode/test/tool/apply_patch.test.ts @@ -1,20 +1,19 @@ -import { describe, expect, test } from "bun:test" +import { describe, expect } from "bun:test" import path from "path" import * as fs from "fs/promises" -import { Effect, ManagedRuntime, Layer } from "effect" +import { Cause, Effect, Exit, Layer } from "effect" import { ApplyPatchTool } from "../../src/tool/apply_patch" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" import { LSP } from "@/lsp/lsp" import { AppFileSystem } from "@opencode-ai/core/filesystem" import { Format } from "../../src/format" import { Agent } from "../../src/agent/agent" import { Bus } from "../../src/bus" import { Truncate } from "@/tool/truncate" -import { tmpdir } from "../fixture/fixture" +import { TestInstance } from "../fixture/fixture" import { SessionID, MessageID } from "../../src/session/schema" +import { testEffect } from "../lib/effect" -const runtime = ManagedRuntime.make( +const it = testEffect( Layer.mergeAll( LSP.defaultLayer, AppFileSystem.defaultLayer, @@ -58,11 +57,11 @@ type ToolCtx = typeof baseCtx & { ask: (input: AskInput) => Effect.Effect } -const execute = async (params: { patchText: string }, ctx: ToolCtx) => { - const info = await runtime.runPromise(ApplyPatchTool) - const tool = await runtime.runPromise(info.init()) - return Effect.runPromise(tool.execute(params, ctx)) -} +const execute = Effect.fn("ApplyPatchToolTest.execute")(function* (params: { patchText: string }, ctx: ToolCtx) { + const info = yield* ApplyPatchTool + const tool = yield* info.init() + return yield* tool.execute(params, ctx) +}) const makeCtx = () => { const calls: AskInput[] = [] @@ -77,39 +76,56 @@ const makeCtx = () => { return { ctx, calls } } -describe("tool.apply_patch freeform", () => { - test("requires patchText", async () => { - const { ctx } = makeCtx() - await expect(execute({ patchText: "" }, ctx)).rejects.toThrow("patchText is required") - }) - - test("rejects invalid patch format", async () => { - const { ctx } = makeCtx() - await expect(execute({ patchText: "invalid patch" }, ctx)).rejects.toThrow("apply_patch verification failed") - }) +const readText = (filepath: string) => Effect.promise(() => fs.readFile(filepath, "utf-8")) +const writeText = (filepath: string, content: string) => Effect.promise(() => fs.writeFile(filepath, content, "utf-8")) +const makeDir = (dir: string) => Effect.promise(() => fs.mkdir(dir, { recursive: true })) - test("rejects empty patch", async () => { - const { ctx } = makeCtx() - const emptyPatch = "*** Begin Patch\n*** End Patch" - await expect(execute({ patchText: emptyPatch }, ctx)).rejects.toThrow("patch rejected: empty patch") +const expectFailure = (effect: Effect.Effect, message?: string) => + Effect.gen(function* () { + const exit = yield* Effect.exit(effect) + expect(Exit.isFailure(exit)).toBe(true) + if (Exit.isFailure(exit) && message) expect(Cause.pretty(exit.cause)).toContain(message) }) - test("applies add/update/delete in one patch", async () => { - await using fixture = await tmpdir({ git: true }) - const { ctx, calls } = makeCtx() +const expectReadFailure = (filepath: string) => expectFailure(readText(filepath)) - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const modifyPath = path.join(fixture.path, "modify.txt") - const deletePath = path.join(fixture.path, "delete.txt") - await fs.writeFile(modifyPath, "line1\nline2\n", "utf-8") - await fs.writeFile(deletePath, "obsolete\n", "utf-8") +describe("tool.apply_patch freeform", () => { + it.live("requires patchText", () => + Effect.gen(function* () { + const { ctx } = makeCtx() + yield* expectFailure(execute({ patchText: "" }, ctx), "patchText is required") + }), + ) + + it.live("rejects invalid patch format", () => + Effect.gen(function* () { + const { ctx } = makeCtx() + yield* expectFailure(execute({ patchText: "invalid patch" }, ctx), "apply_patch verification failed") + }), + ) + + it.live("rejects empty patch", () => + Effect.gen(function* () { + const { ctx } = makeCtx() + yield* expectFailure(execute({ patchText: "*** Begin Patch\n*** End Patch" }, ctx), "patch rejected: empty patch") + }), + ) + + it.instance( + "applies add/update/delete in one patch", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx, calls } = makeCtx() + const modifyPath = path.join(test.directory, "modify.txt") + const deletePath = path.join(test.directory, "delete.txt") + yield* writeText(modifyPath, "line1\nline2\n") + yield* writeText(deletePath, "obsolete\n") const patchText = "*** Begin Patch\n*** Add File: nested/new.txt\n+created\n*** Delete File: delete.txt\n*** Update File: modify.txt\n@@\n-line2\n+changed\n*** End Patch" - const result = await execute({ patchText }, ctx) + const result = yield* execute({ patchText }, ctx) expect(result.title).toContain("Success. Updated the following files") expect(result.output).toContain("Success. Updated the following files") @@ -129,38 +145,34 @@ describe("tool.apply_patch freeform", () => { expect(permissionCall.metadata.files.map((f) => f.type).sort()).toEqual(["add", "delete", "update"]) const addFile = permissionCall.metadata.files.find((f) => f.type === "add") - expect(addFile).toBeDefined() - expect(addFile!.relativePath).toBe("nested/new.txt") - expect(addFile!.patch).toContain("+created") + expect(addFile?.relativePath).toBe("nested/new.txt") + expect(addFile?.patch).toContain("+created") const updateFile = permissionCall.metadata.files.find((f) => f.type === "update") - expect(updateFile).toBeDefined() - expect(updateFile!.patch).toContain("-line2") - expect(updateFile!.patch).toContain("+changed") - - const added = await fs.readFile(path.join(fixture.path, "nested", "new.txt"), "utf-8") - expect(added).toBe("created\n") - expect(await fs.readFile(modifyPath, "utf-8")).toBe("line1\nchanged\n") - await expect(fs.readFile(deletePath, "utf-8")).rejects.toThrow() - }, - }) - }) + expect(updateFile?.patch).toContain("-line2") + expect(updateFile?.patch).toContain("+changed") - test("permission metadata includes move file info", async () => { - await using fixture = await tmpdir({ git: true }) - const { ctx, calls } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const original = path.join(fixture.path, "old", "name.txt") - await fs.mkdir(path.dirname(original), { recursive: true }) - await fs.writeFile(original, "old content\n", "utf-8") + expect(yield* readText(path.join(test.directory, "nested", "new.txt"))).toBe("created\n") + expect(yield* readText(modifyPath)).toBe("line1\nchanged\n") + yield* expectReadFailure(deletePath) + }), + { git: true }, + ) + + it.instance( + "permission metadata includes move file info", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx, calls } = makeCtx() + const original = path.join(test.directory, "old", "name.txt") + yield* makeDir(path.dirname(original)) + yield* writeText(original, "old content\n") const patchText = "*** Begin Patch\n*** Update File: old/name.txt\n*** Move to: renamed/dir/name.txt\n@@\n-old content\n+new content\n*** End Patch" - await execute({ patchText }, ctx) + yield* execute({ patchText }, ctx) expect(calls.length).toBe(1) const permissionCall = calls[0] @@ -169,447 +181,353 @@ describe("tool.apply_patch freeform", () => { const moveFile = permissionCall.metadata.files[0] expect(moveFile.type).toBe("move") expect(moveFile.relativePath).toBe("renamed/dir/name.txt") - expect(moveFile.movePath).toBe(path.join(fixture.path, "renamed/dir/name.txt")) + expect(moveFile.movePath).toBe(path.join(test.directory, "renamed/dir/name.txt")) expect(moveFile.patch).toContain("-old content") expect(moveFile.patch).toContain("+new content") - }, - }) - }) - - test("applies multiple hunks to one file", async () => { - await using fixture = await tmpdir() - const { ctx } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const target = path.join(fixture.path, "multi.txt") - await fs.writeFile(target, "line1\nline2\nline3\nline4\n", "utf-8") - - const patchText = - "*** Begin Patch\n*** Update File: multi.txt\n@@\n-line2\n+changed2\n@@\n-line4\n+changed4\n*** End Patch" - - await execute({ patchText }, ctx) - - expect(await fs.readFile(target, "utf-8")).toBe("line1\nchanged2\nline3\nchanged4\n") - }, - }) - }) - - test("does not invent a first-line diff for BOM files", async () => { - await using fixture = await tmpdir() - const { ctx, calls } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const bom = String.fromCharCode(0xfeff) - const target = path.join(fixture.path, "example.cs") - await fs.writeFile(target, `${bom}using System;\n\nclass Test {}\n`, "utf-8") - - const patchText = - "*** Begin Patch\n*** Update File: example.cs\n@@\n class Test {}\n+class Next {}\n*** End Patch" - - await execute({ patchText }, ctx) - - expect(calls.length).toBe(1) - const shown = calls[0].metadata.files[0]?.patch ?? "" - expect(shown).not.toContain(bom) - expect(shown).not.toContain("-using System;") - expect(shown).not.toContain("+using System;") - - const content = await fs.readFile(target, "utf-8") - expect(content.charCodeAt(0)).toBe(0xfeff) - expect(content.slice(1)).toBe("using System;\n\nclass Test {}\nclass Next {}\n") - }, - }) - }) - - test("inserts lines with insert-only hunk", async () => { - await using fixture = await tmpdir() - const { ctx } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const target = path.join(fixture.path, "insert_only.txt") - await fs.writeFile(target, "alpha\nomega\n", "utf-8") - - const patchText = "*** Begin Patch\n*** Update File: insert_only.txt\n@@\n alpha\n+beta\n omega\n*** End Patch" - - await execute({ patchText }, ctx) - - expect(await fs.readFile(target, "utf-8")).toBe("alpha\nbeta\nomega\n") - }, - }) - }) - - test("appends trailing newline on update", async () => { - await using fixture = await tmpdir() - const { ctx } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const target = path.join(fixture.path, "no_newline.txt") - await fs.writeFile(target, "no newline at end", "utf-8") - - const patchText = - "*** Begin Patch\n*** Update File: no_newline.txt\n@@\n-no newline at end\n+first line\n+second line\n*** End Patch" - - await execute({ patchText }, ctx) - - const contents = await fs.readFile(target, "utf-8") - expect(contents.endsWith("\n")).toBe(true) - expect(contents).toBe("first line\nsecond line\n") - }, - }) - }) - - test("moves file to a new directory", async () => { - await using fixture = await tmpdir() - const { ctx } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const original = path.join(fixture.path, "old", "name.txt") - await fs.mkdir(path.dirname(original), { recursive: true }) - await fs.writeFile(original, "old content\n", "utf-8") - - const patchText = - "*** Begin Patch\n*** Update File: old/name.txt\n*** Move to: renamed/dir/name.txt\n@@\n-old content\n+new content\n*** End Patch" - - await execute({ patchText }, ctx) - - const moved = path.join(fixture.path, "renamed", "dir", "name.txt") - await expect(fs.readFile(original, "utf-8")).rejects.toThrow() - expect(await fs.readFile(moved, "utf-8")).toBe("new content\n") - }, - }) - }) - - test("moves file overwriting existing destination", async () => { - await using fixture = await tmpdir() - const { ctx } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const original = path.join(fixture.path, "old", "name.txt") - const destination = path.join(fixture.path, "renamed", "dir", "name.txt") - await fs.mkdir(path.dirname(original), { recursive: true }) - await fs.mkdir(path.dirname(destination), { recursive: true }) - await fs.writeFile(original, "from\n", "utf-8") - await fs.writeFile(destination, "existing\n", "utf-8") - - const patchText = - "*** Begin Patch\n*** Update File: old/name.txt\n*** Move to: renamed/dir/name.txt\n@@\n-from\n+new\n*** End Patch" - - await execute({ patchText }, ctx) - - await expect(fs.readFile(original, "utf-8")).rejects.toThrow() - expect(await fs.readFile(destination, "utf-8")).toBe("new\n") - }, - }) - }) - - test("adds file overwriting existing file", async () => { - await using fixture = await tmpdir() - const { ctx } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const target = path.join(fixture.path, "duplicate.txt") - await fs.writeFile(target, "old content\n", "utf-8") - - const patchText = "*** Begin Patch\n*** Add File: duplicate.txt\n+new content\n*** End Patch" - - await execute({ patchText }, ctx) - expect(await fs.readFile(target, "utf-8")).toBe("new content\n") - }, - }) - }) - - test("rejects update when target file is missing", async () => { - await using fixture = await tmpdir() - const { ctx } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const patchText = "*** Begin Patch\n*** Update File: missing.txt\n@@\n-nope\n+better\n*** End Patch" - - await expect(execute({ patchText }, ctx)).rejects.toThrow( - "apply_patch verification failed: Failed to read file to update", - ) - }, - }) - }) - - test("rejects delete when file is missing", async () => { - await using fixture = await tmpdir() - const { ctx } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const patchText = "*** Begin Patch\n*** Delete File: missing.txt\n*** End Patch" - - await expect(execute({ patchText }, ctx)).rejects.toThrow() - }, - }) - }) - - test("rejects delete when target is a directory", async () => { - await using fixture = await tmpdir() - const { ctx } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const dirPath = path.join(fixture.path, "dir") - await fs.mkdir(dirPath) - - const patchText = "*** Begin Patch\n*** Delete File: dir\n*** End Patch" - - await expect(execute({ patchText }, ctx)).rejects.toThrow() - }, - }) - }) - - test("rejects invalid hunk header", async () => { - await using fixture = await tmpdir() - const { ctx } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const patchText = "*** Begin Patch\n*** Frobnicate File: foo\n*** End Patch" - - await expect(execute({ patchText }, ctx)).rejects.toThrow("apply_patch verification failed") - }, - }) - }) - - test("rejects update with missing context", async () => { - await using fixture = await tmpdir() - const { ctx } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const target = path.join(fixture.path, "modify.txt") - await fs.writeFile(target, "line1\nline2\n", "utf-8") - - const patchText = "*** Begin Patch\n*** Update File: modify.txt\n@@\n-missing\n+changed\n*** End Patch" - - await expect(execute({ patchText }, ctx)).rejects.toThrow("apply_patch verification failed") - expect(await fs.readFile(target, "utf-8")).toBe("line1\nline2\n") - }, - }) - }) - - test("verification failure leaves no side effects", async () => { - await using fixture = await tmpdir() - const { ctx } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const patchText = - "*** Begin Patch\n*** Add File: created.txt\n+hello\n*** Update File: missing.txt\n@@\n-old\n+new\n*** End Patch" - - await expect(execute({ patchText }, ctx)).rejects.toThrow() - - const createdPath = path.join(fixture.path, "created.txt") - await expect(fs.readFile(createdPath, "utf-8")).rejects.toThrow() - }, - }) - }) - - test("supports end of file anchor", async () => { - await using fixture = await tmpdir() - const { ctx } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const target = path.join(fixture.path, "tail.txt") - await fs.writeFile(target, "alpha\nlast\n", "utf-8") - - const patchText = "*** Begin Patch\n*** Update File: tail.txt\n@@\n-last\n+end\n*** End of File\n*** End Patch" - - await execute({ patchText }, ctx) - expect(await fs.readFile(target, "utf-8")).toBe("alpha\nend\n") - }, - }) - }) - - test("rejects missing second chunk context", async () => { - await using fixture = await tmpdir() - const { ctx } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const target = path.join(fixture.path, "two_chunks.txt") - await fs.writeFile(target, "a\nb\nc\nd\n", "utf-8") - - const patchText = "*** Begin Patch\n*** Update File: two_chunks.txt\n@@\n-b\n+B\n\n-d\n+D\n*** End Patch" - - await expect(execute({ patchText }, ctx)).rejects.toThrow() - expect(await fs.readFile(target, "utf-8")).toBe("a\nb\nc\nd\n") - }, - }) - }) - - test("disambiguates change context with @@ header", async () => { - await using fixture = await tmpdir() - const { ctx } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const target = path.join(fixture.path, "multi_ctx.txt") - await fs.writeFile(target, "fn a\nx=10\ny=2\nfn b\nx=10\ny=20\n", "utf-8") - - const patchText = "*** Begin Patch\n*** Update File: multi_ctx.txt\n@@ fn b\n-x=10\n+x=11\n*** End Patch" - - await execute({ patchText }, ctx) - expect(await fs.readFile(target, "utf-8")).toBe("fn a\nx=10\ny=2\nfn b\nx=11\ny=20\n") - }, - }) - }) - - test("EOF anchor matches from end of file first", async () => { - await using fixture = await tmpdir() - const { ctx } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const target = path.join(fixture.path, "eof_anchor.txt") - // File has duplicate "marker" lines - one in middle, one at end - await fs.writeFile(target, "start\nmarker\nmiddle\nmarker\nend\n", "utf-8") - - // With EOF anchor, should match the LAST "marker" line, not the first - const patchText = - "*** Begin Patch\n*** Update File: eof_anchor.txt\n@@\n-marker\n-end\n+marker-changed\n+end\n*** End of File\n*** End Patch" - - await execute({ patchText }, ctx) - // First marker unchanged, second marker changed - expect(await fs.readFile(target, "utf-8")).toBe("start\nmarker\nmiddle\nmarker-changed\nend\n") - }, - }) - }) - - test("parses heredoc-wrapped patch", async () => { - await using fixture = await tmpdir() - const { ctx } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const patchText = `cat <<'EOF' + }), + { git: true }, + ) + + it.instance("applies multiple hunks to one file", () => + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx } = makeCtx() + const target = path.join(test.directory, "multi.txt") + yield* writeText(target, "line1\nline2\nline3\nline4\n") + + const patchText = + "*** Begin Patch\n*** Update File: multi.txt\n@@\n-line2\n+changed2\n@@\n-line4\n+changed4\n*** End Patch" + + yield* execute({ patchText }, ctx) + + expect(yield* readText(target)).toBe("line1\nchanged2\nline3\nchanged4\n") + }), + ) + + it.instance("does not invent a first-line diff for BOM files", () => + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx, calls } = makeCtx() + const bom = String.fromCharCode(0xfeff) + const target = path.join(test.directory, "example.cs") + yield* writeText(target, `${bom}using System;\n\nclass Test {}\n`) + + const patchText = + "*** Begin Patch\n*** Update File: example.cs\n@@\n class Test {}\n+class Next {}\n*** End Patch" + + yield* execute({ patchText }, ctx) + + expect(calls.length).toBe(1) + const shown = calls[0].metadata.files[0]?.patch ?? "" + expect(shown).not.toContain(bom) + expect(shown).not.toContain("-using System;") + expect(shown).not.toContain("+using System;") + + const content = yield* readText(target) + expect(content.charCodeAt(0)).toBe(0xfeff) + expect(content.slice(1)).toBe("using System;\n\nclass Test {}\nclass Next {}\n") + }), + ) + + it.instance("inserts lines with insert-only hunk", () => + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx } = makeCtx() + const target = path.join(test.directory, "insert_only.txt") + yield* writeText(target, "alpha\nomega\n") + + const patchText = "*** Begin Patch\n*** Update File: insert_only.txt\n@@\n alpha\n+beta\n omega\n*** End Patch" + + yield* execute({ patchText }, ctx) + + expect(yield* readText(target)).toBe("alpha\nbeta\nomega\n") + }), + ) + + it.instance("appends trailing newline on update", () => + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx } = makeCtx() + const target = path.join(test.directory, "no_newline.txt") + yield* writeText(target, "no newline at end") + + const patchText = + "*** Begin Patch\n*** Update File: no_newline.txt\n@@\n-no newline at end\n+first line\n+second line\n*** End Patch" + + yield* execute({ patchText }, ctx) + + const contents = yield* readText(target) + expect(contents.endsWith("\n")).toBe(true) + expect(contents).toBe("first line\nsecond line\n") + }), + ) + + it.instance("moves file to a new directory", () => + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx } = makeCtx() + const original = path.join(test.directory, "old", "name.txt") + yield* makeDir(path.dirname(original)) + yield* writeText(original, "old content\n") + + const patchText = + "*** Begin Patch\n*** Update File: old/name.txt\n*** Move to: renamed/dir/name.txt\n@@\n-old content\n+new content\n*** End Patch" + + yield* execute({ patchText }, ctx) + + const moved = path.join(test.directory, "renamed", "dir", "name.txt") + yield* expectReadFailure(original) + expect(yield* readText(moved)).toBe("new content\n") + }), + ) + + it.instance("moves file overwriting existing destination", () => + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx } = makeCtx() + const original = path.join(test.directory, "old", "name.txt") + const destination = path.join(test.directory, "renamed", "dir", "name.txt") + yield* makeDir(path.dirname(original)) + yield* makeDir(path.dirname(destination)) + yield* writeText(original, "from\n") + yield* writeText(destination, "existing\n") + + const patchText = + "*** Begin Patch\n*** Update File: old/name.txt\n*** Move to: renamed/dir/name.txt\n@@\n-from\n+new\n*** End Patch" + + yield* execute({ patchText }, ctx) + + yield* expectReadFailure(original) + expect(yield* readText(destination)).toBe("new\n") + }), + ) + + it.instance("adds file overwriting existing file", () => + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx } = makeCtx() + const target = path.join(test.directory, "duplicate.txt") + yield* writeText(target, "old content\n") + + const patchText = "*** Begin Patch\n*** Add File: duplicate.txt\n+new content\n*** End Patch" + + yield* execute({ patchText }, ctx) + expect(yield* readText(target)).toBe("new content\n") + }), + ) + + it.instance("rejects update when target file is missing", () => + Effect.gen(function* () { + const { ctx } = makeCtx() + const patchText = "*** Begin Patch\n*** Update File: missing.txt\n@@\n-nope\n+better\n*** End Patch" + + yield* expectFailure( + execute({ patchText }, ctx), + "apply_patch verification failed: Failed to read file to update", + ) + }), + ) + + it.instance("rejects delete when file is missing", () => + Effect.gen(function* () { + const { ctx } = makeCtx() + const patchText = "*** Begin Patch\n*** Delete File: missing.txt\n*** End Patch" + + yield* expectFailure(execute({ patchText }, ctx)) + }), + ) + + it.instance("rejects delete when target is a directory", () => + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx } = makeCtx() + const dirPath = path.join(test.directory, "dir") + yield* makeDir(dirPath) + + const patchText = "*** Begin Patch\n*** Delete File: dir\n*** End Patch" + + yield* expectFailure(execute({ patchText }, ctx)) + }), + ) + + it.instance("rejects invalid hunk header", () => + Effect.gen(function* () { + const { ctx } = makeCtx() + const patchText = "*** Begin Patch\n*** Frobnicate File: foo\n*** End Patch" + + yield* expectFailure(execute({ patchText }, ctx), "apply_patch verification failed") + }), + ) + + it.instance("rejects update with missing context", () => + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx } = makeCtx() + const target = path.join(test.directory, "modify.txt") + yield* writeText(target, "line1\nline2\n") + + const patchText = "*** Begin Patch\n*** Update File: modify.txt\n@@\n-missing\n+changed\n*** End Patch" + + yield* expectFailure(execute({ patchText }, ctx), "apply_patch verification failed") + expect(yield* readText(target)).toBe("line1\nline2\n") + }), + ) + + it.instance("verification failure leaves no side effects", () => + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx } = makeCtx() + const patchText = + "*** Begin Patch\n*** Add File: created.txt\n+hello\n*** Update File: missing.txt\n@@\n-old\n+new\n*** End Patch" + + yield* expectFailure(execute({ patchText }, ctx)) + yield* expectReadFailure(path.join(test.directory, "created.txt")) + }), + ) + + it.instance("supports end of file anchor", () => + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx } = makeCtx() + const target = path.join(test.directory, "tail.txt") + yield* writeText(target, "alpha\nlast\n") + + const patchText = "*** Begin Patch\n*** Update File: tail.txt\n@@\n-last\n+end\n*** End of File\n*** End Patch" + + yield* execute({ patchText }, ctx) + expect(yield* readText(target)).toBe("alpha\nend\n") + }), + ) + + it.instance("rejects missing second chunk context", () => + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx } = makeCtx() + const target = path.join(test.directory, "two_chunks.txt") + yield* writeText(target, "a\nb\nc\nd\n") + + const patchText = "*** Begin Patch\n*** Update File: two_chunks.txt\n@@\n-b\n+B\n\n-d\n+D\n*** End Patch" + + yield* expectFailure(execute({ patchText }, ctx)) + expect(yield* readText(target)).toBe("a\nb\nc\nd\n") + }), + ) + + it.instance("disambiguates change context with @@ header", () => + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx } = makeCtx() + const target = path.join(test.directory, "multi_ctx.txt") + yield* writeText(target, "fn a\nx=10\ny=2\nfn b\nx=10\ny=20\n") + + const patchText = "*** Begin Patch\n*** Update File: multi_ctx.txt\n@@ fn b\n-x=10\n+x=11\n*** End Patch" + + yield* execute({ patchText }, ctx) + expect(yield* readText(target)).toBe("fn a\nx=10\ny=2\nfn b\nx=11\ny=20\n") + }), + ) + + it.instance("EOF anchor matches from end of file first", () => + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx } = makeCtx() + const target = path.join(test.directory, "eof_anchor.txt") + // File has duplicate "marker" lines - one in middle, one at end + yield* writeText(target, "start\nmarker\nmiddle\nmarker\nend\n") + + // With EOF anchor, should match the LAST "marker" line, not the first + const patchText = + "*** Begin Patch\n*** Update File: eof_anchor.txt\n@@\n-marker\n-end\n+marker-changed\n+end\n*** End of File\n*** End Patch" + + yield* execute({ patchText }, ctx) + // First marker unchanged, second marker changed + expect(yield* readText(target)).toBe("start\nmarker\nmiddle\nmarker-changed\nend\n") + }), + ) + + it.instance("parses heredoc-wrapped patch", () => + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx } = makeCtx() + const patchText = `cat <<'EOF' *** Begin Patch *** Add File: heredoc_test.txt +heredoc content *** End Patch EOF` - await execute({ patchText }, ctx) - const content = await fs.readFile(path.join(fixture.path, "heredoc_test.txt"), "utf-8") - expect(content).toBe("heredoc content\n") - }, - }) - }) - - test("parses heredoc-wrapped patch without cat", async () => { - await using fixture = await tmpdir() - const { ctx } = makeCtx() + yield* execute({ patchText }, ctx) + expect(yield* readText(path.join(test.directory, "heredoc_test.txt"))).toBe("heredoc content\n") + }), + ) - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const patchText = `< + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx } = makeCtx() + const patchText = `< { - await using fixture = await tmpdir() - const { ctx } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const target = path.join(fixture.path, "trailing_ws.txt") - // File has trailing spaces on some lines - await fs.writeFile(target, "line1 \nline2\nline3 \n", "utf-8") - - // Patch doesn't have trailing spaces - should still match via rstrip pass - const patchText = "*** Begin Patch\n*** Update File: trailing_ws.txt\n@@\n-line2\n+changed\n*** End Patch" - - await execute({ patchText }, ctx) - expect(await fs.readFile(target, "utf-8")).toBe("line1 \nchanged\nline3 \n") - }, - }) - }) - - test("matches with leading whitespace differences", async () => { - await using fixture = await tmpdir() - const { ctx } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const target = path.join(fixture.path, "leading_ws.txt") - // File has leading spaces - await fs.writeFile(target, " line1\nline2\n line3\n", "utf-8") - - // Patch without leading spaces - should match via trim pass - const patchText = "*** Begin Patch\n*** Update File: leading_ws.txt\n@@\n-line2\n+changed\n*** End Patch" - - await execute({ patchText }, ctx) - expect(await fs.readFile(target, "utf-8")).toBe(" line1\nchanged\n line3\n") - }, - }) - }) - - test("matches with Unicode punctuation differences", async () => { - await using fixture = await tmpdir() - const { ctx } = makeCtx() - - await WithInstance.provide({ - directory: fixture.path, - fn: async () => { - const target = path.join(fixture.path, "unicode.txt") - // File has fancy Unicode quotes (U+201C, U+201D) and em-dash (U+2014) - const leftQuote = "\u201C" - const rightQuote = "\u201D" - const emDash = "\u2014" - await fs.writeFile(target, `He said ${leftQuote}hello${rightQuote}\nsome${emDash}dash\nend\n`, "utf-8") - - // Patch uses ASCII equivalents - should match via normalized pass - // The replacement uses ASCII quotes from the patch (not preserving Unicode) - const patchText = - '*** Begin Patch\n*** Update File: unicode.txt\n@@\n-He said "hello"\n+He said "hi"\n*** End Patch' - - await execute({ patchText }, ctx) - // Result has ASCII quotes because that's what the patch specifies - expect(await fs.readFile(target, "utf-8")).toBe(`He said "hi"\nsome${emDash}dash\nend\n`) - }, - }) - }) + yield* execute({ patchText }, ctx) + expect(yield* readText(path.join(test.directory, "heredoc_no_cat.txt"))).toBe("no cat prefix\n") + }), + ) + + it.instance("matches with trailing whitespace differences", () => + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx } = makeCtx() + const target = path.join(test.directory, "trailing_ws.txt") + // File has trailing spaces on some lines + yield* writeText(target, "line1 \nline2\nline3 \n") + + // Patch doesn't have trailing spaces - should still match via rstrip pass + const patchText = "*** Begin Patch\n*** Update File: trailing_ws.txt\n@@\n-line2\n+changed\n*** End Patch" + + yield* execute({ patchText }, ctx) + expect(yield* readText(target)).toBe("line1 \nchanged\nline3 \n") + }), + ) + + it.instance("matches with leading whitespace differences", () => + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx } = makeCtx() + const target = path.join(test.directory, "leading_ws.txt") + // File has leading spaces + yield* writeText(target, " line1\nline2\n line3\n") + + // Patch without leading spaces - should match via trim pass + const patchText = "*** Begin Patch\n*** Update File: leading_ws.txt\n@@\n-line2\n+changed\n*** End Patch" + + yield* execute({ patchText }, ctx) + expect(yield* readText(target)).toBe(" line1\nchanged\n line3\n") + }), + ) + + it.instance("matches with Unicode punctuation differences", () => + Effect.gen(function* () { + const test = yield* TestInstance + const { ctx } = makeCtx() + const target = path.join(test.directory, "unicode.txt") + // File has fancy Unicode quotes (U+201C, U+201D) and em-dash (U+2014) + const leftQuote = "\u201C" + const rightQuote = "\u201D" + const emDash = "\u2014" + yield* writeText(target, `He said ${leftQuote}hello${rightQuote}\nsome${emDash}dash\nend\n`) + + // Patch uses ASCII equivalents - should match via normalized pass + // The replacement uses ASCII quotes from the patch (not preserving Unicode) + const patchText = + '*** Begin Patch\n*** Update File: unicode.txt\n@@\n-He said "hello"\n+He said "hi"\n*** End Patch' + + yield* execute({ patchText }, ctx) + // Result has ASCII quotes because that's what the patch specifies + expect(yield* readText(target)).toBe(`He said "hi"\nsome${emDash}dash\nend\n`) + }), + ) }) diff --git a/packages/opencode/test/tool/edit.test.ts b/packages/opencode/test/tool/edit.test.ts index a629ff07d11..3f644ed53dd 100644 --- a/packages/opencode/test/tool/edit.test.ts +++ b/packages/opencode/test/tool/edit.test.ts @@ -1,19 +1,19 @@ -import { afterAll, afterEach, describe, test, expect } from "bun:test" +import { afterEach, describe, expect } from "bun:test" import path from "path" import fs from "fs/promises" -import { Effect, Layer, ManagedRuntime } from "effect" +import { Cause, Deferred, Effect, Exit, Fiber, Layer } from "effect" import { EditTool } from "../../src/tool/edit" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" -import { disposeAllInstances, tmpdir } from "../fixture/fixture" +import { disposeAllInstances, TestInstance } from "../fixture/fixture" import { LSP } from "@/lsp/lsp" import { AppFileSystem } from "@opencode-ai/core/filesystem" import { Format } from "../../src/format" import { Agent } from "../../src/agent/agent" import { Bus } from "../../src/bus" -import { BusEvent } from "../../src/bus/bus-event" import { Truncate } from "@/tool/truncate" import { SessionID, MessageID } from "../../src/session/schema" +import * as Tool from "../../src/tool/tool" +import { testEffect } from "../lib/effect" +import { FileWatcher } from "../../src/file/watcher" const ctx = { sessionID: SessionID.make("ses_test-edit-session"), @@ -30,487 +30,269 @@ afterEach(async () => { await disposeAllInstances() }) -const runtime = ManagedRuntime.make( - Layer.mergeAll( - LSP.defaultLayer, - AppFileSystem.defaultLayer, - Format.defaultLayer, - Bus.layer, - Truncate.defaultLayer, - Agent.defaultLayer, - ), +const layer = Layer.mergeAll( + LSP.defaultLayer, + AppFileSystem.defaultLayer, + Format.defaultLayer, + Bus.layer, + Truncate.defaultLayer, + Agent.defaultLayer, ) -afterAll(async () => { - await runtime.dispose() -}) - -const resolve = () => - runtime.runPromise( - Effect.gen(function* () { - const info = yield* EditTool - return yield* info.init() - }), - ) - -const subscribeBus = (def: D, callback: () => unknown) => - runtime.runPromise(Bus.Service.use((bus) => bus.subscribeCallback(def, callback))) - -async function onceBus(def: D) { - const result = Promise.withResolvers() - const unsub = await subscribeBus(def, () => { - unsub() - result.resolve() - }) - return { - wait: result.promise, - unsub, - } -} - -describe("tool.edit", () => { - describe("creating new files", () => { - test("creates new file when oldString is empty", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "newfile.txt") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const edit = await resolve() - const result = await Effect.runPromise( - edit.execute( - { - filePath: filepath, - oldString: "", - newString: "new content", - }, - ctx, - ), - ) - - expect(result.metadata.diff).toContain("new content") - - const content = await fs.readFile(filepath, "utf-8") - expect(content).toBe("new content") - }, - }) - }) +const it = testEffect(layer) - test("preserves BOM when oldString is empty on existing files", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "existing.cs") - const bom = String.fromCharCode(0xfeff) - await fs.writeFile(filepath, `${bom}using System;\n`, "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const edit = await resolve() - const result = await Effect.runPromise( - edit.execute( - { - filePath: filepath, - oldString: "", - newString: "using Up;\n", - }, - ctx, - ), - ) - - expect(result.metadata.diff).toContain("-using System;") - expect(result.metadata.diff).toContain("+using Up;") - - const content = await fs.readFile(filepath, "utf-8") - expect(content.charCodeAt(0)).toBe(0xfeff) - expect(content.slice(1)).toBe("using Up;\n") - }, - }) - }) +const init = Effect.fn("EditToolTest.init")(function* () { + const info = yield* EditTool + return yield* info.init() +}) - test("creates new file with nested directories", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "nested", "dir", "file.txt") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const edit = await resolve() - await Effect.runPromise( - edit.execute( - { - filePath: filepath, - oldString: "", - newString: "nested file", - }, - ctx, - ), - ) - - const content = await fs.readFile(filepath, "utf-8") - expect(content).toBe("nested file") - }, - }) - }) +const run = Effect.fn("EditToolTest.run")(function* ( + args: Tool.InferParameters, + next: Tool.Context = ctx, +) { + const tool = yield* init() + return yield* tool.execute(args, next) +}) - test("emits add event for new files", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "new.txt") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const { FileWatcher } = await import("../../src/file/watcher") - - const updated = await onceBus(FileWatcher.Event.Updated) - - try { - const edit = await resolve() - await Effect.runPromise( - edit.execute( - { - filePath: filepath, - oldString: "", - newString: "content", - }, - ctx, - ), - ) - - await updated.wait - } finally { - updated.unsub() - } - }, - }) - }) - }) +const fail = Effect.fn("EditToolTest.fail")(function* (args: Tool.InferParameters) { + const exit = yield* run(args).pipe(Effect.exit) + if (Exit.isFailure(exit)) { + const err = Cause.squash(exit.cause) + return err instanceof Error ? err : new Error(String(err)) + } + throw new Error("expected edit to fail") +}) - describe("editing existing files", () => { - test("replaces text in existing file", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "existing.txt") - await fs.writeFile(filepath, "old content here", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const edit = await resolve() - const result = await Effect.runPromise( - edit.execute( - { - filePath: filepath, - oldString: "old content", - newString: "new content", - }, - ctx, - ), - ) - - expect(result.output).toContain("Edit applied successfully") - - const content = await fs.readFile(filepath, "utf-8") - expect(content).toBe("new content here") - }, - }) - }) +const put = Effect.fn("EditToolTest.put")(function* (p: string, content: string) { + const fs = yield* AppFileSystem.Service + yield* fs.writeWithDirs(p, content) +}) - test("replaces the first visible line in BOM files", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "existing.cs") - const bom = String.fromCharCode(0xfeff) - await fs.writeFile(filepath, `${bom}using System;\nclass Test {}\n`, "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const edit = await resolve() - const result = await Effect.runPromise( - edit.execute( - { - filePath: filepath, - oldString: "using System;", - newString: "using Up;", - }, - ctx, - ), - ) - - expect(result.metadata.diff).toContain("-using System;") - expect(result.metadata.diff).toContain("+using Up;") - expect(result.metadata.diff).not.toContain(bom) - - const content = await fs.readFile(filepath, "utf-8") - expect(content.charCodeAt(0)).toBe(0xfeff) - expect(content.slice(1)).toBe("using Up;\nclass Test {}\n") - }, - }) - }) +const load = Effect.fn("EditToolTest.load")(function* (p: string) { + const fs = yield* AppFileSystem.Service + return yield* fs.readFileString(p) +}) - test("throws error when file does not exist", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "nonexistent.txt") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const edit = await resolve() - await expect( - Effect.runPromise( - edit.execute( - { - filePath: filepath, - oldString: "old", - newString: "new", - }, - ctx, - ), - ), - ).rejects.toThrow("not found") - }, - }) - }) +const loadRaw = Effect.fn("EditToolTest.loadRaw")(function* (p: string) { + return yield* Effect.promise(() => fs.readFile(p, "utf-8")) +}) - test("throws error when oldString equals newString", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "file.txt") - await fs.writeFile(filepath, "content", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const edit = await resolve() - await expect( - Effect.runPromise( - edit.execute( - { - filePath: filepath, - oldString: "same", - newString: "same", - }, - ctx, - ), - ), - ).rejects.toThrow("identical") - }, - }) - }) +const makeDirectory = Effect.fn("EditToolTest.makeDirectory")(function* (p: string) { + const fs = yield* AppFileSystem.Service + yield* fs.makeDirectory(p) +}) - test("throws error when oldString not found in file", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "file.txt") - await fs.writeFile(filepath, "actual content", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const edit = await resolve() - await expect( - Effect.runPromise( - edit.execute( - { - filePath: filepath, - oldString: "not in file", - newString: "replacement", - }, - ctx, - ), - ), - ).rejects.toThrow() - }, - }) - }) +const onceBus = Effect.fn("EditToolTest.onceBus")(function* (def: typeof FileWatcher.Event.Updated) { + const bus = yield* Bus.Service + const deferred = yield* Deferred.make() + const unsub = yield* bus.subscribeCallback(def, () => Effect.runSync(Deferred.succeed(deferred, undefined))) + yield* Effect.addFinalizer(() => Effect.sync(unsub)) + return deferred +}) - test("replaces all occurrences with replaceAll option", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "file.txt") - await fs.writeFile(filepath, "foo bar foo baz foo", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const edit = await resolve() - await Effect.runPromise( - edit.execute( - { - filePath: filepath, - oldString: "foo", - newString: "qux", - replaceAll: true, - }, - ctx, - ), - ) - - const content = await fs.readFile(filepath, "utf-8") - expect(content).toBe("qux bar qux baz qux") - }, - }) - }) +describe("tool.edit", () => { + describe("creating new files", () => { + it.instance("creates new file when oldString is empty", () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "newfile.txt") + const result = yield* run({ filePath: filepath, oldString: "", newString: "new content" }) + + expect(result.metadata.diff).toContain("new content") + expect(yield* load(filepath)).toBe("new content") + }), + ) + + it.instance("preserves BOM when oldString is empty on existing files", () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "existing.cs") + const bom = String.fromCharCode(0xfeff) + yield* put(filepath, `${bom}using System;\n`) + + const result = yield* run({ filePath: filepath, oldString: "", newString: "using Up;\n" }) + + expect(result.metadata.diff).toContain("-using System;") + expect(result.metadata.diff).toContain("+using Up;") + + const content = yield* loadRaw(filepath) + expect(content.charCodeAt(0)).toBe(0xfeff) + expect(content.slice(1)).toBe("using Up;\n") + }), + ) + + it.instance("creates new file with nested directories", () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "nested", "dir", "file.txt") + + yield* run({ filePath: filepath, oldString: "", newString: "nested file" }) + + expect(yield* load(filepath)).toBe("nested file") + }), + ) + + it.instance("emits add event for new files", () => + Effect.gen(function* () { + const test = yield* TestInstance + const updated = yield* onceBus(FileWatcher.Event.Updated) + + yield* run({ filePath: path.join(test.directory, "new.txt"), oldString: "", newString: "content" }) + yield* Deferred.await(updated) + }), + ) + }) - test("emits change event for existing files", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "file.txt") - await fs.writeFile(filepath, "original", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const { FileWatcher } = await import("../../src/file/watcher") - - const updated = await onceBus(FileWatcher.Event.Updated) - - try { - const edit = await resolve() - await Effect.runPromise( - edit.execute( - { - filePath: filepath, - oldString: "original", - newString: "modified", - }, - ctx, - ), - ) - - await updated.wait - } finally { - updated.unsub() - } - }, - }) - }) + describe("editing existing files", () => { + it.instance("replaces text in existing file", () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "existing.txt") + yield* put(filepath, "old content here") + + const result = yield* run({ filePath: filepath, oldString: "old content", newString: "new content" }) + + expect(result.output).toContain("Edit applied successfully") + expect(yield* load(filepath)).toBe("new content here") + }), + ) + + it.instance("replaces the first visible line in BOM files", () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "existing.cs") + const bom = String.fromCharCode(0xfeff) + yield* put(filepath, `${bom}using System;\nclass Test {}\n`) + + const result = yield* run({ filePath: filepath, oldString: "using System;", newString: "using Up;" }) + + expect(result.metadata.diff).toContain("-using System;") + expect(result.metadata.diff).toContain("+using Up;") + expect(result.metadata.diff).not.toContain(bom) + + const content = yield* loadRaw(filepath) + expect(content.charCodeAt(0)).toBe(0xfeff) + expect(content.slice(1)).toBe("using Up;\nclass Test {}\n") + }), + ) + + it.instance("throws error when file does not exist", () => + Effect.gen(function* () { + const test = yield* TestInstance + expect( + (yield* fail({ filePath: path.join(test.directory, "nonexistent.txt"), oldString: "old", newString: "new" })) + .message, + ).toContain("not found") + }), + ) + + it.instance("throws error when oldString equals newString", () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "file.txt") + yield* put(filepath, "content") + + expect((yield* fail({ filePath: filepath, oldString: "same", newString: "same" })).message).toContain( + "identical", + ) + }), + ) + + it.instance("throws error when oldString not found in file", () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "file.txt") + yield* put(filepath, "actual content") + + expect(yield* fail({ filePath: filepath, oldString: "not in file", newString: "replacement" })).toBeInstanceOf( + Error, + ) + }), + ) + + it.instance("replaces all occurrences with replaceAll option", () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "file.txt") + yield* put(filepath, "foo bar foo baz foo") + + yield* run({ filePath: filepath, oldString: "foo", newString: "qux", replaceAll: true }) + + expect(yield* load(filepath)).toBe("qux bar qux baz qux") + }), + ) + + it.instance("emits change event for existing files", () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "file.txt") + yield* put(filepath, "original") + const updated = yield* onceBus(FileWatcher.Event.Updated) + + yield* run({ filePath: filepath, oldString: "original", newString: "modified" }) + yield* Deferred.await(updated) + }), + ) }) describe("edge cases", () => { - test("handles multiline replacements", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "file.txt") - await fs.writeFile(filepath, "line1\nline2\nline3", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const edit = await resolve() - await Effect.runPromise( - edit.execute( - { - filePath: filepath, - oldString: "line2", - newString: "new line 2\nextra line", - }, - ctx, - ), - ) - - const content = await fs.readFile(filepath, "utf-8") - expect(content).toBe("line1\nnew line 2\nextra line\nline3") - }, - }) - }) - - test("handles CRLF line endings", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "file.txt") - await fs.writeFile(filepath, "line1\r\nold\r\nline3", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const edit = await resolve() - await Effect.runPromise( - edit.execute( - { - filePath: filepath, - oldString: "old", - newString: "new", - }, - ctx, - ), - ) - - const content = await fs.readFile(filepath, "utf-8") - expect(content).toBe("line1\r\nnew\r\nline3") - }, - }) - }) - - test("throws error when oldString equals newString", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "file.txt") - await fs.writeFile(filepath, "content", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const edit = await resolve() - await expect( - Effect.runPromise( - edit.execute( - { - filePath: filepath, - oldString: "", - newString: "", - }, - ctx, - ), - ), - ).rejects.toThrow("identical") - }, - }) - }) - - test("throws error when path is directory", async () => { - await using tmp = await tmpdir() - const dirpath = path.join(tmp.path, "adir") - await fs.mkdir(dirpath) - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const edit = await resolve() - await expect( - Effect.runPromise( - edit.execute( - { - filePath: dirpath, - oldString: "old", - newString: "new", - }, - ctx, - ), - ), - ).rejects.toThrow("directory") - }, - }) - }) - - test("tracks file diff statistics", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "file.txt") - await fs.writeFile(filepath, "line1\nline2\nline3", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const edit = await resolve() - const result = await Effect.runPromise( - edit.execute( - { - filePath: filepath, - oldString: "line2", - newString: "new line a\nnew line b", - }, - ctx, - ), - ) - - expect(result.metadata.filediff).toBeDefined() - expect(result.metadata.filediff.file).toBe(filepath) - expect(result.metadata.filediff.additions).toBeGreaterThan(0) - }, - }) - }) + it.instance("handles multiline replacements", () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "file.txt") + yield* put(filepath, "line1\nline2\nline3") + + yield* run({ filePath: filepath, oldString: "line2", newString: "new line 2\nextra line" }) + + expect(yield* load(filepath)).toBe("line1\nnew line 2\nextra line\nline3") + }), + ) + + it.instance("handles CRLF line endings", () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "file.txt") + yield* put(filepath, "line1\r\nold\r\nline3") + + yield* run({ filePath: filepath, oldString: "old", newString: "new" }) + + expect(yield* load(filepath)).toBe("line1\r\nnew\r\nline3") + }), + ) + + it.instance("throws error when oldString equals newString", () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "file.txt") + yield* put(filepath, "content") + + expect((yield* fail({ filePath: filepath, oldString: "", newString: "" })).message).toContain("identical") + }), + ) + + it.instance("throws error when path is directory", () => + Effect.gen(function* () { + const test = yield* TestInstance + const dirpath = path.join(test.directory, "adir") + yield* makeDirectory(dirpath) + + expect((yield* fail({ filePath: dirpath, oldString: "old", newString: "new" })).message).toContain("directory") + }), + ) + + it.instance("tracks file diff statistics", () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "file.txt") + yield* put(filepath, "line1\nline2\nline3") + + const result = yield* run({ filePath: filepath, oldString: "line2", newString: "new line a\nnew line b" }) + + expect(result.metadata.filediff).toBeDefined() + expect(result.metadata.filediff.file).toBe(filepath) + expect(result.metadata.filediff.additions).toBeGreaterThan(0) + }), + ) }) describe("line endings", () => { @@ -552,204 +334,200 @@ describe("tool.edit", () => { replaceAll?: boolean } - const apply = async (input: Input) => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "test.txt"), input.content) - }, - }) - - return await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const edit = await resolve() - const filePath = path.join(tmp.path, "test.txt") - await Effect.runPromise( - edit.execute( - { - filePath, - oldString: input.oldString, - newString: input.newString, - replaceAll: input.replaceAll, - }, - ctx, - ), - ) - return await Bun.file(filePath).text() - }, - }) - } - - test("preserves LF with LF multi-line strings", async () => { - const content = normalize(old + "\n", "\n") - const output = await apply({ - content, - oldString: normalize(old, "\n"), - newString: normalize(next, "\n"), - }) - expect(output).toBe(normalize(next + "\n", "\n")) - expectLf(output) - }) - - test("preserves CRLF with CRLF multi-line strings", async () => { - const content = normalize(old + "\n", "\r\n") - const output = await apply({ - content, - oldString: normalize(old, "\r\n"), - newString: normalize(next, "\r\n"), - }) - expect(output).toBe(normalize(next + "\n", "\r\n")) - expectCrlf(output) - }) - - test("preserves LF when old/new use CRLF", async () => { - const content = normalize(old + "\n", "\n") - const output = await apply({ - content, - oldString: normalize(old, "\r\n"), - newString: normalize(next, "\r\n"), - }) - expect(output).toBe(normalize(next + "\n", "\n")) - expectLf(output) - }) - - test("preserves CRLF when old/new use LF", async () => { - const content = normalize(old + "\n", "\r\n") - const output = await apply({ - content, - oldString: normalize(old, "\n"), - newString: normalize(next, "\n"), - }) - expect(output).toBe(normalize(next + "\n", "\r\n")) - expectCrlf(output) - }) - - test("preserves LF when newString uses CRLF", async () => { - const content = normalize(old + "\n", "\n") - const output = await apply({ - content, - oldString: normalize(old, "\n"), - newString: normalize(next, "\r\n"), - }) - expect(output).toBe(normalize(next + "\n", "\n")) - expectLf(output) - }) - - test("preserves CRLF when newString uses LF", async () => { - const content = normalize(old + "\n", "\r\n") - const output = await apply({ - content, - oldString: normalize(old, "\r\n"), - newString: normalize(next, "\n"), + const apply = Effect.fn("EditToolTest.lineEndings.apply")(function* (input: Input) { + const test = yield* TestInstance + const filePath = path.join(test.directory, "test.txt") + yield* put(filePath, input.content) + yield* run({ + filePath, + oldString: input.oldString, + newString: input.newString, + replaceAll: input.replaceAll, }) - expect(output).toBe(normalize(next + "\n", "\r\n")) - expectCrlf(output) + return yield* load(filePath) }) - test("preserves LF with mixed old/new line endings", async () => { - const content = normalize(old + "\n", "\n") - const output = await apply({ - content, - oldString: "alpha\nbeta\r\ngamma", - newString: "alpha\r\nbeta\nomega", - }) - expect(output).toBe(normalize(alt + "\n", "\n")) - expectLf(output) - }) - - test("preserves CRLF with mixed old/new line endings", async () => { - const content = normalize(old + "\n", "\r\n") - const output = await apply({ - content, - oldString: "alpha\r\nbeta\ngamma", - newString: "alpha\nbeta\r\nomega", - }) - expect(output).toBe(normalize(alt + "\n", "\r\n")) - expectCrlf(output) - }) - - test("replaceAll preserves LF for multi-line blocks", async () => { - const blockOld = "alpha\nbeta" - const blockNew = "alpha\nbeta-updated" - const content = normalize(blockOld + "\n" + blockOld + "\n", "\n") - const output = await apply({ - content, - oldString: normalize(blockOld, "\n"), - newString: normalize(blockNew, "\n"), - replaceAll: true, - }) - expect(output).toBe(normalize(blockNew + "\n" + blockNew + "\n", "\n")) - expectLf(output) - }) - - test("replaceAll preserves CRLF for multi-line blocks", async () => { - const blockOld = "alpha\nbeta" - const blockNew = "alpha\nbeta-updated" - const content = normalize(blockOld + "\n" + blockOld + "\n", "\r\n") - const output = await apply({ - content, - oldString: normalize(blockOld, "\r\n"), - newString: normalize(blockNew, "\r\n"), - replaceAll: true, - }) - expect(output).toBe(normalize(blockNew + "\n" + blockNew + "\n", "\r\n")) - expectCrlf(output) - }) + it.instance("preserves LF with LF multi-line strings", () => + Effect.gen(function* () { + const content = normalize(old + "\n", "\n") + const output = yield* apply({ + content, + oldString: normalize(old, "\n"), + newString: normalize(next, "\n"), + }) + expect(output).toBe(normalize(next + "\n", "\n")) + expectLf(output) + }), + ) + + it.instance("preserves CRLF with CRLF multi-line strings", () => + Effect.gen(function* () { + const content = normalize(old + "\n", "\r\n") + const output = yield* apply({ + content, + oldString: normalize(old, "\r\n"), + newString: normalize(next, "\r\n"), + }) + expect(output).toBe(normalize(next + "\n", "\r\n")) + expectCrlf(output) + }), + ) + + it.instance("preserves LF when old/new use CRLF", () => + Effect.gen(function* () { + const content = normalize(old + "\n", "\n") + const output = yield* apply({ + content, + oldString: normalize(old, "\r\n"), + newString: normalize(next, "\r\n"), + }) + expect(output).toBe(normalize(next + "\n", "\n")) + expectLf(output) + }), + ) + + it.instance("preserves CRLF when old/new use LF", () => + Effect.gen(function* () { + const content = normalize(old + "\n", "\r\n") + const output = yield* apply({ + content, + oldString: normalize(old, "\n"), + newString: normalize(next, "\n"), + }) + expect(output).toBe(normalize(next + "\n", "\r\n")) + expectCrlf(output) + }), + ) + + it.instance("preserves LF when newString uses CRLF", () => + Effect.gen(function* () { + const content = normalize(old + "\n", "\n") + const output = yield* apply({ + content, + oldString: normalize(old, "\n"), + newString: normalize(next, "\r\n"), + }) + expect(output).toBe(normalize(next + "\n", "\n")) + expectLf(output) + }), + ) + + it.instance("preserves CRLF when newString uses LF", () => + Effect.gen(function* () { + const content = normalize(old + "\n", "\r\n") + const output = yield* apply({ + content, + oldString: normalize(old, "\r\n"), + newString: normalize(next, "\n"), + }) + expect(output).toBe(normalize(next + "\n", "\r\n")) + expectCrlf(output) + }), + ) + + it.instance("preserves LF with mixed old/new line endings", () => + Effect.gen(function* () { + const content = normalize(old + "\n", "\n") + const output = yield* apply({ + content, + oldString: "alpha\nbeta\r\ngamma", + newString: "alpha\r\nbeta\nomega", + }) + expect(output).toBe(normalize(alt + "\n", "\n")) + expectLf(output) + }), + ) + + it.instance("preserves CRLF with mixed old/new line endings", () => + Effect.gen(function* () { + const content = normalize(old + "\n", "\r\n") + const output = yield* apply({ + content, + oldString: "alpha\r\nbeta\ngamma", + newString: "alpha\nbeta\r\nomega", + }) + expect(output).toBe(normalize(alt + "\n", "\r\n")) + expectCrlf(output) + }), + ) + + it.instance("replaceAll preserves LF for multi-line blocks", () => + Effect.gen(function* () { + const blockOld = "alpha\nbeta" + const blockNew = "alpha\nbeta-updated" + const content = normalize(blockOld + "\n" + blockOld + "\n", "\n") + const output = yield* apply({ + content, + oldString: normalize(blockOld, "\n"), + newString: normalize(blockNew, "\n"), + replaceAll: true, + }) + expect(output).toBe(normalize(blockNew + "\n" + blockNew + "\n", "\n")) + expectLf(output) + }), + ) + + it.instance("replaceAll preserves CRLF for multi-line blocks", () => + Effect.gen(function* () { + const blockOld = "alpha\nbeta" + const blockNew = "alpha\nbeta-updated" + const content = normalize(blockOld + "\n" + blockOld + "\n", "\r\n") + const output = yield* apply({ + content, + oldString: normalize(blockOld, "\r\n"), + newString: normalize(blockNew, "\r\n"), + replaceAll: true, + }) + expect(output).toBe(normalize(blockNew + "\n" + blockNew + "\n", "\r\n")) + expectCrlf(output) + }), + ) }) describe("concurrent editing", () => { - test("preserves concurrent edits to different sections of the same file", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "file.txt") - await fs.writeFile(filepath, "top = 0\nmiddle = keep\nbottom = 0\n", "utf-8") - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const edit = await resolve() - let asks = 0 - const firstAsk = Promise.withResolvers() - const delayedCtx = { - ...ctx, - ask: () => - Effect.gen(function* () { - asks++ - if (asks !== 1) return - firstAsk.resolve() - yield* Effect.promise(() => Bun.sleep(50)) - }), - } - - const promise1 = Effect.runPromise( - edit.execute( - { - filePath: filepath, - oldString: "top = 0", - newString: "top = 1", - }, - delayedCtx, - ), - ) - - await firstAsk.promise - - const promise2 = Effect.runPromise( - edit.execute( - { - filePath: filepath, - oldString: "bottom = 0", - newString: "bottom = 2", - }, - delayedCtx, - ), - ) - - const results = await Promise.allSettled([promise1, promise2]) - expect(results[0]?.status).toBe("fulfilled") - expect(results[1]?.status).toBe("fulfilled") - expect(await fs.readFile(filepath, "utf-8")).toBe("top = 1\nmiddle = keep\nbottom = 2\n") - }, - }) - }) + it.instance("preserves concurrent edits to different sections of the same file", () => + Effect.gen(function* () { + const test = yield* TestInstance + const filepath = path.join(test.directory, "file.txt") + yield* put(filepath, "top = 0\nmiddle = keep\nbottom = 0\n") + + const firstAsk = yield* Deferred.make() + let asks = 0 + const delayedCtx = { + ...ctx, + ask: () => + Effect.gen(function* () { + asks++ + if (asks !== 1) return + yield* Deferred.succeed(firstAsk, undefined) + yield* Effect.sleep("50 millis") + }), + } + + const first = yield* run( + { + filePath: filepath, + oldString: "top = 0", + newString: "top = 1", + }, + delayedCtx, + ).pipe(Effect.forkScoped) + + yield* Deferred.await(firstAsk) + yield* Effect.all([ + Fiber.join(first), + run( + { + filePath: filepath, + oldString: "bottom = 0", + newString: "bottom = 2", + }, + delayedCtx, + ), + ]) + + expect(yield* load(filepath)).toBe("top = 1\nmiddle = keep\nbottom = 2\n") + }), + ) }) }) diff --git a/packages/opencode/test/tool/external-directory.test.ts b/packages/opencode/test/tool/external-directory.test.ts index 519042275af..2dcb157c422 100644 --- a/packages/opencode/test/tool/external-directory.test.ts +++ b/packages/opencode/test/tool/external-directory.test.ts @@ -1,14 +1,16 @@ -import { describe, expect, test } from "bun:test" +import { describe, expect } from "bun:test" import path from "path" import { Effect } from "effect" +import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import type { Tool } from "@/tool/tool" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" -import { assertExternalDirectory } from "../../src/tool/external-directory" +import { assertExternalDirectoryEffect } from "../../src/tool/external-directory" import { Filesystem } from "@/util/filesystem" -import { tmpdir } from "../fixture/fixture" +import { provideInstance, TestInstance, tmpdirScoped } from "../fixture/fixture" import type { Permission } from "../../src/permission" import { SessionID, MessageID } from "../../src/session/schema" +import { testEffect } from "../lib/effect" + +const it = testEffect(CrossSpawnSpawner.defaultLayer) const baseCtx: Omit = { sessionID: SessionID.make("ses_test"), @@ -36,135 +38,120 @@ function makeCtx() { } describe("tool.assertExternalDirectory", () => { - test("no-ops for empty target", async () => { - const { requests, ctx } = makeCtx() - - await WithInstance.provide({ - directory: "/tmp", - fn: async () => { - await assertExternalDirectory(ctx) - }, - }) - - expect(requests.length).toBe(0) - }) - - test("no-ops for paths inside Instance.directory", async () => { - const { requests, ctx } = makeCtx() - - await WithInstance.provide({ - directory: "/tmp/project", - fn: async () => { - await assertExternalDirectory(ctx, path.join("/tmp/project", "file.txt")) - }, - }) - - expect(requests.length).toBe(0) - }) - - test("asks with a single canonical glob", async () => { - const { requests, ctx } = makeCtx() - - const directory = "/tmp/project" - const target = "/tmp/outside/file.txt" - const expected = glob(path.join(path.dirname(target), "*")) - - await WithInstance.provide({ - directory, - fn: async () => { - await assertExternalDirectory(ctx, target) - }, - }) - - const req = requests.find((r) => r.permission === "external_directory") - expect(req).toBeDefined() - expect(req!.patterns).toEqual([expected]) - expect(req!.always).toEqual([expected]) - }) - - test("uses target directory when kind=directory", async () => { - const { requests, ctx } = makeCtx() - - const directory = "/tmp/project" - const target = "/tmp/outside" - const expected = glob(path.join(target, "*")) - - await WithInstance.provide({ - directory, - fn: async () => { - await assertExternalDirectory(ctx, target, { kind: "directory" }) - }, - }) - - const req = requests.find((r) => r.permission === "external_directory") - expect(req).toBeDefined() - expect(req!.patterns).toEqual([expected]) - expect(req!.always).toEqual([expected]) - }) - - test("skips prompting when bypass=true", async () => { - const { requests, ctx } = makeCtx() - - await WithInstance.provide({ - directory: "/tmp/project", - fn: async () => { - await assertExternalDirectory(ctx, "/tmp/outside/file.txt", { bypass: true }) - }, - }) - - expect(requests.length).toBe(0) - }) + it.live("no-ops for empty target", () => + Effect.gen(function* () { + const { requests, ctx } = makeCtx() - if (process.platform === "win32") { - test("normalizes Windows path variants to one glob", async () => { + yield* assertExternalDirectoryEffect(ctx) + + expect(requests.length).toBe(0) + }), + ) + + it.live("no-ops for paths inside Instance.directory", () => + provideInstance("/tmp/project")( + Effect.gen(function* () { + const { requests, ctx } = makeCtx() + + yield* assertExternalDirectoryEffect(ctx, path.join("/tmp/project", "file.txt")) + + expect(requests.length).toBe(0) + }), + ), + ) + + it.live("asks with a single canonical glob", () => + Effect.gen(function* () { const { requests, ctx } = makeCtx() - await using outerTmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "outside.txt"), "x") - }, - }) - await using tmp = await tmpdir({ git: true }) - - const target = path.join(outerTmp.path, "outside.txt") - const alt = target - .replace(/^[A-Za-z]:/, "") - .replaceAll("\\", "/") - .toLowerCase() - - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await assertExternalDirectory(ctx, alt) - }, - }) + const directory = "/tmp/project" + const target = "/tmp/outside/file.txt" + const expected = glob(path.join(path.dirname(target), "*")) + + yield* provideInstance(directory)(assertExternalDirectoryEffect(ctx, target)) const req = requests.find((r) => r.permission === "external_directory") - const expected = glob(path.join(outerTmp.path, "*")) expect(req).toBeDefined() expect(req!.patterns).toEqual([expected]) expect(req!.always).toEqual([expected]) - }) + }), + ) - test("uses drive root glob for root files", async () => { + it.live("uses target directory when kind=directory", () => + Effect.gen(function* () { const { requests, ctx } = makeCtx() - await using tmp = await tmpdir({ git: true }) - const root = path.parse(tmp.path).root - const target = path.join(root, "boot.ini") + const directory = "/tmp/project" + const target = "/tmp/outside" + const expected = glob(path.join(target, "*")) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - await assertExternalDirectory(ctx, target) - }, - }) + yield* provideInstance(directory)(assertExternalDirectoryEffect(ctx, target, { kind: "directory" })) const req = requests.find((r) => r.permission === "external_directory") - const expected = path.join(root, "*") expect(req).toBeDefined() expect(req!.patterns).toEqual([expected]) expect(req!.always).toEqual([expected]) - }) + }), + ) + + it.live("skips prompting when bypass=true", () => + provideInstance("/tmp/project")( + Effect.gen(function* () { + const { requests, ctx } = makeCtx() + + yield* assertExternalDirectoryEffect(ctx, "/tmp/outside/file.txt", { bypass: true }) + + expect(requests.length).toBe(0) + }), + ), + ) + + if (process.platform === "win32") { + it.instance( + "normalizes Windows path variants to one glob", + () => + Effect.gen(function* () { + const { requests, ctx } = makeCtx() + + const outerTmp = yield* tmpdirScoped() + yield* Effect.promise(() => Bun.write(path.join(outerTmp, "outside.txt"), "x")) + + const target = path.join(outerTmp, "outside.txt") + const alt = target + .replace(/^[A-Za-z]:/, "") + .replaceAll("\\", "/") + .toLowerCase() + + yield* assertExternalDirectoryEffect(ctx, alt) + + const req = requests.find((r) => r.permission === "external_directory") + const expected = glob(path.join(outerTmp, "*")) + expect(req).toBeDefined() + expect(req!.patterns).toEqual([expected]) + expect(req!.always).toEqual([expected]) + }), + { git: true }, + ) + + it.instance( + "uses drive root glob for root files", + () => + Effect.gen(function* () { + const { requests, ctx } = makeCtx() + + const tmp = yield* TestInstance + const root = path.parse(tmp.directory).root + const target = path.join(root, "boot.ini") + + yield* assertExternalDirectoryEffect(ctx, target) + + const req = requests.find((r) => r.permission === "external_directory") + const expected = path.join(root, "*") + expect(req).toBeDefined() + expect(req!.patterns).toEqual([expected]) + expect(req!.always).toEqual([expected]) + }), + { git: true }, + ) } }) diff --git a/packages/opencode/test/tool/fixtures/models-api.json b/packages/opencode/test/tool/fixtures/models-api.json index d560eced207..d7ad589c33c 100644 --- a/packages/opencode/test/tool/fixtures/models-api.json +++ b/packages/opencode/test/tool/fixtures/models-api.json @@ -1,339 +1,429 @@ { - "ollama-cloud": { - "id": "ollama-cloud", - "env": ["OLLAMA_API_KEY"], + "302ai": { + "id": "302ai", + "env": ["302AI_API_KEY"], "npm": "@ai-sdk/openai-compatible", - "api": "https://ollama.com/v1", - "name": "Ollama Cloud", - "doc": "https://docs.ollama.com/cloud", + "api": "https://api.302.ai/v1", + "name": "302.AI", + "doc": "https://doc.302.ai", "models": { - "mistral-large-3:675b": { - "id": "mistral-large-3:675b", - "name": "mistral-large-3:675b", - "family": "mistral-large", - "attachment": true, - "reasoning": false, - "tool_call": true, - "release_date": "2025-12-02", - "last_updated": "2026-01-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 262144, "output": 262144 } - }, - "kimi-k2-thinking": { - "id": "kimi-k2-thinking", - "name": "kimi-k2-thinking", - "family": "kimi-thinking", - "attachment": false, - "reasoning": true, - "tool_call": true, - "knowledge": "2024-08", - "release_date": "2025-11-06", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 262144, "output": 262144 } - }, - "qwen3-coder-next": { - "id": "qwen3-coder-next", - "name": "qwen3-coder-next", + "qwen3-235b-a22b": { + "id": "qwen3-235b-a22b", + "name": "Qwen3-235B-A22B", "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, - "release_date": "2026-02-02", - "last_updated": "2026-02-08", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 262144, "output": 65536 } + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-04-29", + "last_updated": "2025-04-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.29, + "output": 2.86 + } }, - "qwen3-vl:235b-instruct": { - "id": "qwen3-vl:235b-instruct", - "name": "qwen3-vl:235b-instruct", - "family": "qwen", + "grok-4.1": { + "id": "grok-4.1", + "name": "grok-4.1", "attachment": true, "reasoning": false, "tool_call": true, - "release_date": "2025-09-22", - "last_updated": "2026-01-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 262144, "output": 131072 } + "temperature": true, + "knowledge": "2025-06", + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 2, + "output": 10 + } }, - "gpt-oss:120b": { - "id": "gpt-oss:120b", - "name": "gpt-oss:120b", - "family": "gpt-oss", + "MiniMax-M2": { + "id": "MiniMax-M2", + "name": "MiniMax-M2", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "release_date": "2025-08-05", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 131072, "output": 32768 } + "temperature": true, + "release_date": "2025-10-26", + "last_updated": "2025-10-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 0.33, + "output": 1.32 + } }, - "kimi-k2.5": { - "id": "kimi-k2.5", - "name": "kimi-k2.5", - "family": "kimi", + "grok-4-1-fast-reasoning": { + "id": "grok-4-1-fast-reasoning", + "name": "grok-4-1-fast-reasoning", "attachment": true, "reasoning": true, "tool_call": true, - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 262144, "output": 262144 } - }, - "cogito-2.1:671b": { - "id": "cogito-2.1:671b", - "name": "cogito-2.1:671b", - "family": "cogito", - "attachment": false, - "reasoning": true, - "tool_call": true, - "release_date": "2025-11-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 163840, "output": 32000 } - }, - "nemotron-3-nano:30b": { - "id": "nemotron-3-nano:30b", - "name": "nemotron-3-nano:30b", - "family": "nemotron", - "attachment": false, - "reasoning": true, - "tool_call": true, - "release_date": "2025-12-15", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 1048576, "output": 131072 } + "temperature": true, + "knowledge": "2025-06", + "release_date": "2025-11-20", + "last_updated": "2025-11-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5 + } }, - "ministral-3:14b": { - "id": "ministral-3:14b", - "name": "ministral-3:14b", - "family": "ministral", + "gemini-2.5-flash-nothink": { + "id": "gemini-2.5-flash-nothink", + "name": "gemini-2.5-flash-nothink", + "family": "gemini-flash", "attachment": true, "reasoning": false, "tool_call": true, - "release_date": "2024-12-01", - "last_updated": "2026-01-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 262144, "output": 128000 } + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-06-24", + "last_updated": "2025-06-24", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5 + } }, - "devstral-small-2:24b": { - "id": "devstral-small-2:24b", - "name": "devstral-small-2:24b", - "family": "devstral", + "grok-4.20-multi-agent-beta-0309": { + "id": "grok-4.20-multi-agent-beta-0309", + "name": "grok-4.20-multi-agent-beta-0309", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "release_date": "2025-12-09", - "last_updated": "2026-01-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 262144, "output": 262144 } + "temperature": true, + "release_date": "2026-03-16", + "last_updated": "2026-03-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 2, + "output": 6 + } }, - "minimax-m2": { - "id": "minimax-m2", - "name": "minimax-m2", - "family": "minimax", + "kimi-k2-0905-preview": { + "id": "kimi-k2-0905-preview", + "name": "kimi-k2-0905-preview", "attachment": false, "reasoning": false, "tool_call": true, - "release_date": "2025-10-23", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 204800, "output": 128000 } - }, - "qwen3-next:80b": { - "id": "qwen3-next:80b", - "name": "qwen3-next:80b", - "family": "qwen", - "attachment": false, - "reasoning": true, - "tool_call": true, - "release_date": "2025-09-15", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 262144, "output": 32768 } + "temperature": true, + "knowledge": "2025-06", + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.632, + "output": 2.53 + } }, - "qwen3-vl:235b": { - "id": "qwen3-vl:235b", - "name": "qwen3-vl:235b", - "family": "qwen", + "claude-haiku-4-5": { + "id": "claude-haiku-4-5", + "name": "claude-haiku-4-5", + "family": "claude-haiku", "attachment": true, "reasoning": true, "tool_call": true, - "release_date": "2025-09-22", - "last_updated": "2026-01-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 262144, "output": 32768 } + "temperature": true, + "knowledge": "2025-02-28", + "release_date": "2025-10-16", + "last_updated": "2025-10-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5 + } }, - "minimax-m2.1": { - "id": "minimax-m2.1", - "name": "minimax-m2.1", - "family": "minimax", - "attachment": false, + "claude-opus-4-5-20251101": { + "id": "claude-opus-4-5-20251101", + "name": "claude-opus-4-5-20251101", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, - "release_date": "2025-12-23", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 204800, "output": 131072 } - }, - "gemma3:12b": { - "id": "gemma3:12b", - "name": "gemma3:12b", - "family": "gemma", - "attachment": true, - "reasoning": false, - "tool_call": false, - "release_date": "2024-12-01", - "last_updated": "2026-01-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 131072, "output": 131072 } + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-11-25", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 5, + "output": 25 + } }, - "gemma3:27b": { - "id": "gemma3:27b", - "name": "gemma3:27b", - "family": "gemma", + "gemini-2.5-flash-lite-preview-09-2025": { + "id": "gemini-2.5-flash-lite-preview-09-2025", + "name": "gemini-2.5-flash-lite-preview-09-2025", "attachment": true, "reasoning": false, - "tool_call": false, - "release_date": "2025-07-27", - "last_updated": "2026-01-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 131072, "output": 131072 } + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-09-26", + "last_updated": "2025-09-26", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } }, - "qwen3-coder:480b": { - "id": "qwen3-coder:480b", - "name": "qwen3-coder:480b", - "family": "qwen", + "qwen3-235b-a22b-instruct-2507": { + "id": "qwen3-235b-a22b-instruct-2507", + "name": "qwen3-235b-a22b-instruct-2507", "attachment": false, "reasoning": false, "tool_call": true, - "release_date": "2025-07-22", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 262144, "output": 65536 } + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-07-30", + "last_updated": "2025-07-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 65536 + }, + "cost": { + "input": 0.29, + "output": 1.143 + } }, - "glm-4.6": { - "id": "glm-4.6", - "name": "glm-4.6", + "glm-5v-turbo": { + "id": "glm-5v-turbo", + "name": "GLM-5V-Turbo", "family": "glm", - "attachment": false, + "attachment": true, "reasoning": true, "tool_call": true, - "release_date": "2025-09-29", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 202752, "output": 131072 } + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 0.72, + "output": 3.2 + } }, - "gemini-3-flash-preview": { - "id": "gemini-3-flash-preview", - "name": "gemini-3-flash-preview", - "family": "gemini-flash", - "attachment": false, - "reasoning": true, + "mistral-large-2512": { + "id": "mistral-large-2512", + "name": "mistral-large-2512", + "attachment": true, + "reasoning": false, "tool_call": true, - "knowledge": "2025-01", - "release_date": "2025-12-17", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 1048576, "output": 65536 } + "temperature": true, + "knowledge": "2024-12", + "release_date": "2025-12-16", + "last_updated": "2025-12-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 262144 + }, + "cost": { + "input": 1.1, + "output": 3.3 + } }, - "minimax-m2.7": { - "id": "minimax-m2.7", - "name": "minimax-m2.7", - "family": "minimax", + "glm-4.7": { + "id": "glm-4.7", + "name": "glm-4.7", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.286, + "output": 1.142 + } }, - "gpt-oss:20b": { - "id": "gpt-oss:20b", - "name": "gpt-oss:20b", - "family": "gpt-oss", - "attachment": false, - "reasoning": true, + "claude-3-5-haiku-20241022": { + "id": "claude-3-5-haiku-20241022", + "name": "claude-3-5-haiku-20241022", + "family": "claude-haiku", + "attachment": true, + "reasoning": false, "tool_call": true, - "release_date": "2025-08-05", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 131072, "output": 32768 } + "temperature": true, + "knowledge": "2024-07-31", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 4 + } }, - "ministral-3:8b": { - "id": "ministral-3:8b", - "name": "ministral-3:8b", - "family": "ministral", + "doubao-seed-1-8-251215": { + "id": "doubao-seed-1-8-251215", + "name": "doubao-seed-1-8-251215", "attachment": true, "reasoning": false, "tool_call": true, - "release_date": "2024-12-01", - "last_updated": "2026-01-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 262144, "output": 128000 } + "temperature": true, + "release_date": "2025-12-18", + "last_updated": "2025-12-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 224000, + "output": 64000 + }, + "cost": { + "input": 0.114, + "output": 0.286 + } }, - "gemma3:4b": { - "id": "gemma3:4b", - "name": "gemma3:4b", - "family": "gemma", + "chatgpt-4o-latest": { + "id": "chatgpt-4o-latest", + "name": "chatgpt-4o-latest", + "family": "gpt", "attachment": true, "reasoning": false, "tool_call": false, - "release_date": "2024-12-01", - "last_updated": "2026-01-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 131072, "output": 131072 } - }, - "qwen3.5:397b": { - "id": "qwen3.5:397b", - "name": "qwen3.5:397b", - "family": "qwen", - "attachment": true, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_details" }, - "release_date": "2026-02-15", - "last_updated": "2026-02-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 262144, "output": 81920 } - }, - "nemotron-3-super": { - "id": "nemotron-3-super", - "name": "nemotron-3-super", - "family": "nemotron", - "attachment": false, - "reasoning": true, - "tool_call": true, - "release_date": "2026-03-11", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 262144, "output": 65536 } + "temperature": true, + "knowledge": "2023-09", + "release_date": "2024-08-08", + "last_updated": "2024-08-08", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 5, + "output": 15 + } }, "glm-5": { "id": "glm-5", @@ -342,529 +432,540 @@ "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "limit": { "context": 202752, "output": 131072 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 2.6 + } }, - "devstral-2:123b": { - "id": "devstral-2:123b", - "name": "devstral-2:123b", - "family": "devstral", + "deepseek-chat": { + "id": "deepseek-chat", + "name": "Deepseek-Chat", + "family": "deepseek", "attachment": false, "reasoning": false, "tool_call": true, - "release_date": "2025-12-09", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 262144, "output": 262144 } + "temperature": true, + "knowledge": "2024-07", + "release_date": "2024-11-29", + "last_updated": "2024-11-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.29, + "output": 0.43 + } }, - "glm-4.7": { - "id": "glm-4.7", - "name": "glm-4.7", - "family": "glm", + "deepseek-v3.2-thinking": { + "id": "deepseek-v3.2-thinking", + "name": "DeepSeek-V3.2-Thinking", "attachment": false, "reasoning": true, "tool_call": true, - "release_date": "2025-12-22", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 202752, "output": 131072 } - }, - "rnj-1:8b": { - "id": "rnj-1:8b", - "name": "rnj-1:8b", - "family": "rnj", - "attachment": false, - "reasoning": false, - "tool_call": true, - "release_date": "2025-12-06", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 32768, "output": 4096 } - }, - "deepseek-v3.2": { - "id": "deepseek-v3.2", - "name": "deepseek-v3.2", - "family": "deepseek", - "attachment": false, - "reasoning": true, - "tool_call": true, - "release_date": "2025-06-15", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 163840, "output": 65536 } + "temperature": true, + "knowledge": "2024-12", + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.29, + "output": 0.43 + } }, - "deepseek-v3.1:671b": { - "id": "deepseek-v3.1:671b", - "name": "deepseek-v3.1:671b", - "family": "deepseek", - "attachment": false, + "claude-sonnet-4-6": { + "id": "claude-sonnet-4-6", + "name": "claude-sonnet-4-6", + "family": "claude-sonnet", + "attachment": true, "reasoning": true, "tool_call": true, - "release_date": "2025-08-21", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 163840, "output": 163840 } + "temperature": true, + "knowledge": "2025-08-31", + "release_date": "2026-02-18", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "ministral-3:3b": { - "id": "ministral-3:3b", - "name": "ministral-3:3b", - "family": "ministral", + "gpt-5-thinking": { + "id": "gpt-5-thinking", + "name": "gpt-5-thinking", "attachment": true, - "reasoning": false, - "tool_call": true, - "release_date": "2024-10-22", - "last_updated": "2026-01-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 262144, "output": 128000 } - }, - "kimi-k2:1t": { - "id": "kimi-k2:1t", - "name": "kimi-k2:1t", - "family": "kimi", - "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "temperature": true, "knowledge": "2024-10", - "release_date": "2025-07-11", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 262144, "output": 262144 } + "release_date": "2025-08-08", + "last_updated": "2025-08-08", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, - "minimax-m2.5": { - "id": "minimax-m2.5", - "name": "minimax-m2.5", - "family": "minimax", - "attachment": false, - "reasoning": true, - "tool_call": true, - "knowledge": "2025-01", - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 204800, "output": 131072 } - } - } - }, - "moark": { - "id": "moark", - "env": ["MOARK_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://moark.com/v1", - "name": "Moark", - "doc": "https://moark.com/docs/openapi/v1#tag/%E6%96%87%E6%9C%AC%E7%94%9F%E6%88%90", - "models": { - "GLM-4.7": { - "id": "GLM-4.7", - "name": "GLM-4.7", - "family": "glm", + "glm-4.7-flashx": { + "id": "glm-4.7-flashx", + "name": "glm-4.7-flashx", + "family": "glm-flash", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01-20", + "last_updated": "2026-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 3.5, "output": 14 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 0.0715, + "output": 0.429 + } }, - "MiniMax-M2.1": { - "id": "MiniMax-M2.1", - "name": "MiniMax-M2.1", - "family": "minimax", - "attachment": false, - "reasoning": true, + "gemini-3-flash-preview": { + "id": "gemini-3-flash-preview", + "name": "gemini-3-flash-preview", + "attachment": true, + "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2.1, "output": 8.4 }, - "limit": { "context": 204800, "output": 131072 } - } - } - }, - "drun": { - "id": "drun", - "env": ["DRUN_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://chat.d.run/v1", - "name": "D.Run (China)", - "doc": "https://www.d.run", - "models": { - "public/deepseek-r1": { - "id": "public/deepseek-r1", - "name": "DeepSeek R1", - "family": "deepseek-thinking", + "knowledge": "2025-06", + "release_date": "2025-12-18", + "last_updated": "2025-12-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 3 + } + }, + "qwen-plus": { + "id": "qwen-plus", + "name": "Qwen-Plus", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.55, "output": 2.2 }, - "limit": { "context": 131072, "output": 32000 } + "knowledge": "2024-10", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 32768 + }, + "cost": { + "input": 0.12, + "output": 1.2 + } }, - "public/deepseek-v3": { - "id": "public/deepseek-v3", - "name": "DeepSeek V3", - "family": "deepseek", - "attachment": false, + "grok-4.20-beta-0309-non-reasoning": { + "id": "grok-4.20-beta-0309-non-reasoning", + "name": "grok-4.20-beta-0309-non-reasoning", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2024-12-26", - "last_updated": "2024-12-26", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.28, "output": 1.1 }, - "limit": { "context": 131072, "output": 8192 } + "release_date": "2026-03-16", + "last_updated": "2026-03-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 2, + "output": 6 + } }, - "public/minimax-m25": { - "id": "public/minimax-m25", - "name": "MiniMax M2.5", - "family": "minimax", - "attachment": false, + "claude-opus-4-7": { + "id": "claude-opus-4-7", + "name": "claude-opus-4-7", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_details" }, "temperature": true, - "release_date": "2025-03-01", - "last_updated": "2025-03-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2026-01-31", + "release_date": "2026-04-17", + "last_updated": "2026-04-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.29, "output": 1.16 }, - "limit": { "context": 204800, "output": 131072 } - } - } - }, - "inference": { - "id": "inference", - "env": ["INFERENCE_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://inference.net/v1", - "name": "Inference", - "doc": "https://inference.net/models", - "models": { - "google/gemma-3": { - "id": "google/gemma-3", - "name": "Google Gemma 3", - "family": "gemma", + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25, + "tiers": [ + { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5 + } + } + }, + "gpt-5-mini": { + "id": "gpt-5-mini", + "name": "gpt-5-mini", + "family": "gpt-mini", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.3 }, - "limit": { "context": 125000, "output": 4096 } - }, - "qwen/qwen3-embedding-4b": { - "id": "qwen/qwen3-embedding-4b", - "name": "Qwen 3 Embedding 4B", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": false, + "structured_output": true, "temperature": false, - "knowledge": "2024-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.01, "output": 0 }, - "limit": { "context": 32000, "output": 2048 } + "knowledge": "2024-05-30", + "release_date": "2025-08-08", + "last_updated": "2025-08-08", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2 + } }, - "qwen/qwen-2.5-7b-vision-instruct": { - "id": "qwen/qwen-2.5-7b-vision-instruct", - "name": "Qwen 2.5 7B Vision Instruct", - "family": "qwen", + "gemini-3-pro-preview": { + "id": "gemini-3-pro-preview", + "name": "gemini-3-pro-preview", "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 125000, "output": 4096 } + "knowledge": "2025-06", + "release_date": "2025-11-19", + "last_updated": "2025-11-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 2, + "output": 12 + } }, - "mistral/mistral-nemo-12b-instruct": { - "id": "mistral/mistral-nemo-12b-instruct", - "name": "Mistral Nemo 12B Instruct", - "family": "mistral-nemo", + "MiniMax-M2.7": { + "id": "MiniMax-M2.7", + "name": "MiniMax-M2.7", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.038, "output": 0.1 }, - "limit": { "context": 16000, "output": 4096 } + "release_date": "2026-03-19", + "last_updated": "2026-03-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "meta/llama-3.2-1b-instruct": { - "id": "meta/llama-3.2-1b-instruct", - "name": "Llama 3.2 1B Instruct", - "family": "llama", + "qwen3-max-2025-09-23": { + "id": "qwen3-max-2025-09-23", + "name": "qwen3-max-2025-09-23", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.01, "output": 0.01 }, - "limit": { "context": 16000, "output": 4096 } + "knowledge": "2025-04", + "release_date": "2025-09-24", + "last_updated": "2025-09-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 258048, + "output": 65536 + }, + "cost": { + "input": 0.86, + "output": 3.43 + } }, - "meta/llama-3.1-8b-instruct": { - "id": "meta/llama-3.1-8b-instruct", - "name": "Llama 3.1 8B Instruct", - "family": "llama", - "attachment": false, - "reasoning": false, + "claude-sonnet-4-5-20250929": { + "id": "claude-sonnet-4-5-20250929", + "name": "claude-sonnet-4-5-20250929", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.025, "output": 0.025 }, - "limit": { "context": 16000, "output": 4096 } + "knowledge": "2025-07-31", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "meta/llama-3.2-11b-vision-instruct": { - "id": "meta/llama-3.2-11b-vision-instruct", - "name": "Llama 3.2 11B Vision Instruct", - "family": "llama", - "attachment": true, + "qwen-flash": { + "id": "qwen-flash", + "name": "Qwen-Flash", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.055, "output": 0.055 }, - "limit": { "context": 16000, "output": 4096 } + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 32768 + }, + "cost": { + "input": 0.022, + "output": 0.22 + } }, - "meta/llama-3.2-3b-instruct": { - "id": "meta/llama-3.2-3b-instruct", - "name": "Llama 3.2 3B Instruct", - "family": "llama", - "attachment": false, + "gemini-2.5-pro": { + "id": "gemini-2.5-pro", + "name": "gemini-2.5-pro", + "family": "gemini-pro", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.02, "output": 0.02 }, - "limit": { "context": 16000, "output": 4096 } + "knowledge": "2025-01", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, - "osmosis/osmosis-structure-0.6b": { - "id": "osmosis/osmosis-structure-0.6b", - "name": "Osmosis Structure 0.6B", - "family": "osmosis", - "attachment": false, + "grok-4-1-fast-non-reasoning": { + "id": "grok-4-1-fast-non-reasoning", + "name": "grok-4-1-fast-non-reasoning", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.5 }, - "limit": { "context": 4000, "output": 2048 } - } - } - }, - "bailing": { - "id": "bailing", - "env": ["BAILING_API_TOKEN"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.tbox.cn/api/llm/v1/chat/completions", - "name": "Bailing", - "doc": "https://alipaytbox.yuque.com/sxs0ba/ling/intro", - "models": { - "Ling-1T": { - "id": "Ling-1T", - "name": "Ling-1T", - "family": "ling", - "attachment": false, + "knowledge": "2025-06", + "release_date": "2025-11-20", + "last_updated": "2025-11-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5 + } + }, + "claude-3-5-haiku-latest": { + "id": "claude-3-5-haiku-latest", + "name": "claude-3-5-haiku-latest", + "family": "claude-haiku", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-06", - "release_date": "2025-10", - "last_updated": "2025-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.57, "output": 2.29 }, - "limit": { "context": 128000, "output": 32000 } + "knowledge": "2024-07-31", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 4 + } }, - "Ring-1T": { - "id": "Ring-1T", - "name": "Ring-1T", - "family": "ring", - "attachment": false, - "reasoning": true, - "tool_call": false, - "temperature": true, - "knowledge": "2024-06", - "release_date": "2025-10", - "last_updated": "2025-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.57, "output": 2.29 }, - "limit": { "context": 128000, "output": 32000 } - } - } - }, - "openai": { - "id": "openai", - "env": ["OPENAI_API_KEY"], - "npm": "@ai-sdk/openai", - "name": "OpenAI", - "doc": "https://platform.openai.com/docs/models", - "models": { - "gpt-5.2-codex": { - "id": "gpt-5.2-codex", - "name": "GPT-5.2 Codex", - "family": "gpt-codex", + "claude-opus-4-5-20251101-thinking": { + "id": "claude-opus-4-5-20251101-thinking", + "name": "claude-opus-4-5-20251101-thinking", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-03", + "release_date": "2025-11-25", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 5, + "output": 25 + } }, - "o1-pro": { - "id": "o1-pro", - "name": "o1-pro", - "family": "o-pro", + "gpt-5.2": { + "id": "gpt-5.2", + "name": "gpt-5.2", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, - "knowledge": "2023-09", - "release_date": "2025-03-19", - "last_updated": "2025-03-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 150, "output": 600 }, - "limit": { "context": 200000, "output": 100000 } - }, - "text-embedding-3-large": { - "id": "text-embedding-3-large", - "name": "text-embedding-3-large", - "family": "text-embedding", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "knowledge": "2024-01", - "release_date": "2024-01-25", - "last_updated": "2024-01-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.13, "output": 0 }, - "limit": { "context": 8191, "output": 3072 } - }, - "gpt-5.1-codex-mini": { - "id": "gpt-5.1-codex-mini", - "name": "GPT-5.1 Codex mini", - "family": "gpt-codex", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.025 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } - }, - "gpt-5.4-pro": { - "id": "gpt-5.4-pro", - "name": "GPT-5.4 Pro", - "family": "gpt-pro", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": false, - "temperature": false, "knowledge": "2025-08-31", - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 30, "output": 180, "context_over_200k": { "input": 60, "output": 270 } }, - "limit": { "context": 1050000, "input": 922000, "output": 128000 } - }, - "o3-mini": { - "id": "o3-mini", - "name": "o3-mini", - "family": "o-mini", - "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2024-12-20", - "last_updated": "2025-01-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-12", + "last_updated": "2025-12-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.55 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14 + } }, "gpt-5.4-mini": { "id": "gpt-5.4-mini", - "name": "GPT-5.4 mini", + "name": "gpt-5.4-mini", "family": "gpt-mini", "attachment": true, "reasoning": true, @@ -872,545 +973,1059 @@ "structured_output": true, "temperature": false, "knowledge": "2025-08-31", - "release_date": "2026-03-17", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-03-19", + "last_updated": "2026-03-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.75, "output": 4.5, "cache_read": 0.075 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.75, + "output": 4.5 + } }, - "gpt-5-pro": { - "id": "gpt-5-pro", - "name": "GPT-5 Pro", - "family": "gpt-pro", + "gemini-3-pro-image-preview": { + "id": "gemini-3-pro-image-preview", + "name": "gemini-3-pro-image-preview", "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "knowledge": "2025-06", + "release_date": "2025-11-20", + "last_updated": "2025-11-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "output": 64000 + }, + "cost": { + "input": 2, + "output": 120 + } + }, + "glm-5.1": { + "id": "glm-5.1", + "name": "glm-5.1", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-10-06", - "last_updated": "2025-10-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "release_date": "2026-04-10", + "last_updated": "2026-04-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 120 }, - "limit": { "context": 400000, "input": 272000, "output": 272000 } + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 0.86, + "output": 3.5 + } }, - "gpt-5.2-chat-latest": { - "id": "gpt-5.2-chat-latest", - "name": "GPT-5.2 Chat", - "family": "gpt-codex", - "attachment": true, - "reasoning": true, + "qwen-max-latest": { + "id": "qwen-max-latest", + "name": "Qwen-Max-Latest", + "family": "qwen", + "attachment": false, + "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-11", + "release_date": "2024-04-03", + "last_updated": "2025-01-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.343, + "output": 1.372 + } }, - "gpt-5": { - "id": "gpt-5", - "name": "GPT-5", - "family": "gpt", + "gpt-5.4-nano": { + "id": "gpt-5.4-nano", + "name": "gpt-5.4-nano", + "family": "gpt-nano", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-03-19", + "last_updated": "2026-03-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.2, + "output": 1.25 + } }, - "gpt-4-turbo": { - "id": "gpt-4-turbo", - "name": "GPT-4 Turbo", - "family": "gpt", + "gemini-2.5-flash-image": { + "id": "gemini-2.5-flash-image", + "name": "gemini-2.5-flash-image", "attachment": true, "reasoning": false, - "tool_call": true, - "structured_output": false, + "tool_call": false, "temperature": true, - "knowledge": "2023-12", - "release_date": "2023-11-06", - "last_updated": "2024-04-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-10-08", + "last_updated": "2025-10-08", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 10, "output": 30 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.3, + "output": 30 + } }, - "gpt-4o": { - "id": "gpt-4o", - "name": "GPT-4o", - "family": "gpt", - "attachment": true, - "reasoning": false, + "glm-4.5": { + "id": "glm-4.5", + "name": "GLM-4.5", + "family": "glm", + "attachment": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2023-09", - "release_date": "2024-05-13", - "last_updated": "2024-08-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 10, "cache_read": 1.25 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2025-04", + "release_date": "2025-07-29", + "last_updated": "2025-07-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 98304 + }, + "cost": { + "input": 0.286, + "output": 1.142 + } }, - "gpt-5.3-codex": { - "id": "gpt-5.3-codex", - "name": "GPT-5.3 Codex", - "family": "gpt-codex", + "gpt-5.4-mini-2026-03-17": { + "id": "gpt-5.4-mini-2026-03-17", + "name": "gpt-5.4-mini-2026-03-17", + "family": "gpt-mini", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, "knowledge": "2025-08-31", - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-03-19", + "last_updated": "2026-03-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.75, + "output": 4.5 + } }, - "gpt-5-mini": { - "id": "gpt-5-mini", - "name": "GPT-5 Mini", - "family": "gpt-mini", + "gemini-2.5-flash": { + "id": "gemini-2.5-flash", + "name": "gemini-2.5-flash", + "family": "gemini-flash", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.025 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5 + } }, - "gpt-5.3-codex-spark": { - "id": "gpt-5.3-codex-spark", - "name": "GPT-5.3 Codex Spark", - "family": "gpt-codex-spark", + "gpt-5.2-chat-latest": { + "id": "gpt-5.2-chat-latest", + "name": "gpt-5.2-chat-latest", + "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, "knowledge": "2025-08-31", - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-12-12", + "last_updated": "2025-12-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 128000, "input": 100000, "output": 32000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.75, + "output": 14 + } }, - "gpt-4o-mini": { - "id": "gpt-4o-mini", - "name": "GPT-4o mini", - "family": "gpt-mini", + "doubao-seed-1-6-vision-250815": { + "id": "doubao-seed-1-6-vision-250815", + "name": "doubao-seed-1-6-vision-250815", "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2023-09", - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.6, "cache_read": 0.08 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 256000, + "output": 32000 + }, + "cost": { + "input": 0.114, + "output": 1.143 + } }, - "gpt-5.1-codex-max": { - "id": "gpt-5.1-codex-max", - "name": "GPT-5.1 Codex Max", - "family": "gpt-codex", + "gemini-3.1-flash-image-preview": { + "id": "gemini-3.1-flash-image-preview", + "name": "gemini-3.1-flash-image-preview", "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-02-27", + "last_updated": "2026-02-27", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text", "image"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.5, + "output": 60 + } }, - "gpt-4.1": { - "id": "gpt-4.1", - "name": "GPT-4.1", - "family": "gpt", - "attachment": true, + "MiniMax-M2.7-highspeed": { + "id": "MiniMax-M2.7-highspeed", + "name": "MiniMax-M2.7-highspeed", + "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-03-19", + "last_updated": "2026-03-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 4.8 + } }, - "gpt-5.1-chat-latest": { - "id": "gpt-5.1-chat-latest", - "name": "GPT-5.1 Chat", - "family": "gpt-codex", - "attachment": true, - "reasoning": true, + "glm-4.5-x": { + "id": "glm-4.5-x", + "name": "glm-4.5-x", + "family": "glm", + "attachment": false, + "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-07-29", + "last_updated": "2025-07-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.143, + "output": 2.29 + } }, - "gpt-3.5-turbo": { - "id": "gpt-3.5-turbo", - "name": "GPT-3.5-turbo", - "family": "gpt", + "MiniMax-M2.1": { + "id": "MiniMax-M2.1", + "name": "MiniMax-M2.1", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, + "tool_call": true, "temperature": true, - "knowledge": "2021-09-01", - "release_date": "2023-03-01", - "last_updated": "2023-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-19", + "last_updated": "2025-12-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 1.5, "cache_read": 1.25 }, - "limit": { "context": 16385, "output": 4096 } + "limit": { + "context": 1000000, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "gpt-5.4": { - "id": "gpt-5.4", - "name": "GPT-5.4", + "gpt-5.1": { + "id": "gpt-5.1", + "name": "gpt-5.1", "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2024-09-30", + "release_date": "2025-11-14", + "last_updated": "2025-11-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { - "input": 2.5, - "output": 15, - "cache_read": 0.25, - "context_over_200k": { "input": 5, "output": 22.5, "cache_read": 0.5 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 }, - "limit": { "context": 1050000, "input": 922000, "output": 128000 } + "cost": { + "input": 1.25, + "output": 10 + } }, - "o1": { - "id": "o1", - "name": "o1", - "family": "o", - "attachment": true, + "kimi-k2-thinking-turbo": { + "id": "kimi-k2-thinking-turbo", + "name": "kimi-k2-thinking-turbo", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2023-09", - "release_date": "2024-12-05", - "last_updated": "2024-12-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-06", + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 60, "cache_read": 7.5 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 1.265, + "output": 9.119 + } }, - "codex-mini-latest": { - "id": "codex-mini-latest", - "name": "Codex Mini", - "family": "gpt-codex-mini", - "attachment": true, + "deepseek-reasoner": { + "id": "deepseek-reasoner", + "name": "Deepseek-Reasoner", + "family": "deepseek-thinking", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2024-04", - "release_date": "2025-05-16", - "last_updated": "2025-05-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-07", + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.5, "output": 6, "cache_read": 0.375 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.29, + "output": 0.43 + } }, - "o3": { - "id": "o3", - "name": "o3", - "family": "o", + "grok-4-fast-reasoning": { + "id": "grok-4-fast-reasoning", + "name": "grok-4-fast-reasoning", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-06", + "release_date": "2025-09-23", + "last_updated": "2025-09-23", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5 + } }, - "gpt-5.3-chat-latest": { - "id": "gpt-5.3-chat-latest", - "name": "GPT-5.3 Chat (latest)", - "family": "gpt", + "claude-opus-4-1-20250805-thinking": { + "id": "claude-opus-4-1-20250805-thinking", + "name": "claude-opus-4-1-20250805-thinking", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-08-31", - "release_date": "2026-03-03", - "last_updated": "2026-03-03", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-03", + "release_date": "2025-05-27", + "last_updated": "2025-05-27", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75 + } }, - "gpt-5.4-nano": { - "id": "gpt-5.4-nano", - "name": "GPT-5.4 nano", - "family": "gpt-nano", + "glm-4.5-air": { + "id": "glm-4.5-air", + "name": "glm-4.5-air", + "family": "glm-air", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-07-29", + "last_updated": "2025-07-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 98304 + }, + "cost": { + "input": 0.1143, + "output": 0.286 + } + }, + "gpt-5.4-pro": { + "id": "gpt-5.4-pro", + "name": "gpt-5.4-pro", + "family": "gpt-pro", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": false, "knowledge": "2025-08-31", - "release_date": "2026-03-17", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 1.25, "cache_read": 0.02 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 30, + "output": 180, + "cache_read": 0, + "cache_write": 0, + "tiers": [ + { + "input": 60, + "output": 270, + "tier": { + "type": "context", + "size": 272000 + } + } + ], + "context_over_200k": { + "input": 60, + "output": 270 + } + } }, - "gpt-4o-2024-05-13": { - "id": "gpt-4o-2024-05-13", - "name": "GPT-4o (2024-05-13)", - "family": "gpt", - "attachment": true, - "reasoning": false, + "glm-5-turbo": { + "id": "glm-5-turbo", + "name": "glm-5-turbo", + "family": "glm", + "attachment": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "knowledge": "2023-09", - "release_date": "2024-05-13", - "last_updated": "2024-05-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-03-16", + "last_updated": "2026-03-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 15 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 0.72, + "output": 3.2 + } }, - "gpt-4": { - "id": "gpt-4", - "name": "GPT-4", - "family": "gpt", - "attachment": true, + "qwen3-30b-a3b": { + "id": "qwen3-30b-a3b", + "name": "Qwen3-30B-A3B", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": false, "temperature": true, - "knowledge": "2023-11", - "release_date": "2023-11-06", - "last_updated": "2024-04-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04-29", + "last_updated": "2025-04-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 30, "output": 60 }, - "limit": { "context": 8192, "output": 8192 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.11, + "output": 1.08 + } }, - "gpt-4o-2024-11-20": { - "id": "gpt-4o-2024-11-20", - "name": "GPT-4o (2024-11-20)", - "family": "gpt", + "claude-opus-4-5": { + "id": "claude-opus-4-5", + "name": "claude-opus-4-5", + "family": "claude-opus", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2023-09", - "release_date": "2024-11-20", - "last_updated": "2024-11-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-03-31", + "release_date": "2025-11-25", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 10, "cache_read": 1.25 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 5, + "output": 25 + } }, - "text-embedding-ada-002": { - "id": "text-embedding-ada-002", - "name": "text-embedding-ada-002", - "family": "text-embedding", + "glm-4.5v": { + "id": "glm-4.5v", + "name": "GLM-4.5V", + "family": "glm", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-08-12", + "last_updated": "2025-08-12", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 64000, + "output": 16384 + }, + "cost": { + "input": 0.29, + "output": 0.86 + } + }, + "glm-4.6": { + "id": "glm-4.6", + "name": "glm-4.6", + "family": "glm", "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.286, + "output": 1.142 + } + }, + "claude-opus-4-6-thinking": { + "id": "claude-opus-4-6-thinking", + "name": "claude-opus-4-6-thinking", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-05", + "release_date": "2026-02-06", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25 + } + }, + "gemini-2.5-flash-preview-09-2025": { + "id": "gemini-2.5-flash-preview-09-2025", + "name": "gemini-2.5-flash-preview-09-2025", + "attachment": true, "reasoning": false, - "tool_call": false, - "temperature": false, - "knowledge": "2022-12", - "release_date": "2022-12-15", - "last_updated": "2022-12-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-09-26", + "last_updated": "2025-09-26", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0 }, - "limit": { "context": 8192, "output": 1536 } + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5 + } }, - "gpt-5.2": { - "id": "gpt-5.2", - "name": "GPT-5.2", - "family": "gpt", + "claude-sonnet-4-6-thinking": { + "id": "claude-sonnet-4-6-thinking", + "name": "claude-sonnet-4-6-thinking", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-08", + "release_date": "2026-02-18", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "o4-mini-deep-research": { - "id": "o4-mini-deep-research", - "name": "o4-mini-deep-research", - "family": "o-mini", + "glm-4.6v": { + "id": "glm-4.6v", + "name": "GLM-4.6V", + "family": "glm", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2024-06-26", - "last_updated": "2024-06-26", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-12-08", + "last_updated": "2025-12-08", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.145, + "output": 0.43 + } + }, + "claude-opus-4-1-20250805": { + "id": "claude-opus-4-1-20250805", + "name": "claude-opus-4-1-20250805", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75 + } }, - "gpt-5.1": { - "id": "gpt-5.1", - "name": "GPT-5.1", + "gpt-5.4": { + "id": "gpt-5.4", + "name": "gpt-5.4", "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.13 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 2.5, + "output": 15, + "cache_read": 0.25, + "cache_write": 0, + "tiers": [ + { + "input": 5, + "output": 22.5, + "tier": { + "type": "context", + "size": 272000 + } + } + ], + "context_over_200k": { + "input": 5, + "output": 22.5 + } + } }, - "gpt-4.1-mini": { - "id": "gpt-4.1-mini", - "name": "GPT-4.1 mini", - "family": "gpt-mini", + "gpt-5.1-chat-latest": { + "id": "gpt-5.1-chat-latest", + "name": "gpt-5.1-chat-latest", + "family": "gpt-codex", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-14", + "last_updated": "2025-11-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 1.6, "cache_read": 0.1 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, - "gpt-5-chat-latest": { - "id": "gpt-5-chat-latest", - "name": "GPT-5 Chat (latest)", - "family": "gpt-codex", + "claude-haiku-4-5-20251001": { + "id": "claude-haiku-4-5-20251001", + "name": "claude-haiku-4-5-20251001", + "family": "claude-haiku", "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": true, + "tool_call": true, "temperature": true, - "knowledge": "2024-09-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-02-28", + "release_date": "2025-10-16", + "last_updated": "2025-10-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5 + } }, - "gpt-5-nano": { - "id": "gpt-5-nano", - "name": "GPT-5 Nano", + "MiniMax-M1": { + "id": "MiniMax-M1", + "name": "MiniMax-M1", + "family": "minimax", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-06-16", + "last_updated": "2025-06-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 0.132, + "output": 1.254 + } + }, + "gpt-5.4-nano-2026-03-17": { + "id": "gpt-5.4-nano-2026-03-17", + "name": "gpt-5.4-nano-2026-03-17", "family": "gpt-nano", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-03-19", + "last_updated": "2026-03-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.05, "output": 0.4, "cache_read": 0.005 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.2, + "output": 1.25 + } }, - "o3-pro": { - "id": "o3-pro", - "name": "o3-pro", - "family": "o-pro", + "claude-sonnet-4-20250514": { + "id": "claude-sonnet-4-20250514", + "name": "claude-sonnet-4-20250514", + "family": "claude-sonnet", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2025-06-10", - "last_updated": "2025-06-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15 + } + }, + "qwen3-coder-480b-a35b-instruct": { + "id": "qwen3-coder-480b-a35b-instruct", + "name": "qwen3-coder-480b-a35b-instruct", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 20, "output": 80 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.86, + "output": 3.43 + } }, - "gpt-4o-2024-08-06": { - "id": "gpt-4o-2024-08-06", - "name": "GPT-4o (2024-08-06)", - "family": "gpt", + "claude-opus-4-6": { + "id": "claude-opus-4-6", + "name": "claude-opus-4-6", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-05-31", + "release_date": "2026-02-06", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25 + } + }, + "doubao-seed-code-preview-251028": { + "id": "doubao-seed-code-preview-251028", + "name": "doubao-seed-code-preview-251028", "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2023-09", - "release_date": "2024-08-06", - "last_updated": "2024-08-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-11-11", + "last_updated": "2025-11-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 10, "cache_read": 1.25 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 256000, + "output": 32000 + }, + "cost": { + "input": 0.17, + "output": 1.14 + } }, "gpt-4.1-nano": { "id": "gpt-4.1-nano", - "name": "GPT-4.1 nano", + "name": "gpt-4.1-nano", "family": "gpt-nano", "attachment": true, "reasoning": false, @@ -1420,10735 +2035,16105 @@ "knowledge": "2024-04", "release_date": "2025-04-14", "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.03 }, - "limit": { "context": 1047576, "output": 32768 } - }, - "o1-preview": { - "id": "o1-preview", - "name": "o1-preview", - "family": "o", - "attachment": false, - "reasoning": true, - "tool_call": false, - "temperature": true, - "knowledge": "2023-09", - "release_date": "2024-09-12", - "last_updated": "2024-09-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 60, "cache_read": 7.5 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } }, - "text-embedding-3-small": { - "id": "text-embedding-3-small", - "name": "text-embedding-3-small", - "family": "text-embedding", + "deepseek-v3.2": { + "id": "deepseek-v3.2", + "name": "deepseek-v3.2", "attachment": false, "reasoning": false, - "tool_call": false, - "temperature": false, - "knowledge": "2024-01", - "release_date": "2024-01-25", - "last_updated": "2024-01-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.02, "output": 0 }, - "limit": { "context": 8191, "output": 1536 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.29, + "output": 0.43 + } }, - "gpt-5-codex": { - "id": "gpt-5-codex", - "name": "GPT-5-Codex", - "family": "gpt-codex", - "attachment": false, + "gpt-5-pro": { + "id": "gpt-5-pro", + "name": "gpt-5-pro", + "family": "gpt-pro", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, "knowledge": "2024-09-30", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-10-08", + "last_updated": "2025-10-08", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 272000 + }, + "cost": { + "input": 15, + "output": 120 + } }, - "o4-mini": { - "id": "o4-mini", - "name": "o4-mini", - "family": "o-mini", + "gpt-4o": { + "id": "gpt-4o", + "name": "gpt-4o", + "family": "gpt", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "structured_output": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2023-09", + "release_date": "2024-05-13", + "last_updated": "2024-05-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.28 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10 + } }, - "o3-deep-research": { - "id": "o3-deep-research", - "name": "o3-deep-research", - "family": "o", + "claude-sonnet-4-5": { + "id": "claude-sonnet-4-5", + "name": "claude-sonnet-4-5", + "family": "claude-sonnet", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2024-06-26", - "last_updated": "2024-06-26", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-07-31", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 10, "output": 40, "cache_read": 2.5 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "gpt-5.1-codex": { - "id": "gpt-5.1-codex", - "name": "GPT-5.1 Codex", - "family": "gpt-codex", + "gpt-5": { + "id": "gpt-5", + "name": "gpt-5", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-08", + "last_updated": "2025-08-08", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, - "o1-mini": { - "id": "o1-mini", - "name": "o1-mini", - "family": "o-mini", - "attachment": false, + "grok-4.20-beta-0309-reasoning": { + "id": "grok-4.20-beta-0309-reasoning", + "name": "grok-4.20-beta-0309-reasoning", + "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": true, - "temperature": false, - "knowledge": "2023-09", - "release_date": "2024-09-12", - "last_updated": "2024-09-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-16", + "last_updated": "2026-03-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.55 }, - "limit": { "context": 128000, "output": 65536 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 2, + "output": 6 + } }, - "gpt-5.2-pro": { - "id": "gpt-5.2-pro", - "name": "GPT-5.2 Pro", - "family": "gpt-pro", + "claude-opus-4-20250514": { + "id": "claude-opus-4-20250514", + "name": "claude-opus-4-20250514", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": false, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 21, "output": 168 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } - } - } - }, - "io-net": { - "id": "io-net", - "env": ["IOINTELLIGENCE_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.intelligence.io.solutions/api/v1", - "name": "IO.NET", - "doc": "https://io.net/docs/guides/intelligence/io-intelligence", - "models": { - "openai/gpt-oss-120b": { - "id": "openai/gpt-oss-120b", - "name": "GPT-OSS 120B", - "family": "gpt-oss", + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75 + } + }, + "glm-for-coding": { + "id": "glm-for-coding", + "name": "glm-for-coding", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.04, "output": 0.4, "cache_read": 0.02, "cache_write": 0.08 }, - "limit": { "context": 131072, "output": 4096 } + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 0.086, + "output": 0.343 + } }, - "openai/gpt-oss-20b": { - "id": "openai/gpt-oss-20b", - "name": "GPT-OSS 20B", - "family": "gpt-oss", - "attachment": false, - "reasoning": false, + "claude-sonnet-4-5-20250929-thinking": { + "id": "claude-sonnet-4-5-20250929-thinking", + "name": "claude-sonnet-4-5-20250929-thinking", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.03, "output": 0.14, "cache_read": 0.015, "cache_write": 0.06 }, - "limit": { "context": 64000, "output": 4096 } + "knowledge": "2025-03", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "zai-org/GLM-4.6": { - "id": "zai-org/GLM-4.6", - "name": "GLM 4.6", + "glm-4.5-airx": { + "id": "glm-4.5-airx", + "name": "glm-4.5-airx", "family": "glm", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-11-15", - "last_updated": "2024-11-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-29", + "last_updated": "2025-07-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 1.75, "cache_read": 0.2, "cache_write": 0.8 }, - "limit": { "context": 200000, "output": 4096 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.572, + "output": 1.714 + } }, - "Intel/Qwen3-Coder-480B-A35B-Instruct-int4-mixed-ar": { - "id": "Intel/Qwen3-Coder-480B-A35B-Instruct-int4-mixed-ar", - "name": "Qwen 3 Coder 480B", - "family": "qwen", - "attachment": false, + "gpt-4.1": { + "id": "gpt-4.1", + "name": "gpt-4.1", + "family": "gpt", + "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-01-15", - "last_updated": "2025-01-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.22, "output": 0.95, "cache_read": 0.11, "cache_write": 0.44 }, - "limit": { "context": 106000, "output": 4096 } + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 8 + } }, - "meta-llama/Llama-3.3-70B-Instruct": { - "id": "meta-llama/Llama-3.3-70B-Instruct", - "name": "Llama 3.3 70B Instruct", - "family": "llama", + "kimi-k2-thinking": { + "id": "kimi-k2-thinking", + "name": "kimi-k2-thinking", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.13, "output": 0.38, "cache_read": 0.065, "cache_write": 0.26 }, - "limit": { "context": 128000, "output": 4096 } + "knowledge": "2025-06", + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.575, + "output": 2.3 + } }, - "meta-llama/Llama-3.2-90B-Vision-Instruct": { - "id": "meta-llama/Llama-3.2-90B-Vision-Instruct", - "name": "Llama 3.2 90B Vision Instruct", - "family": "llama", - "attachment": false, + "gemini-2.0-flash-lite": { + "id": "gemini-2.0-flash-lite", + "name": "gemini-2.0-flash-lite", + "family": "gemini-flash-lite", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "knowledge": "2024-11", + "release_date": "2025-06-16", + "last_updated": "2025-06-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 8192 + }, + "cost": { + "input": 0.075, + "output": 0.3 + } + }, + "gpt-4.1-mini": { + "id": "gpt-4.1-mini", + "name": "gpt-4.1-mini", + "family": "gpt-mini", + "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.35, "output": 0.4, "cache_read": 0.175, "cache_write": 0.7 }, - "limit": { "context": 16000, "output": 4096 } + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 1.6 + } }, - "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8": { - "id": "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8", - "name": "Llama 4 Maverick 17B 128E Instruct", - "family": "llama", - "attachment": false, + "grok-4-fast-non-reasoning": { + "id": "grok-4-fast-non-reasoning", + "name": "grok-4-fast-non-reasoning", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-01-15", - "last_updated": "2025-01-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.6, "cache_read": 0.075, "cache_write": 0.3 }, - "limit": { "context": 430000, "output": 4096 } + "knowledge": "2025-06", + "release_date": "2025-09-23", + "last_updated": "2025-09-23", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5 + } }, - "deepseek-ai/DeepSeek-R1-0528": { - "id": "deepseek-ai/DeepSeek-R1-0528", - "name": "DeepSeek R1", - "family": "deepseek-thinking", - "attachment": false, + "doubao-seed-1-6-thinking-250715": { + "id": "doubao-seed-1-6-thinking-250715", + "name": "doubao-seed-1-6-thinking-250715", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-01-20", - "last_updated": "2025-05-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2, "output": 8.75, "cache_read": 1, "cache_write": 4 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2025-07-15", + "last_updated": "2025-07-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 16000 + }, + "cost": { + "input": 0.121, + "output": 1.21 + } }, - "Qwen/Qwen3-235B-A22B-Thinking-2507": { - "id": "Qwen/Qwen3-235B-A22B-Thinking-2507", - "name": "Qwen 3 235B Thinking", + "ministral-14b-2512": { + "id": "ministral-14b-2512", + "name": "ministral-14b-2512", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2025-12-16", + "last_updated": "2025-12-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.33, + "output": 0.33 + } + } + } + }, + "alibaba": { + "id": "alibaba", + "env": ["DASHSCOPE_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://dashscope-intl.aliyuncs.com/compatible-mode/v1", + "name": "Alibaba", + "doc": "https://www.alibabacloud.com/help/en/model-studio/models", + "models": { + "qwen3-235b-a22b": { + "id": "qwen3-235b-a22b", + "name": "Qwen3 235B-A22B", "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-07-01", - "last_updated": "2025-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.11, "output": 0.6, "cache_read": 0.055, "cache_write": 0.22 }, - "limit": { "context": 262144, "output": 4096 } + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.7, + "output": 2.8, + "reasoning": 8.4 + } }, - "Qwen/Qwen3-Next-80B-A3B-Instruct": { - "id": "Qwen/Qwen3-Next-80B-A3B-Instruct", - "name": "Qwen 3 Next 80B Instruct", + "qwen3.5-122b-a10b": { + "id": "qwen3.5-122b-a10b", + "name": "Qwen3.5 122B-A10B", "family": "qwen", - "attachment": false, - "reasoning": false, + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-01-10", - "last_updated": "2025-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-23", + "last_updated": "2026-02-23", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.8, "cache_read": 0.05, "cache_write": 0.2 }, - "limit": { "context": 262144, "output": 4096 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.4, + "output": 3.2 + } }, - "Qwen/Qwen2.5-VL-32B-Instruct": { - "id": "Qwen/Qwen2.5-VL-32B-Instruct", - "name": "Qwen 2.5 VL 32B Instruct", + "qwen3-coder-plus": { + "id": "qwen3-coder-plus", + "name": "Qwen3 Coder Plus", "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-09", - "release_date": "2024-11-01", - "last_updated": "2024-11-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.05, "output": 0.22, "cache_read": 0.025, "cache_write": 0.1 }, - "limit": { "context": 32000, "output": 4096 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1, + "output": 5 + } }, - "moonshotai/Kimi-K2-Instruct-0905": { - "id": "moonshotai/Kimi-K2-Instruct-0905", - "name": "Kimi K2 Instruct", - "family": "kimi", - "attachment": false, - "reasoning": false, + "qwen3.6-27b": { + "id": "qwen3.6-27b", + "name": "Qwen3.6 27B", + "family": "qwen", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-08", - "release_date": "2024-09-05", - "last_updated": "2024-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.39, "output": 1.9, "cache_read": 0.195, "cache_write": 0.78 }, - "limit": { "context": 32768, "output": 4096 } + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.6, + "output": 3.6 + } }, - "moonshotai/Kimi-K2-Thinking": { - "id": "moonshotai/Kimi-K2-Thinking", - "name": "Kimi K2 Thinking", - "family": "kimi-thinking", - "attachment": false, + "qwen3.5-27b": { + "id": "qwen3.5-27b", + "name": "Qwen3.5 27B", + "family": "qwen", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-08", - "release_date": "2024-11-01", - "last_updated": "2024-11-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.55, "output": 2.25, "cache_read": 0.275, "cache_write": 1.1 }, - "limit": { "context": 32768, "output": 4096 } + "release_date": "2026-02-23", + "last_updated": "2026-02-23", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.4 + } }, - "mistralai/Devstral-Small-2505": { - "id": "mistralai/Devstral-Small-2505", - "name": "Devstral Small 2505", - "family": "devstral", + "qwen-vl-ocr": { + "id": "qwen-vl-ocr", + "name": "Qwen-VL OCR", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-05-01", - "last_updated": "2025-05-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-10-28", + "last_updated": "2025-04-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.05, "output": 0.22, "cache_read": 0.025, "cache_write": 0.1 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 34096, + "output": 4096 + }, + "cost": { + "input": 0.72, + "output": 0.72 + } }, - "mistralai/Mistral-Large-Instruct-2411": { - "id": "mistralai/Mistral-Large-Instruct-2411", - "name": "Mistral Large Instruct 2411", - "family": "mistral-large", + "qwen-omni-turbo-realtime": { + "id": "qwen-omni-turbo-realtime", + "name": "Qwen-Omni Turbo Realtime", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-11-01", - "last_updated": "2024-11-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-05-08", + "last_updated": "2025-05-08", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text", "audio"] + }, "open_weights": false, - "cost": { "input": 2, "output": 6, "cache_read": 1, "cache_write": 4 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 32768, + "output": 2048 + }, + "cost": { + "input": 0.27, + "output": 1.07, + "input_audio": 4.44, + "output_audio": 8.89 + } }, - "mistralai/Mistral-Nemo-Instruct-2407": { - "id": "mistralai/Mistral-Nemo-Instruct-2407", - "name": "Mistral Nemo Instruct 2407", - "family": "mistral-nemo", + "qwen3-8b": { + "id": "qwen3-8b", + "name": "Qwen3 8B", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-05", - "release_date": "2024-07-01", - "last_updated": "2024-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.02, "output": 0.04, "cache_read": 0.01, "cache_write": 0.04 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.18, + "output": 0.7, + "reasoning": 2.1 + } }, - "mistralai/Magistral-Small-2506": { - "id": "mistralai/Magistral-Small-2506", - "name": "Magistral Small 2506", - "family": "magistral-small", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-01", - "last_updated": "2025-06-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 1.5, "cache_read": 0.25, "cache_write": 1 }, - "limit": { "context": 128000, "output": 4096 } - } - } - }, - "cohere": { - "id": "cohere", - "env": ["COHERE_API_KEY"], - "npm": "@ai-sdk/cohere", - "name": "Cohere", - "doc": "https://docs.cohere.com/docs/models", - "models": { - "command-a-reasoning-08-2025": { - "id": "command-a-reasoning-08-2025", - "name": "Command A Reasoning", - "family": "command-a", - "attachment": false, + "qwen3.5-397b-a17b": { + "id": "qwen3.5-397b-a17b", + "name": "Qwen3.5 397B-A17B", + "family": "qwen", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-06-01", - "release_date": "2025-08-21", - "last_updated": "2025-08-21", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-15", + "last_updated": "2026-02-15", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 256000, "output": 32000 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.6, + "output": 3.6 + } }, - "command-r-08-2024": { - "id": "command-r-08-2024", - "name": "Command R", - "family": "command-r", + "qwq-plus": { + "id": "qwq-plus", + "name": "QwQ Plus", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-06-01", - "release_date": "2024-08-30", - "last_updated": "2024-08-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 128000, "output": 4000 } + "knowledge": "2024-04", + "release_date": "2025-03-05", + "last_updated": "2025-03-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 2.4 + } }, - "command-a-translate-08-2025": { - "id": "command-a-translate-08-2025", - "name": "Command A Translate", - "family": "command-a", + "qwen-vl-plus": { + "id": "qwen-vl-plus", + "name": "Qwen-VL Plus", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-06-01", - "release_date": "2025-08-28", - "last_updated": "2025-08-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 8000, "output": 8000 } + "knowledge": "2024-04", + "release_date": "2024-01-25", + "last_updated": "2025-08-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.21, + "output": 0.63 + } }, - "command-a-03-2025": { - "id": "command-a-03-2025", - "name": "Command A", - "family": "command-a", + "qwen3-livetranslate-flash-realtime": { + "id": "qwen3-livetranslate-flash-realtime", + "name": "Qwen3-LiveTranslate Flash Realtime", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2024-06-01", - "release_date": "2025-03-13", - "last_updated": "2025-03-13", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 256000, "output": 8000 } + "knowledge": "2024-04", + "release_date": "2025-09-22", + "last_updated": "2025-09-22", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text", "audio"] + }, + "open_weights": false, + "limit": { + "context": 53248, + "output": 4096 + }, + "cost": { + "input": 10, + "output": 10, + "input_audio": 10, + "output_audio": 38 + } }, - "command-r-plus-08-2024": { - "id": "command-r-plus-08-2024", - "name": "Command R+", - "family": "command-r", + "qwen3-32b": { + "id": "qwen3-32b", + "name": "Qwen3 32B", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-06-01", - "release_date": "2024-08-30", - "last_updated": "2024-08-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 128000, "output": 4000 } - }, - "c4ai-aya-expanse-32b": { - "id": "c4ai-aya-expanse-32b", - "name": "Aya Expanse 32B", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2024-10-24", - "last_updated": "2024-10-24", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 128000, "output": 4000 } + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.7, + "output": 2.8, + "reasoning": 8.4 + } }, - "command-a-vision-07-2025": { - "id": "command-a-vision-07-2025", - "name": "Command A Vision", - "family": "command-a", + "qwen-max": { + "id": "qwen-max", + "name": "Qwen Max", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "knowledge": "2024-06-01", - "release_date": "2025-07-31", - "last_updated": "2025-07-31", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 128000, "output": 8000 } + "knowledge": "2024-04", + "release_date": "2024-04-03", + "last_updated": "2025-01-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "output": 8192 + }, + "cost": { + "input": 1.6, + "output": 6.4 + } }, - "command-r7b-arabic-02-2025": { - "id": "command-r7b-arabic-02-2025", - "name": "Command R7B Arabic", - "family": "command-r", + "qwen-plus": { + "id": "qwen-plus", + "name": "Qwen Plus", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-06-01", - "release_date": "2025-02-27", - "last_updated": "2025-02-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.0375, "output": 0.15 }, - "limit": { "context": 128000, "output": 4000 } + "knowledge": "2024-04", + "release_date": "2024-01-25", + "last_updated": "2025-09-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 1.2, + "reasoning": 4 + } }, - "c4ai-aya-vision-8b": { - "id": "c4ai-aya-vision-8b", - "name": "Aya Vision 8B", + "qwen3.6-35b-a3b": { + "id": "qwen3.6-35b-a3b", + "name": "Qwen3.6 35B-A3B", + "family": "qwen", "attachment": true, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-03-04", - "last_updated": "2025-05-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-04-17", + "last_updated": "2026-04-17", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, "open_weights": true, - "limit": { "context": 16000, "output": 4000 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.248, + "output": 1.485 + } }, - "command-r7b-12-2024": { - "id": "command-r7b-12-2024", - "name": "Command R7B", - "family": "command-r", + "qwen-omni-turbo": { + "id": "qwen-omni-turbo", + "name": "Qwen-Omni Turbo", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-06-01", - "release_date": "2024-02-27", - "last_updated": "2024-02-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.0375, "output": 0.15 }, - "limit": { "context": 128000, "output": 4000 } + "knowledge": "2024-04", + "release_date": "2025-01-19", + "last_updated": "2025-03-26", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text", "audio"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "output": 2048 + }, + "cost": { + "input": 0.07, + "output": 0.27, + "input_audio": 4.44, + "output_audio": 8.89 + } }, - "c4ai-aya-expanse-8b": { - "id": "c4ai-aya-expanse-8b", - "name": "Aya Expanse 8B", + "qwen-flash": { + "id": "qwen-flash", + "name": "Qwen Flash", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2024-10-24", - "last_updated": "2024-10-24", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 8000, "output": 4000 } + "knowledge": "2024-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 32768 + }, + "cost": { + "input": 0.05, + "output": 0.4 + } }, - "c4ai-aya-vision-32b": { - "id": "c4ai-aya-vision-32b", - "name": "Aya Vision 32B", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2025-03-04", - "last_updated": "2025-05-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "limit": { "context": 16000, "output": 4000 } - } - } - }, - "minimax-coding-plan": { - "id": "minimax-coding-plan", - "env": ["MINIMAX_API_KEY"], - "npm": "@ai-sdk/anthropic", - "api": "https://api.minimax.io/anthropic/v1", - "name": "MiniMax Coding Plan (minimax.io)", - "doc": "https://platform.minimax.io/docs/coding-plan/intro", - "models": { - "MiniMax-M2.7": { - "id": "MiniMax-M2.7", - "name": "MiniMax-M2.7", - "family": "minimax", + "qwen2-5-vl-7b-instruct": { + "id": "qwen2-5-vl-7b-instruct", + "name": "Qwen2.5-VL 7B Instruct", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-09", + "last_updated": "2024-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.35, + "output": 1.05 + } }, - "MiniMax-M2.1": { - "id": "MiniMax-M2.1", - "name": "MiniMax-M2.1", - "family": "minimax", + "qwen3.6-plus": { + "id": "qwen3.6-plus", + "name": "Qwen3.6 Plus", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 204800, "output": 131072 } + "knowledge": "2025-04", + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.05, + "cache_write": 0.625, + "tiers": [ + { + "input": 2, + "output": 6, + "cache_read": 0.2, + "cache_write": 2.5, + "tier": { + "type": "context", + "size": 256000 + } + } + ], + "context_over_200k": { + "input": 2, + "output": 6, + "cache_read": 0.2, + "cache_write": 2.5 + } + } }, - "MiniMax-M2.5": { - "id": "MiniMax-M2.5", - "name": "MiniMax-M2.5", - "family": "minimax", + "qwen3-max": { + "id": "qwen3-max", + "name": "Qwen3 Max", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } + "knowledge": "2025-04", + "release_date": "2025-09-23", + "last_updated": "2025-09-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 1.2, + "output": 6 + } }, - "MiniMax-M2": { - "id": "MiniMax-M2", - "name": "MiniMax-M2", - "family": "minimax", + "qwen3-omni-flash": { + "id": "qwen3-omni-flash", + "name": "Qwen3-Omni Flash", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-10-27", - "last_updated": "2025-10-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 196608, "output": 128000 } + "knowledge": "2024-04", + "release_date": "2025-09-15", + "last_updated": "2025-09-15", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text", "audio"] + }, + "open_weights": false, + "limit": { + "context": 65536, + "output": 16384 + }, + "cost": { + "input": 0.43, + "output": 1.66, + "input_audio": 3.81, + "output_audio": 15.11 + } }, - "MiniMax-M2.5-highspeed": { - "id": "MiniMax-M2.5-highspeed", - "name": "MiniMax-M2.5-highspeed", - "family": "minimax", + "qwen2-5-72b-instruct": { + "id": "qwen2-5-72b-instruct", + "name": "Qwen2.5 72B Instruct", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2026-02-13", - "last_updated": "2026-02-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-09", + "last_updated": "2024-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 1.4, + "output": 5.6 + } }, - "MiniMax-M2.7-highspeed": { - "id": "MiniMax-M2.7-highspeed", - "name": "MiniMax-M2.7-highspeed", - "family": "minimax", + "qwen3-vl-235b-a22b": { + "id": "qwen3-vl-235b-a22b", + "name": "Qwen3-VL 235B-A22B", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } - } - } - }, - "cloudflare-ai-gateway": { - "id": "cloudflare-ai-gateway", - "env": ["CLOUDFLARE_API_TOKEN", "CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_GATEWAY_ID"], - "npm": "ai-gateway-provider", - "name": "Cloudflare AI Gateway", - "doc": "https://developers.cloudflare.com/ai-gateway/", - "models": { - "openai/gpt-5.2-codex": { - "id": "openai/gpt-5.2-codex", - "name": "GPT-5.2 Codex", - "family": "gpt-codex", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.7, + "output": 2.8, + "reasoning": 8.4 + } + }, + "qwen3-asr-flash": { + "id": "qwen3-asr-flash", + "name": "Qwen3-ASR Flash", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": false, "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-09-08", + "last_updated": "2025-09-08", + "modalities": { + "input": ["audio"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 }, - "provider": { "npm": "ai-gateway-provider" } + "limit": { + "context": 53248, + "output": 4096 + }, + "cost": { + "input": 0.035, + "output": 0.035 + } }, - "openai/o3-mini": { - "id": "openai/o3-mini", - "name": "o3-mini", - "family": "o-mini", + "qwen3-next-80b-a3b-thinking": { + "id": "qwen3-next-80b-a3b-thinking", + "name": "Qwen3-Next 80B-A3B (Thinking)", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2024-12-20", - "last_updated": "2025-01-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-09", + "last_updated": "2025-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.5, + "output": 6 + } + }, + "qwen-mt-plus": { + "id": "qwen-mt-plus", + "name": "Qwen-MT Plus", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2025-01", + "last_updated": "2025-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.55 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 16384, + "output": 8192 + }, + "cost": { + "input": 2.46, + "output": 7.37 + } }, - "openai/gpt-4-turbo": { - "id": "openai/gpt-4-turbo", - "name": "GPT-4 Turbo", - "family": "gpt", - "attachment": true, + "qwen-vl-max": { + "id": "qwen-vl-max", + "name": "Qwen-VL Max", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": false, "temperature": true, - "knowledge": "2023-12", - "release_date": "2023-11-06", - "last_updated": "2024-04-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-04-08", + "last_updated": "2025-08-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 10, "output": 30 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 3.2 + } }, - "openai/gpt-4o": { - "id": "openai/gpt-4o", - "name": "GPT-4o", - "family": "gpt", - "attachment": true, + "qwen3-coder-flash": { + "id": "qwen3-coder-flash", + "name": "Qwen3 Coder Flash", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2023-09", - "release_date": "2024-05-13", - "last_updated": "2024-08-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 10, "cache_read": 1.25 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 1.5 + } }, - "openai/gpt-5.3-codex": { - "id": "openai/gpt-5.3-codex", - "name": "GPT-5.3 Codex", - "family": "gpt-codex", - "attachment": true, - "reasoning": true, + "qwen2-5-7b-instruct": { + "id": "qwen2-5-7b-instruct", + "name": "Qwen2.5 7B Instruct", + "family": "qwen", + "attachment": false, + "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 }, - "provider": { "npm": "ai-gateway-provider" } + "temperature": true, + "knowledge": "2024-04", + "release_date": "2024-09", + "last_updated": "2024-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.175, + "output": 0.7 + } }, - "openai/gpt-4o-mini": { - "id": "openai/gpt-4o-mini", - "name": "GPT-4o mini", - "family": "gpt-mini", - "attachment": true, + "qwen2-5-14b-instruct": { + "id": "qwen2-5-14b-instruct", + "name": "Qwen2.5 14B Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2023-09", - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.6, "cache_read": 0.08 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2024-04", + "release_date": "2024-09", + "last_updated": "2024-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.35, + "output": 1.4 + } }, - "openai/gpt-3.5-turbo": { - "id": "openai/gpt-3.5-turbo", - "name": "GPT-3.5-turbo", - "family": "gpt", + "qwen2-5-32b-instruct": { + "id": "qwen2-5-32b-instruct", + "name": "Qwen2.5 32B Instruct", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, + "tool_call": true, "temperature": true, - "knowledge": "2021-09-01", - "release_date": "2023-03-01", - "last_updated": "2023-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 1.5, "cache_read": 1.25 }, - "limit": { "context": 16385, "output": 4096 } + "knowledge": "2024-04", + "release_date": "2024-09", + "last_updated": "2024-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.7, + "output": 2.8 + } }, - "openai/gpt-5.4": { - "id": "openai/gpt-5.4", - "name": "GPT-5.4", - "family": "gpt", - "attachment": true, - "reasoning": true, + "qwen3-next-80b-a3b-instruct": { + "id": "qwen3-next-80b-a3b-instruct", + "name": "Qwen3-Next 80B-A3B Instruct", + "family": "qwen", + "attachment": false, + "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 15, "cache_read": 0.25 }, - "limit": { "context": 1050000, "input": 922000, "output": 128000 }, - "provider": { "npm": "ai-gateway-provider" } + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-09", + "last_updated": "2025-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.5, + "output": 2 + } }, - "openai/o1": { - "id": "openai/o1", - "name": "o1", - "family": "o", - "attachment": true, - "reasoning": true, + "qwen-plus-character-ja": { + "id": "qwen-plus-character-ja", + "name": "Qwen Plus Character (Japanese)", + "family": "qwen", + "attachment": false, + "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2023-09", - "release_date": "2024-12-05", - "last_updated": "2024-12-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2024-01", + "last_updated": "2024-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 60, "cache_read": 7.5 }, - "limit": { "context": 200000, "output": 100000 } - }, - "openai/o3": { - "id": "openai/o3", - "name": "o3", - "family": "o", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 8192, + "output": 512 + }, + "cost": { + "input": 0.5, + "output": 1.4 + } }, - "openai/gpt-4": { - "id": "openai/gpt-4", - "name": "GPT-4", - "family": "gpt", - "attachment": true, + "qwen3-omni-flash-realtime": { + "id": "qwen3-omni-flash-realtime", + "name": "Qwen3-Omni Flash Realtime", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": false, "temperature": true, - "knowledge": "2023-11", - "release_date": "2023-11-06", - "last_updated": "2024-04-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 30, "output": 60 }, - "limit": { "context": 8192, "output": 8192 } - }, - "openai/gpt-5.2": { - "id": "openai/gpt-5.2", - "name": "GPT-5.2", - "family": "gpt", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "output": 128000 } - }, - "openai/gpt-5.1": { - "id": "openai/gpt-5.1", - "name": "GPT-5.1", - "family": "gpt", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.13 }, - "limit": { "context": 400000, "output": 128000 } - }, - "openai/o3-pro": { - "id": "openai/o3-pro", - "name": "o3-pro", - "family": "o-pro", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2025-06-10", - "last_updated": "2025-06-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-09-15", + "last_updated": "2025-09-15", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text", "audio"] + }, "open_weights": false, - "cost": { "input": 20, "output": 80 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 65536, + "output": 16384 + }, + "cost": { + "input": 0.52, + "output": 1.99, + "input_audio": 4.57, + "output_audio": 18.13 + } }, - "openai/o4-mini": { - "id": "openai/o4-mini", - "name": "o4-mini", - "family": "o-mini", - "attachment": true, + "qwen3-vl-30b-a3b": { + "id": "qwen3-vl-30b-a3b", + "name": "Qwen3-VL 30B-A3B", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.28 }, - "limit": { "context": 200000, "output": 100000 } + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.2, + "output": 0.8, + "reasoning": 2.4 + } }, - "openai/gpt-5.1-codex": { - "id": "openai/gpt-5.1-codex", - "name": "GPT-5.1 Codex", - "family": "gpt-codex", - "attachment": true, + "qwen3-vl-plus": { + "id": "qwen3-vl-plus", + "name": "Qwen3-VL Plus", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-09-23", + "last_updated": "2025-09-23", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0.2, + "output": 1.6, + "reasoning": 4.8 + } }, - "anthropic/claude-3-opus": { - "id": "anthropic/claude-3-opus", - "name": "Claude Opus 3", - "family": "claude-opus", - "attachment": true, + "qwen3-coder-480b-a35b-instruct": { + "id": "qwen3-coder-480b-a35b-instruct", + "name": "Qwen3-Coder 480B-A35B Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-08-31", - "release_date": "2024-02-29", - "last_updated": "2024-02-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 4096 } + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 1.5, + "output": 7.5 + } }, - "anthropic/claude-3-sonnet": { - "id": "anthropic/claude-3-sonnet", - "name": "Claude Sonnet 3", - "family": "claude-sonnet", - "attachment": true, + "qwen3-coder-30b-a3b-instruct": { + "id": "qwen3-coder-30b-a3b-instruct", + "name": "Qwen3-Coder 30B-A3B Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-08-31", - "release_date": "2024-03-04", - "last_updated": "2024-03-04", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 0.3 }, - "limit": { "context": 200000, "output": 4096 } + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.45, + "output": 2.25 + } }, - "anthropic/claude-opus-4-5": { - "id": "anthropic/claude-opus-4-5", - "name": "Claude Opus 4.5 (latest)", - "family": "claude-opus", - "attachment": true, + "qwen-turbo": { + "id": "qwen-turbo", + "name": "Qwen Turbo", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-11-24", - "last_updated": "2025-11-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-11-01", + "last_updated": "2025-04-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 1000000, + "output": 16384 + }, + "cost": { + "input": 0.05, + "output": 0.2, + "reasoning": 0.5 + } }, - "anthropic/claude-opus-4-6": { - "id": "anthropic/claude-opus-4-6", - "name": "Claude Opus 4.6 (latest)", - "family": "claude-opus", - "attachment": true, - "reasoning": true, - "tool_call": true, + "qwen-mt-turbo": { + "id": "qwen-mt-turbo", + "name": "Qwen-MT Turbo", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2025-08-31", - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-01", + "last_updated": "2025-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { - "input": 5, - "output": 25, - "cache_read": 0.5, - "cache_write": 6.25, - "context_over_200k": { "input": 10, "output": 37.5, "cache_read": 1, "cache_write": 12.5 } + "limit": { + "context": 16384, + "output": 8192 }, - "limit": { "context": 1000000, "output": 128000 } + "cost": { + "input": 0.16, + "output": 0.49 + } }, - "anthropic/claude-sonnet-4-6": { - "id": "anthropic/claude-sonnet-4-6", - "name": "Claude Sonnet 4.6", - "family": "claude-sonnet", - "attachment": true, + "qwen3.6-max-preview": { + "id": "qwen3.6-max-preview", + "name": "Qwen3.6 Max Preview", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": true, "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2026-02-17", - "last_updated": "2026-02-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2026-04-20", + "last_updated": "2026-04-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75, - "context_over_200k": { "input": 6, "output": 22.5, "cache_read": 0.6, "cache_write": 7.5 } + "limit": { + "context": 262144, + "output": 65536 }, - "limit": { "context": 1000000, "output": 64000 }, - "provider": { "npm": "ai-gateway-provider" } + "cost": { + "input": 1.3, + "output": 7.8, + "cache_read": 0.13, + "cache_write": 1.625 + } }, - "anthropic/claude-opus-4-1": { - "id": "anthropic/claude-opus-4-1", - "name": "Claude Opus 4.1 (latest)", - "family": "claude-opus", - "attachment": true, - "reasoning": true, + "qwen2-5-omni-7b": { + "id": "qwen2-5-omni-7b", + "name": "Qwen2.5-Omni 7B", + "family": "qwen", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "knowledge": "2024-04", + "release_date": "2024-12", + "last_updated": "2024-12", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text", "audio"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 2048 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "input_audio": 6.76 + } }, - "anthropic/claude-sonnet-4": { - "id": "anthropic/claude-sonnet-4", - "name": "Claude Sonnet 4 (latest)", - "family": "claude-sonnet", - "attachment": true, + "qwen3.5-plus": { + "id": "qwen3.5-plus", + "name": "Qwen3.5 Plus", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2026-02-16", + "last_updated": "2026-02-16", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.4, + "output": 2.4, + "reasoning": 2.4 + } }, - "anthropic/claude-3.5-haiku": { - "id": "anthropic/claude-3.5-haiku", - "name": "Claude Haiku 3.5 (latest)", - "family": "claude-haiku", - "attachment": true, + "qwen2-5-vl-72b-instruct": { + "id": "qwen2-5-vl-72b-instruct", + "name": "Qwen2.5-VL 72B Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-07-31", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.8, "output": 4, "cache_read": 0.08, "cache_write": 1 }, - "limit": { "context": 200000, "output": 8192 } + "knowledge": "2024-04", + "release_date": "2024-09", + "last_updated": "2024-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 2.8, + "output": 8.4 + } }, - "anthropic/claude-3-5-haiku": { - "id": "anthropic/claude-3-5-haiku", - "name": "Claude Haiku 3.5 (latest)", - "family": "claude-haiku", - "attachment": true, - "reasoning": false, + "qvq-max": { + "id": "qvq-max", + "name": "QVQ Max", + "family": "qvq", + "attachment": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-07-31", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-03-25", + "last_updated": "2025-03-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.8, "output": 4, "cache_read": 0.08, "cache_write": 1 }, - "limit": { "context": 200000, "output": 8192 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 1.2, + "output": 4.8 + } }, - "anthropic/claude-sonnet-4-5": { - "id": "anthropic/claude-sonnet-4-5", - "name": "Claude Sonnet 4.5 (latest)", - "family": "claude-sonnet", - "attachment": true, + "qwen3-14b": { + "id": "qwen3-14b", + "name": "Qwen3 14B", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.35, + "output": 1.4, + "reasoning": 4.2 + } }, - "anthropic/claude-3.5-sonnet": { - "id": "anthropic/claude-3.5-sonnet", - "name": "Claude Sonnet 3.5 v2", - "family": "claude-sonnet", + "qwen3.5-35b-a3b": { + "id": "qwen3.5-35b-a3b", + "name": "Qwen3.5 35B-A3B", + "family": "qwen", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04-30", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-02-23", + "last_updated": "2026-02-23", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.25, + "output": 2 + } + } + } + }, + "scaleway": { + "id": "scaleway", + "env": ["SCALEWAY_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.scaleway.ai/v1", + "name": "Scaleway", + "doc": "https://www.scaleway.com/en/docs/generative-apis/", + "models": { + "qwen3-embedding-8b": { + "id": "qwen3-embedding-8b", + "name": "Qwen3 Embedding 8B", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2025-25-11", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 8192 } + "limit": { + "context": 32768, + "output": 4096 + }, + "cost": { + "input": 0.1, + "output": 0 + } }, - "anthropic/claude-3-haiku": { - "id": "anthropic/claude-3-haiku", - "name": "Claude Haiku 3", - "family": "claude-haiku", + "qwen3-235b-a22b-instruct-2507": { + "id": "qwen3-235b-a22b-instruct-2507", + "name": "Qwen3 235B A22B Instruct 2507", + "family": "qwen", "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-08-31", - "release_date": "2024-03-13", - "last_updated": "2024-03-13", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 1.25, "cache_read": 0.03, "cache_write": 0.3 }, - "limit": { "context": 200000, "output": 4096 } + "release_date": "2025-07-01", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 260000, + "output": 16384 + }, + "cost": { + "input": 0.75, + "output": 2.25 + } }, - "anthropic/claude-haiku-4-5": { - "id": "anthropic/claude-haiku-4-5", - "name": "Claude Haiku 4.5 (latest)", - "family": "claude-haiku", + "llama-3.3-70b-instruct": { + "id": "llama-3.3-70b-instruct", + "name": "Llama-3.3-70B-Instruct", + "family": "llama", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-02-28", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 64000 } + "knowledge": "2023-12", + "release_date": "2024-12-06", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 100000, + "output": 16384 + }, + "cost": { + "input": 0.9, + "output": 0.9 + } }, - "anthropic/claude-opus-4": { - "id": "anthropic/claude-opus-4", - "name": "Claude Opus 4 (latest)", - "family": "claude-opus", - "attachment": true, + "qwen3.5-397b-a17b": { + "id": "qwen3.5-397b-a17b", + "name": "Qwen3.5 397B A17B", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } - }, - "workers-ai/@cf/openai/gpt-oss-120b": { - "id": "workers-ai/@cf/openai/gpt-oss-120b", - "name": "GPT OSS 120B", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.35, "output": 0.75 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2025-04", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 16384 + }, + "cost": { + "input": 0.6, + "output": 3.6 + } }, - "workers-ai/@cf/openai/gpt-oss-20b": { - "id": "workers-ai/@cf/openai/gpt-oss-20b", - "name": "GPT OSS 20B", + "devstral-2-123b-instruct-2512": { + "id": "devstral-2-123b-instruct-2512", + "name": "Devstral 2 123B Instruct (2512)", + "family": "devstral", "attachment": false, "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.3 }, - "limit": { "context": 128000, "output": 16384 } - }, - "workers-ai/@cf/zai-org/glm-4.7-flash": { - "id": "workers-ai/@cf/zai-org/glm-4.7-flash", - "name": "GLM-4.7-Flash", - "family": "glm-flash", - "attachment": false, - "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-01-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01-07", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.06, "output": 0.4 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 256000, + "output": 16384 + }, + "cost": { + "input": 0.4, + "output": 2 + } }, - "workers-ai/@cf/nvidia/nemotron-3-120b-a12b": { - "id": "workers-ai/@cf/nvidia/nemotron-3-120b-a12b", - "name": "Nemotron 3 Super 120B", - "family": "nemotron", + "deepseek-r1-distill-llama-70b": { + "id": "deepseek-r1-distill-llama-70b", + "name": "DeepSeek R1 Distill Llama 70B", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "release_date": "2026-03-11", - "last_updated": "2026-03-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-07", + "release_date": "2025-01-20", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.5, "output": 1.5 }, - "limit": { "context": 256000, "output": 256000 } + "limit": { + "context": 32000, + "output": 8196 + }, + "cost": { + "input": 0.9, + "output": 0.9 + } }, - "workers-ai/@cf/myshell-ai/melotts": { - "id": "workers-ai/@cf/myshell-ai/melotts", - "name": "MyShell MeloTTS", - "family": "melotts", + "pixtral-12b-2409": { + "id": "pixtral-12b-2409", + "name": "Pixtral 12B 2409", + "family": "pixtral", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-09-25", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.2, + "output": 0.2 + } + }, + "whisper-large-v3": { + "id": "whisper-large-v3", + "name": "Whisper Large v3", + "family": "whisper", "attachment": false, "reasoning": false, "tool_call": false, + "temperature": false, + "knowledge": "2023-09", + "release_date": "2023-09-01", + "last_updated": "2026-03-17", + "modalities": { + "input": ["audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 0, + "output": 8192 + }, + "cost": { + "input": 0.003, + "output": 0 + } + }, + "voxtral-small-24b-2507": { + "id": "voxtral-small-24b-2507", + "name": "Voxtral Small 24B 2507", + "family": "voxtral", + "attachment": true, + "reasoning": false, + "tool_call": true, "temperature": true, - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-07-01", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.35 + } + }, + "gemma-3-27b-it": { + "id": "gemma-3-27b-it", + "name": "Gemma-3-27B-IT", + "family": "gemma", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2024-12-01", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 40000, + "output": 8192 + }, + "cost": { + "input": 0.25, + "output": 0.5 + } }, - "workers-ai/@cf/google/gemma-3-12b-it": { - "id": "workers-ai/@cf/google/gemma-3-12b-it", - "name": "Gemma 3 12B IT", + "bge-multilingual-gemma2": { + "id": "bge-multilingual-gemma2", + "name": "BGE Multilingual Gemma2", "family": "gemma", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-04-11", - "last_updated": "2025-04-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2024-07-26", + "last_updated": "2025-06-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.35, "output": 0.56 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 8191, + "output": 3072 + }, + "cost": { + "input": 0.1, + "output": 0 + } }, - "workers-ai/@cf/ibm-granite/granite-4.0-h-micro": { - "id": "workers-ai/@cf/ibm-granite/granite-4.0-h-micro", - "name": "IBM Granite 4.0 H Micro", - "family": "granite", + "qwen3-coder-30b-a3b-instruct": { + "id": "qwen3-coder-30b-a3b-instruct", + "name": "Qwen3-Coder 30B-A3B Instruct", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.017, "output": 0.11 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.2, + "output": 0.8 + } }, - "workers-ai/@cf/ai4bharat/indictrans2-en-indic-1B": { - "id": "workers-ai/@cf/ai4bharat/indictrans2-en-indic-1B", - "name": "IndicTrans2 EN-Indic 1B", - "family": "indictrans", + "mistral-small-3.2-24b-instruct-2506": { + "id": "mistral-small-3.2-24b-instruct-2506", + "name": "Mistral Small 3.2 24B Instruct (2506)", + "family": "mistral-small", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.34, "output": 0.34 }, - "limit": { "context": 128000, "output": 16384 } + "release_date": "2025-06-20", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.15, + "output": 0.35 + } }, - "workers-ai/@cf/deepseek-ai/deepseek-r1-distill-qwen-32b": { - "id": "workers-ai/@cf/deepseek-ai/deepseek-r1-distill-qwen-32b", - "name": "DeepSeek R1 Distill Qwen 32B", - "family": "deepseek-thinking", - "attachment": false, + "gpt-oss-120b": { + "id": "gpt-oss-120b", + "name": "GPT-OSS 120B", + "family": "gpt-oss", + "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 4.88 }, - "limit": { "context": 128000, "output": 16384 } + "release_date": "2024-01-01", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "workers-ai/@cf/qwen/qwq-32b": { - "id": "workers-ai/@cf/qwen/qwq-32b", - "name": "QwQ 32B", - "family": "qwen", - "attachment": false, + "mistral-nemo-instruct-2407": { + "id": "mistral-nemo-instruct-2407", + "name": "Mistral Nemo Instruct 2407", + "family": "mistral-nemo", + "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2025-04-11", - "last_updated": "2025-04-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.66, "output": 1 }, - "limit": { "context": 128000, "output": 16384 } + "release_date": "2024-07-25", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.2, + "output": 0.2 + } }, - "workers-ai/@cf/qwen/qwen3-30b-a3b-fp8": { - "id": "workers-ai/@cf/qwen/qwen3-30b-a3b-fp8", - "name": "Qwen3 30B A3B FP8", - "family": "qwen", + "llama-3.1-8b-instruct": { + "id": "llama-3.1-8b-instruct", + "name": "Llama 3.1 8B Instruct", + "family": "llama", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.051, "output": 0.34 }, - "limit": { "context": 128000, "output": 16384 } - }, - "workers-ai/@cf/qwen/qwen3-embedding-0.6b": { - "id": "workers-ai/@cf/qwen/qwen3-embedding-0.6b", - "name": "Qwen3 Embedding 0.6B", - "family": "qwen", + "knowledge": "2023-12", + "release_date": "2025-01-01", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.2, + "output": 0.2 + } + } + } + }, + "nano-gpt": { + "id": "nano-gpt", + "env": ["NANO_GPT_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://nano-gpt.com/api/v1", + "name": "NanoGPT", + "doc": "https://docs.nano-gpt.com", + "models": { + "glm-4-flash": { + "id": "glm-4-flash", + "name": "GLM-4 Flash", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2024-08-01", + "last_updated": "2024-08-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.012, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + }, + "cost": { + "input": 0.1003, + "output": 0.1003 + } }, - "workers-ai/@cf/qwen/qwen2.5-coder-32b-instruct": { - "id": "workers-ai/@cf/qwen/qwen2.5-coder-32b-instruct", - "name": "Qwen 2.5 Coder 32B Instruct", - "family": "qwen", + "Meta-Llama-3-1-8B-Instruct-FP8": { + "id": "Meta-Llama-3-1-8B-Instruct-FP8", + "name": "Llama 3.1 8B (decentralized)", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-04-11", - "last_updated": "2025-04-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.66, "output": 1 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0.02, + "output": 0.03 + } }, - "workers-ai/@cf/huggingface/distilbert-sst-2-int8": { - "id": "workers-ai/@cf/huggingface/distilbert-sst-2-int8", - "name": "DistilBERT SST-2 INT8", - "family": "distilbert", - "attachment": false, - "reasoning": false, + "claude-opus-4-thinking:32000": { + "id": "claude-opus-4-thinking:32000", + "name": "Claude 4 Opus Thinking (32K)", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + }, + "cost": { + "input": 14.994, + "output": 75.004 + } + }, + "gemini-2.5-pro-preview-05-06": { + "id": "gemini-2.5-pro-preview-05-06", + "name": "Gemini 2.5 Pro Preview 0506", + "attachment": true, + "reasoning": true, "tool_call": false, - "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-05-06", + "last_updated": "2025-05-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.026, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + }, + "cost": { + "input": 2.5, + "output": 10 + } }, - "workers-ai/@cf/facebook/bart-large-cnn": { - "id": "workers-ai/@cf/facebook/bart-large-cnn", - "name": "BART Large CNN", - "family": "bart", + "grok-3-mini-fast-beta": { + "id": "grok-3-mini-fast-beta", + "name": "Grok 3 Mini Fast Beta", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-04-09", - "last_updated": "2025-04-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 131072, + "input": 131072, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 4 + } }, - "workers-ai/@cf/baai/bge-base-en-v1.5": { - "id": "workers-ai/@cf/baai/bge-base-en-v1.5", - "name": "BGE Base EN v1.5", - "family": "bge", + "MiniMax-M2": { + "id": "MiniMax-M2", + "name": "MiniMax M2", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": false, - "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-10-25", + "last_updated": "2025-10-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.067, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 200000, + "input": 200000, + "output": 131072 + }, + "cost": { + "input": 0.17, + "output": 1.53 + } }, - "workers-ai/@cf/baai/bge-small-en-v1.5": { - "id": "workers-ai/@cf/baai/bge-small-en-v1.5", - "name": "BGE Small EN v1.5", - "family": "bge", + "command-a-reasoning-08-2025": { + "id": "command-a-reasoning-08-2025", + "name": "Cohere Command A (08/2025)", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-08-22", + "last_updated": "2025-08-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.02, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 256000, + "input": 256000, + "output": 8192 + }, + "cost": { + "input": 2.5, + "output": 10 + } }, - "workers-ai/@cf/baai/bge-large-en-v1.5": { - "id": "workers-ai/@cf/baai/bge-large-en-v1.5", - "name": "BGE Large EN v1.5", - "family": "bge", + "brave": { + "id": "brave", + "name": "Brave (Answers)", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2023-03-02", + "last_updated": "2024-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 8192, + "input": 8192, + "output": 8192 + }, + "cost": { + "input": 5, + "output": 5 + } }, - "workers-ai/@cf/baai/bge-reranker-base": { - "id": "workers-ai/@cf/baai/bge-reranker-base", - "name": "BGE Reranker Base", - "family": "bge", + "exa-research": { + "id": "exa-research", + "name": "Exa (Research)", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-04-09", - "last_updated": "2025-04-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-06-04", + "last_updated": "2025-06-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.0031, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 8192, + "input": 8192, + "output": 8192 + }, + "cost": { + "input": 2.5, + "output": 2.5 + } }, - "workers-ai/@cf/baai/bge-m3": { - "id": "workers-ai/@cf/baai/bge-m3", - "name": "BGE M3", - "family": "bge", + "Llama-3.3-70B-Nova": { + "id": "Llama-3.3-70B-Nova", + "name": "Llama 3.3 70B Nova", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.012, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "workers-ai/@cf/pipecat-ai/smart-turn-v2": { - "id": "workers-ai/@cf/pipecat-ai/smart-turn-v2", - "name": "Pipecat Smart Turn v2", - "family": "smart-turn", - "attachment": false, + "gemini-exp-1206": { + "id": "gemini-exp-1206", + "name": "Gemini 2.0 Pro 1206", + "attachment": true, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 2097152, + "input": 2097152, + "output": 8192 + }, + "cost": { + "input": 1.258, + "output": 4.998 + } }, - "workers-ai/@cf/moonshotai/kimi-k2.5": { - "id": "workers-ai/@cf/moonshotai/kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi", + "claude-opus-4-5-20251101": { + "id": "claude-opus-4-5-20251101", + "name": "Claude 4.5 Opus", "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 3, "cache_read": 0.1 }, - "limit": { "context": 256000, "output": 256000 } + "release_date": "2025-11-01", + "last_updated": "2025-11-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + }, + "cost": { + "input": 4.998, + "output": 25.007 + } }, - "workers-ai/@cf/deepgram/aura-2-en": { - "id": "workers-ai/@cf/deepgram/aura-2-en", - "name": "Deepgram Aura 2 (EN)", - "family": "aura", + "auto-model-basic": { + "id": "auto-model-basic", + "name": "Auto model (Basic)", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2024-06-01", + "last_updated": "2024-06-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 1000000 + }, + "cost": { + "input": 9.996, + "output": 19.992 + } }, - "workers-ai/@cf/deepgram/nova-3": { - "id": "workers-ai/@cf/deepgram/nova-3", - "name": "Deepgram Nova 3", - "family": "nova", + "jamba-mini": { + "id": "jamba-mini", + "name": "Jamba Mini", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 256000, + "input": 256000, + "output": 4096 + }, + "cost": { + "input": 0.1989, + "output": 0.408 + } }, - "workers-ai/@cf/deepgram/aura-2-es": { - "id": "workers-ai/@cf/deepgram/aura-2-es", - "name": "Deepgram Aura 2 (ES)", - "family": "aura", + "gemini-2.5-flash-lite-preview-09-2025": { + "id": "gemini-2.5-flash-lite-preview-09-2025", + "name": "Gemini 2.5 Flash Lite Preview (09/2025)", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } + }, + "yi-large": { + "id": "yi-large", + "name": "Yi Large", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2024-05-13", + "last_updated": "2024-05-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 32000, + "input": 32000, + "output": 4096 + }, + "cost": { + "input": 3.196, + "output": 3.196 + } }, - "workers-ai/@cf/mistral/mistral-7b-instruct-v0.1": { - "id": "workers-ai/@cf/mistral/mistral-7b-instruct-v0.1", - "name": "Mistral 7B Instruct v0.1", - "family": "mistral", + "auto-model-premium": { + "id": "auto-model-premium", + "name": "Auto model (Premium)", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2024-06-01", + "last_updated": "2024-06-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.11, "output": 0.19 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 1000000 + }, + "cost": { + "input": 9.996, + "output": 19.992 + } }, - "workers-ai/@cf/aisingapore/gemma-sea-lion-v4-27b-it": { - "id": "workers-ai/@cf/aisingapore/gemma-sea-lion-v4-27b-it", - "name": "Gemma SEA-LION v4 27B IT", - "family": "gemma", - "attachment": false, + "azure-gpt-4o": { + "id": "azure-gpt-4o", + "name": "Azure gpt-4o", + "attachment": true, "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "release_date": "2024-05-13", + "last_updated": "2024-05-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.35, "output": 0.56 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 2.499, + "output": 9.996 + } }, - "workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast": { - "id": "workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast", - "name": "Llama 3.3 70B Instruct FP8 Fast", - "family": "llama", + "deepseek-v3-0324": { + "id": "deepseek-v3-0324", + "name": "DeepSeek Chat 0324", "attachment": false, "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "release_date": "2025-03-24", + "last_updated": "2025-03-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.29, "output": 2.25 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "cost": { + "input": 0.25, + "output": 0.7 + } }, - "workers-ai/@cf/meta/llama-3-8b-instruct-awq": { - "id": "workers-ai/@cf/meta/llama-3-8b-instruct-awq", - "name": "Llama 3 8B Instruct AWQ", - "family": "llama", - "attachment": false, + "claude-3-5-haiku-20241022": { + "id": "claude-3-5-haiku-20241022", + "name": "Claude 3.5 Haiku", + "attachment": true, "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.12, "output": 0.27 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 200000, + "input": 200000, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 4 + } }, - "workers-ai/@cf/meta/llama-3.2-1b-instruct": { - "id": "workers-ai/@cf/meta/llama-3.2-1b-instruct", - "name": "Llama 3.2 1B Instruct", - "family": "llama", + "doubao-seed-1-8-251215": { + "id": "doubao-seed-1-8-251215", + "name": "Doubao Seed 1.8", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-12-15", + "last_updated": "2025-12-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.027, "output": 0.2 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "cost": { + "input": 0.612, + "output": 6.12 + } }, - "workers-ai/@cf/meta/m2m100-1.2b": { - "id": "workers-ai/@cf/meta/m2m100-1.2b", - "name": "M2M100 1.2B", - "family": "m2m", + "doubao-seed-1-6-250615": { + "id": "doubao-seed-1-6-250615", + "name": "Doubao Seed 1.6", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-06-15", + "last_updated": "2025-06-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.34, "output": 0.34 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 256000, + "input": 256000, + "output": 16384 + }, + "cost": { + "input": 0.204, + "output": 0.51 + } }, - "workers-ai/@cf/meta/llama-3.1-8b-instruct": { - "id": "workers-ai/@cf/meta/llama-3.1-8b-instruct", - "name": "Llama 3.1 8B Instruct", - "family": "llama", - "attachment": false, + "ernie-x1.1-preview": { + "id": "ernie-x1.1-preview", + "name": "ERNIE X1.1", + "attachment": true, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-09-10", + "last_updated": "2025-09-10", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.28, "output": 0.8299999999999998 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 64000, + "input": 64000, + "output": 8192 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "workers-ai/@cf/meta/llama-4-scout-17b-16e-instruct": { - "id": "workers-ai/@cf/meta/llama-4-scout-17b-16e-instruct", - "name": "Llama 4 Scout 17B 16E Instruct", - "family": "llama", + "ernie-5.0-thinking-preview": { + "id": "ernie-5.0-thinking-preview", + "name": "Ernie 5.0 Thinking Preview", + "attachment": true, + "reasoning": true, + "tool_call": false, + "structured_output": false, + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 1.1, + "output": 2 + } + }, + "glm-4-air-0111": { + "id": "glm-4-air-0111", + "name": "GLM 4 Air 0111", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-01-11", + "last_updated": "2025-01-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.27, "output": 0.85 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + }, + "cost": { + "input": 0.1394, + "output": 0.1394 + } }, - "workers-ai/@cf/meta/llama-3.2-11b-vision-instruct": { - "id": "workers-ai/@cf/meta/llama-3.2-11b-vision-instruct", - "name": "Llama 3.2 11B Vision Instruct", - "family": "llama", + "fastgpt": { + "id": "fastgpt", + "name": "Web Answer", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2023-08-01", + "last_updated": "2024-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.049, "output": 0.68 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + }, + "cost": { + "input": 7.5, + "output": 7.5 + } }, - "workers-ai/@cf/meta/llama-3-8b-instruct": { - "id": "workers-ai/@cf/meta/llama-3-8b-instruct", - "name": "Llama 3 8B Instruct", - "family": "llama", + "doubao-seed-1-6-thinking-250615": { + "id": "doubao-seed-1-6-thinking-250615", + "name": "Doubao Seed 1.6 Thinking", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-06-15", + "last_updated": "2025-06-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.28, "output": 0.83 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 256000, + "input": 256000, + "output": 16384 + }, + "cost": { + "input": 0.204, + "output": 2.04 + } }, - "workers-ai/@cf/meta/llama-guard-3-8b": { - "id": "workers-ai/@cf/meta/llama-guard-3-8b", - "name": "Llama Guard 3 8B", - "family": "llama", + "gemini-2.0-flash-001": { + "id": "gemini-2.0-flash-001", + "name": "Gemini 2.0 Flash", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 8192 + }, + "cost": { + "input": 0.1003, + "output": 0.408 + } + }, + "claude-opus-4-1-thinking:32000": { + "id": "claude-opus-4-1-thinking:32000", + "name": "Claude 4.1 Opus Thinking (32K)", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + }, + "cost": { + "input": 14.994, + "output": 75.004 + } + }, + "Llama-3.3-70B-RAWMAW": { + "id": "Llama-3.3-70B-RAWMAW", + "name": "Llama 3.3 70B RAWMAW", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.48, "output": 0.03 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "workers-ai/@cf/meta/llama-3.2-3b-instruct": { - "id": "workers-ai/@cf/meta/llama-3.2-3b-instruct", - "name": "Llama 3.2 3B Instruct", - "family": "llama", + "GLM-4.5-Air-Derestricted-Steam": { + "id": "GLM-4.5-Air-Derestricted-Steam", + "name": "GLM 4.5 Air Derestricted Steam", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.051, "output": 0.34 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 220600, + "input": 220600, + "output": 65536 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "workers-ai/@cf/meta/llama-3.1-8b-instruct-awq": { - "id": "workers-ai/@cf/meta/llama-3.1-8b-instruct-awq", - "name": "Llama 3.1 8B Instruct AWQ", - "family": "llama", + "claude-3-5-sonnet-20241022": { + "id": "claude-3-5-sonnet-20241022", + "name": "Claude 3.5 Sonnet", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "release_date": "2025-08-26", + "last_updated": "2025-08-26", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "input": 200000, + "output": 8192 + }, + "cost": { + "input": 2.992, + "output": 14.994 + } + }, + "yi-medium-200k": { + "id": "yi-medium-200k", + "name": "Yi Medium 200k", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2024-03-01", + "last_updated": "2024-03-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.12, "output": 0.27 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 200000, + "input": 200000, + "output": 4096 + }, + "cost": { + "input": 2.499, + "output": 2.499 + } }, - "workers-ai/@cf/meta/llama-3.1-8b-instruct-fp8": { - "id": "workers-ai/@cf/meta/llama-3.1-8b-instruct-fp8", - "name": "Llama 3.1 8B Instruct FP8", - "family": "llama", + "Gemma-3-27B-ArliAI-RPMax-v3": { + "id": "Gemma-3-27B-ArliAI-RPMax-v3", + "name": "Gemma 3 27B RPMax v3", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-07-03", + "last_updated": "2025-07-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.29 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "workers-ai/@cf/meta/llama-2-7b-chat-fp16": { - "id": "workers-ai/@cf/meta/llama-2-7b-chat-fp16", - "name": "Llama 2 7B Chat FP16", - "family": "llama", + "phi-4-mini-instruct": { + "id": "phi-4-mini-instruct", + "name": "Phi 4 Mini", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-07-26", + "last_updated": "2025-07-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.56, "output": 6.67 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0.17, + "output": 0.68 + } }, - "workers-ai/@cf/pfnet/plamo-embedding-1b": { - "id": "workers-ai/@cf/pfnet/plamo-embedding-1b", - "name": "PLaMo Embedding 1B", - "family": "plamo", + "Llama-3.3+(3v3.3)-70B-TenyxChat-DaybreakStorywriter": { + "id": "Llama-3.3+(3v3.3)-70B-TenyxChat-DaybreakStorywriter", + "name": "Llama 3.3+ 70B TenyxChat DaybreakStorywriter", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.019, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "workers-ai/@cf/mistralai/mistral-small-3.1-24b-instruct": { - "id": "workers-ai/@cf/mistralai/mistral-small-3.1-24b-instruct", - "name": "Mistral Small 3.1 24B Instruct", - "family": "mistral-small", - "attachment": false, + "ernie-x1-32k": { + "id": "ernie-x1-32k", + "name": "Ernie X1 32k", + "attachment": true, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-04-11", - "last_updated": "2025-04-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-05-08", + "last_updated": "2025-05-08", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.35, "output": 0.56 }, - "limit": { "context": 128000, "output": 16384 } - } - } - }, - "wandb": { - "id": "wandb", - "env": ["WANDB_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.inference.wandb.ai/v1", - "name": "Weights & Biases", - "doc": "https://docs.wandb.ai/guides/integrations/inference/", - "models": { - "openai/gpt-oss-120b": { - "id": "openai/gpt-oss-120b", - "name": "gpt-oss-120b", - "family": "gpt-oss", - "attachment": false, + "limit": { + "context": 32000, + "input": 32000, + "output": 16384 + }, + "cost": { + "input": 0.33, + "output": 1.32 + } + }, + "deepseek-chat": { + "id": "deepseek-chat", + "name": "DeepSeek V3/Deepseek Chat", + "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-02-27", + "last_updated": "2025-02-27", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "cost": { + "input": 0.25, + "output": 0.7 + } }, - "openai/gpt-oss-20b": { - "id": "openai/gpt-oss-20b", - "name": "gpt-oss-20b", - "family": "gpt-oss", + "glm-z1-air": { + "id": "glm-z1-air", + "name": "GLM Z1 Air", "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-04-15", + "last_updated": "2025-04-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.05, "output": 0.2 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 32000, + "input": 32000, + "output": 16384 + }, + "cost": { + "input": 0.07, + "output": 0.07 + } }, - "microsoft/Phi-4-mini-instruct": { - "id": "microsoft/Phi-4-mini-instruct", - "name": "Phi-4-mini-instruct", - "family": "phi", - "attachment": false, + "claude-3-7-sonnet-thinking:128000": { + "id": "claude-3-7-sonnet-thinking:128000", + "name": "Claude 3.7 Sonnet Thinking (128K)", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-12-11", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.08, "output": 0.35 }, - "limit": { "context": 128000, "output": 128000 } + "release_date": "2025-02-24", + "last_updated": "2025-02-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "input": 200000, + "output": 64000 + }, + "cost": { + "input": 2.992, + "output": 14.994 + } }, - "zai-org/GLM-5-FP8": { - "id": "zai-org/GLM-5-FP8", - "name": "GLM 5", - "family": "glm", + "glm-4-air": { + "id": "glm-4-air", + "name": "GLM-4 Air", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1, "output": 3.2 }, - "limit": { "context": 200000, "output": 200000 } + "tool_call": false, + "structured_output": false, + "release_date": "2024-06-05", + "last_updated": "2024-06-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + }, + "cost": { + "input": 0.2006, + "output": 0.2006 + } }, - "nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-FP8": { - "id": "nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-FP8", - "name": "NVIDIA Nemotron 3 Super 120B", - "family": "nemotron", + "Llama-3.3-70B-MiraiFanfare": { + "id": "Llama-3.3-70B-MiraiFanfare", + "name": "Llama 3.3 70b Mirai Fanfare", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-03-11", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 262144, "output": 262144 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-26", + "last_updated": "2025-07-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.493, + "output": 0.493 + } }, - "OpenPipe/Qwen3-14B-Instruct": { - "id": "OpenPipe/Qwen3-14B-Instruct", - "name": "OpenPipe Qwen3 14B Instruct", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-04-29", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.05, "output": 0.22 }, - "limit": { "context": 32768, "output": 32768 } - }, - "meta-llama/Llama-4-Scout-17B-16E-Instruct": { - "id": "meta-llama/Llama-4-Scout-17B-16E-Instruct", - "name": "Llama 4 Scout 17B 16E Instruct", - "family": "llama", - "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-01-31", - "last_updated": "2026-03-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.17, "output": 0.66 }, - "limit": { "context": 64000, "output": 64000 } - }, - "meta-llama/Llama-3.1-8B-Instruct": { - "id": "meta-llama/Llama-3.1-8B-Instruct", - "name": "Meta-Llama-3.1-8B-Instruct", - "family": "llama", - "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-07-23", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.22, "output": 0.22 }, - "limit": { "context": 128000, "output": 128000 } - }, - "meta-llama/Llama-3.3-70B-Instruct": { - "id": "meta-llama/Llama-3.3-70B-Instruct", - "name": "Llama-3.3-70B-Instruct", - "family": "llama", - "attachment": false, + "gemini-2.0-flash-thinking-exp-01-21": { + "id": "gemini-2.0-flash-thinking-exp-01-21", + "name": "Gemini 2.0 Flash Thinking 0121", + "attachment": true, "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-12-06", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.71, "output": 0.71 }, - "limit": { "context": 128000, "output": 128000 } - }, - "meta-llama/Llama-3.1-70B-Instruct": { - "id": "meta-llama/Llama-3.1-70B-Instruct", - "name": "Llama 3.1 70B", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2024-07-23", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.8, "output": 0.8 }, - "limit": { "context": 128000, "output": 128000 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-01-21", + "last_updated": "2025-01-21", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 8192 + }, + "cost": { + "input": 0.306, + "output": 1.003 + } }, - "MiniMaxAI/MiniMax-M2.5": { - "id": "MiniMaxAI/MiniMax-M2.5", - "name": "MiniMax M2.5", - "family": "minimax", + "Magistral-Small-2506": { + "id": "Magistral-Small-2506", + "name": "Magistral Small 2506", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 196608, "output": 196608 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 1.4 + } }, - "deepseek-ai/DeepSeek-V3.1": { - "id": "deepseek-ai/DeepSeek-V3.1", - "name": "DeepSeek V3.1", - "family": "deepseek", + "doubao-1.5-pro-32k": { + "id": "doubao-1.5-pro-32k", + "name": "Doubao 1.5 Pro 32k", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-08-21", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.55, "output": 1.65 }, - "limit": { "context": 161000, "output": 161000 } - }, - "Qwen/Qwen3-235B-A22B-Thinking-2507": { - "id": "Qwen/Qwen3-235B-A22B-Thinking-2507", - "name": "Qwen3-235B-A22B-Thinking-2507", - "family": "qwen", - "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-25", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.1 }, - "limit": { "context": 262144, "output": 262144 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-01-22", + "last_updated": "2025-01-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32000, + "input": 32000, + "output": 8192 + }, + "cost": { + "input": 0.1343, + "output": 0.3349 + } }, - "Qwen/Qwen3-Coder-480B-A35B-Instruct": { - "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct", - "name": "Qwen3-Coder-480B-A35B-Instruct", - "family": "qwen", + "venice-uncensored:web": { + "id": "venice-uncensored:web", + "name": "Venice Uncensored Web", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1, "output": 1.5 }, - "limit": { "context": 262144, "output": 262144 } + "tool_call": false, + "structured_output": false, + "release_date": "2024-05-01", + "last_updated": "2024-05-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 80000, + "input": 80000, + "output": 16384 + }, + "cost": { + "input": 0.4, + "output": 0.4 + } }, - "Qwen/Qwen3-30B-A3B-Instruct-2507": { - "id": "Qwen/Qwen3-30B-A3B-Instruct-2507", - "name": "Qwen3 30B A3B Instruct 2507", - "family": "qwen", + "glm-4": { + "id": "glm-4", + "name": "GLM-4", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-07-29", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 262144, "output": 262144 } + "tool_call": false, + "structured_output": false, + "release_date": "2024-01-16", + "last_updated": "2024-01-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + }, + "cost": { + "input": 14.994, + "output": 14.994 + } }, - "Qwen/Qwen3-235B-A22B-Instruct-2507": { - "id": "Qwen/Qwen3-235B-A22B-Instruct-2507", - "name": "Qwen3 235B A22B Instruct 2507", - "family": "qwen", + "qwen-max": { + "id": "qwen-max", + "name": "Qwen 2.5 Max", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04-28", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.1 }, - "limit": { "context": 262144, "output": 262144 } + "tool_call": false, + "structured_output": false, + "release_date": "2024-04-03", + "last_updated": "2024-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32000, + "input": 32000, + "output": 8192 + }, + "cost": { + "input": 1.5997, + "output": 6.392 + } }, - "moonshotai/Kimi-K2.5": { - "id": "moonshotai/Kimi-K2.5", - "name": "Kimi K2.5", - "family": "kimi", - "attachment": true, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, - "temperature": true, - "release_date": "2026-01-27", - "last_updated": "2026-03-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.5, "output": 2.85 }, - "limit": { "context": 262144, "output": 262144 } - } - } - }, - "qiniu-ai": { - "id": "qiniu-ai", - "env": ["QINIU_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.qnaigc.com/v1", - "name": "Qiniu", - "doc": "https://developer.qiniu.com/aitokenapi", - "models": { - "gemini-2.5-flash-lite": { - "id": "gemini-2.5-flash-lite", - "name": "Gemini 2.5 Flash Lite", + "qwen3-vl-235b-a22b-instruct-original": { + "id": "qwen3-vl-235b-a22b-instruct-original", + "name": "Qwen3 VL 235B A22B Instruct Original", "attachment": true, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 1048576, "output": 64000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + }, + "cost": { + "input": 0.5, + "output": 1.2 + } }, - "qwen3-235b-a22b-instruct-2507": { - "id": "qwen3-235b-a22b-instruct-2507", - "name": "Qwen3 235b A22B Instruct 2507", + "jamba-large-1.6": { + "id": "jamba-large-1.6", + "name": "Jamba Large 1.6", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-12", - "last_updated": "2025-08-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-03-12", + "last_updated": "2025-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 262144, "output": 64000 } + "limit": { + "context": 256000, + "input": 256000, + "output": 4096 + }, + "cost": { + "input": 1.989, + "output": 7.99 + } }, - "claude-4.5-opus": { - "id": "claude-4.5-opus", - "name": "Claude 4.5 Opus", - "attachment": true, + "qwen-plus": { + "id": "qwen-plus", + "name": "Qwen Plus", + "attachment": false, "reasoning": true, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-11-25", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2024-01-25", + "last_updated": "2024-01-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 200000, "output": 200000 } + "limit": { + "context": 995904, + "input": 995904, + "output": 32768 + }, + "cost": { + "input": 0.3995, + "output": 1.2002 + } }, - "doubao-1.5-vision-pro": { - "id": "doubao-1.5-vision-pro", - "name": "Doubao 1.5 Vision Pro", + "qwen25-vl-72b-instruct": { + "id": "qwen25-vl-72b-instruct", + "name": "Qwen25 VL 72b", "attachment": true, "reasoning": false, "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "release_date": "2025-05-10", + "last_updated": "2025-05-10", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 16000 } + "limit": { + "context": 32000, + "input": 32000, + "output": 32768 + }, + "cost": { + "input": 0.69989, + "output": 0.69989 + } }, - "qwen-turbo": { - "id": "qwen-turbo", - "name": "Qwen-Turbo", - "attachment": false, + "claude-sonnet-4-thinking:64000": { + "id": "claude-sonnet-4-thinking:64000", + "name": "Claude 4 Sonnet Thinking (64K)", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 1000000, "output": 4096 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 64000 + }, + "cost": { + "input": 2.992, + "output": 14.994 + } }, - "deepseek-v3-0324": { - "id": "deepseek-v3-0324", - "name": "DeepSeek-V3-0324", + "gemini-3-pro-preview": { + "id": "gemini-3-pro-preview", + "name": "Gemini 3 Pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12 + } + }, + "Llama-3.3+(3.1v3.3)-70B-New-Dawn-v1.1": { + "id": "Llama-3.3+(3.1v3.3)-70B-New-Dawn-v1.1", + "name": "Llama 3.3+ 70B New Dawn v1.1", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 16000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "deepseek-r1": { - "id": "deepseek-r1", - "name": "DeepSeek-R1", + "GLM-4.5-Air-Derestricted-Iceblink-ReExtract": { + "id": "GLM-4.5-Air-Derestricted-Iceblink-ReExtract", + "name": "GLM 4.5 Air Derestricted Iceblink ReExtract", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-12", + "last_updated": "2025-12-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 32000 } + "limit": { + "context": 131072, + "input": 131072, + "output": 98304 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "qwen3-max-preview": { - "id": "qwen3-max-preview", - "name": "Qwen3 Max Preview", + "universal-summarizer": { + "id": "universal-summarizer", + "name": "Universal Summarizer", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-09-06", - "last_updated": "2025-09-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2023-05-01", + "last_updated": "2024-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + }, + "cost": { + "input": 30, + "output": 30 + } }, - "claude-4.0-sonnet": { - "id": "claude-4.0-sonnet", - "name": "Claude 4.0 Sonnet", + "claude-sonnet-4-thinking:32768": { + "id": "claude-sonnet-4-thinking:32768", + "name": "Claude 4 Sonnet Thinking (32K)", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 64000 + }, + "cost": { + "input": 2.992, + "output": 14.994 + } }, - "doubao-1.5-pro-32k": { - "id": "doubao-1.5-pro-32k", - "name": "Doubao 1.5 Pro 32k", + "sarvan-medium": { + "id": "sarvan-medium", + "name": "Sarvam Medium", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 12000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0.25, + "output": 0.75 + } }, - "gemini-3.0-flash-preview": { - "id": "gemini-3.0-flash-preview", - "name": "Gemini 3.0 Flash Preview", + "claude-3-7-sonnet-thinking:8192": { + "id": "claude-3-7-sonnet-thinking:8192", + "name": "Claude 3.7 Sonnet Thinking (8K)", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2025-12-18", - "last_updated": "2025-12-18", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-02-24", + "last_updated": "2025-02-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 1000000, "output": 64000 } + "limit": { + "context": 200000, + "input": 200000, + "output": 64000 + }, + "cost": { + "input": 2.992, + "output": 14.994 + } }, - "deepseek-v3": { - "id": "deepseek-v3", - "name": "DeepSeek-V3", - "attachment": false, + "gemini-2.5-flash-preview-05-20": { + "id": "gemini-2.5-flash-preview-05-20", + "name": "Gemini 2.5 Flash 0520", + "attachment": true, "reasoning": false, "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-13", - "last_updated": "2025-08-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-05-20", + "last_updated": "2025-05-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 16000 } + "limit": { + "context": 1048000, + "input": 1048000, + "output": 65536 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "MiniMax-M1": { - "id": "MiniMax-M1", - "name": "MiniMax M1", + "GLM-4.5-Air-Derestricted-Iceblink-v2-ReExtract": { + "id": "GLM-4.5-Air-Derestricted-Iceblink-v2-ReExtract", + "name": "GLM 4.5 Air Derestricted Iceblink v2 ReExtract", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-12", + "last_updated": "2025-12-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 1000000, "output": 80000 } + "limit": { + "context": 131072, + "input": 131072, + "output": 65536 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "gemini-3.0-pro-image-preview": { - "id": "gemini-3.0-pro-image-preview", - "name": "Gemini 3.0 Pro Image Preview", - "attachment": true, + "Llama-3.3-70B-Fallen-v1": { + "id": "Llama-3.3-70B-Fallen-v1", + "name": "Llama 3.3 70B Fallen v1", + "attachment": false, "reasoning": false, "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-11-20", - "last_updated": "2025-11-20", - "modalities": { "input": ["text", "image"], "output": ["text", "image"] }, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 32768, "output": 8192 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "doubao-seed-2.0-lite": { - "id": "doubao-seed-2.0-lite", - "name": "Doubao Seed 2.0 Lite", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2026-02-14", - "last_updated": "2026-02-14", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": false, - "limit": { "context": 256000, "output": 32000 } - }, - "doubao-seed-2.0-mini": { - "id": "doubao-seed-2.0-mini", - "name": "Doubao Seed 2.0 Mini", + "qwen3-vl-235b-a22b-thinking": { + "id": "qwen3-vl-235b-a22b-thinking", + "name": "Qwen3 VL 235B A22B Thinking", "attachment": true, "reasoning": true, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2026-02-14", - "last_updated": "2026-02-14", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "release_date": "2025-08-26", + "last_updated": "2025-08-26", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 256000, "output": 32000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + }, + "cost": { + "input": 0.5, + "output": 6 + } }, - "qwen3-30b-a3b-thinking-2507": { - "id": "qwen3-30b-a3b-thinking-2507", - "name": "Qwen3 30b A3b Thinking 2507", - "attachment": false, + "claude-3-7-sonnet-thinking:32768": { + "id": "claude-3-7-sonnet-thinking:32768", + "name": "Claude 3.7 Sonnet Thinking (32K)", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2026-02-04", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "limit": { "context": 126000, "output": 32000 } - }, - "kimi-k2": { - "id": "kimi-k2", - "name": "Kimi K2", - "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-07-15", + "last_updated": "2025-07-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 200000, + "input": 200000, + "output": 64000 + }, + "cost": { + "input": 2.992, + "output": 14.994 + } }, - "qwen3-30b-a3b": { - "id": "qwen3-30b-a3b", - "name": "Qwen3 30B A3B", - "attachment": false, + "claude-3-7-sonnet-thinking:1024": { + "id": "claude-3-7-sonnet-thinking:1024", + "name": "Claude 3.7 Sonnet Thinking (1K)", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-02-24", + "last_updated": "2025-02-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 40000, "output": 4096 } + "limit": { + "context": 200000, + "input": 200000, + "output": 64000 + }, + "cost": { + "input": 2.992, + "output": 14.994 + } }, - "doubao-seed-2.0-pro": { - "id": "doubao-seed-2.0-pro", - "name": "Doubao Seed 2.0 Pro", + "claude-sonnet-4-5-20250929": { + "id": "claude-sonnet-4-5-20250929", + "name": "Claude Sonnet 4.5", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2026-02-14", - "last_updated": "2026-02-14", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 256000, "output": 128000 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 64000 + }, + "cost": { + "input": 2.992, + "output": 14.994 + } }, - "gpt-oss-120b": { - "id": "gpt-oss-120b", - "name": "gpt-oss-120b", + "Llama-3.3-70B-Vulpecula-R1": { + "id": "Llama-3.3-70B-Vulpecula-R1", + "name": "Llama 3.3 70B Vulpecula R1", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-06", - "last_updated": "2025-08-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "gemini-2.0-flash-lite": { - "id": "gemini-2.0-flash-lite", - "name": "Gemini 2.0 Flash Lite", + "claude-sonnet-4-thinking:8192": { + "id": "claude-sonnet-4-thinking:8192", + "name": "Claude 4 Sonnet Thinking (8K)", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 1048576, "output": 8192 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 64000 + }, + "cost": { + "input": 2.992, + "output": 14.994 + } }, - "doubao-seed-2.0-code": { - "id": "doubao-seed-2.0-code", - "name": "Doubao Seed 2.0 Code", + "gemini-2.5-pro": { + "id": "gemini-2.5-pro", + "name": "Gemini 2.5 Pro", "attachment": true, "reasoning": true, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2026-02-14", - "last_updated": "2026-02-14", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "release_date": "2025-06-05", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 256000, "output": 128000 } + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + }, + "cost": { + "input": 2.5, + "output": 10 + } }, - "doubao-seed-1.6": { - "id": "doubao-seed-1.6", - "name": "Doubao-Seed 1.6", - "attachment": true, - "reasoning": true, - "tool_call": true, + "Llama-3.3-70B-Ignition-v0.1": { + "id": "Llama-3.3-70B-Ignition-v0.1", + "name": "Llama 3.3 70B Ignition v0.1", + "attachment": false, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-15", - "last_updated": "2025-08-15", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 256000, "output": 32000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "glm-4.5": { - "id": "glm-4.5", - "name": "GLM 4.5", + "glm-4-plus-0111": { + "id": "glm-4-plus-0111", + "name": "GLM 4 Plus 0111", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 131072, "output": 98304 } + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + }, + "cost": { + "input": 9.996, + "output": 9.996 + } }, - "qwen2.5-vl-72b-instruct": { - "id": "qwen2.5-vl-72b-instruct", - "name": "Qwen 2.5 VL 72B Instruct", - "attachment": true, + "KAT-Coder-Air-V1": { + "id": "KAT-Coder-Air-V1", + "name": "KAT Coder Air V1", + "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, + "release_date": "2025-10-28", + "last_updated": "2025-10-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 128000, + "input": 128000, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.2 + } }, - "claude-3.7-sonnet": { - "id": "claude-3.7-sonnet", - "name": "Claude 3.7 Sonnet", - "attachment": true, - "reasoning": true, - "tool_call": true, + "deepseek-r1-sambanova": { + "id": "deepseek-r1-sambanova", + "name": "DeepSeek R1 Fast", + "attachment": false, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-02-20", + "last_updated": "2025-02-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 200000, "output": 128000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + }, + "cost": { + "input": 4.998, + "output": 6.987 + } }, - "doubao-seed-1.6-flash": { - "id": "doubao-seed-1.6-flash", - "name": "Doubao-Seed 1.6 Flash", - "attachment": true, + "deepseek-r1": { + "id": "deepseek-r1", + "name": "DeepSeek R1", + "attachment": false, "reasoning": true, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-15", - "last_updated": "2025-08-15", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 256000, "output": 32000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "cost": { + "input": 0.4, + "output": 1.7 + } }, - "gemini-3.0-pro-preview": { - "id": "gemini-3.0-pro-preview", - "name": "Gemini 3.0 Pro Preview", + "doubao-1-5-thinking-pro-250415": { + "id": "doubao-1-5-thinking-pro-250415", + "name": "Doubao 1.5 Thinking Pro", "attachment": true, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-11-19", - "last_updated": "2025-11-19", - "modalities": { "input": ["text", "image", "video", "pdf", "audio"], "output": ["text"] }, + "release_date": "2025-04-17", + "last_updated": "2025-04-17", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 1000000, "output": 64000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0.6, + "output": 2.4 + } }, - "qwen3-coder-480b-a35b-instruct": { - "id": "qwen3-coder-480b-a35b-instruct", - "name": "Qwen3 Coder 480B A35B Instruct", + "sonar-pro": { + "id": "sonar-pro", + "name": "Perplexity Pro", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-14", - "last_updated": "2025-08-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 262000, "output": 4096 } + "limit": { + "context": 200000, + "input": 200000, + "output": 128000 + }, + "cost": { + "input": 2.992, + "output": 14.994 + } }, - "qwen3-next-80b-a3b-thinking": { - "id": "qwen3-next-80b-a3b-thinking", - "name": "Qwen3 Next 80B A3B Thinking", + "Gemma-3-27B-it-Abliterated": { + "id": "Gemma-3-27B-it-Abliterated", + "name": "Gemma 3 27B IT Abliterated", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-09-12", - "last_updated": "2025-09-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-07-03", + "last_updated": "2025-07-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 32768, + "input": 32768, + "output": 96000 + }, + "cost": { + "input": 0.42, + "output": 0.42 + } }, - "gemini-2.5-pro": { - "id": "gemini-2.5-pro", - "name": "Gemini 2.5 Pro", + "deepseek-chat-cheaper": { + "id": "deepseek-chat-cheaper", + "name": "DeepSeek V3/Chat Cheaper", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "video", "audio"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-04-15", + "last_updated": "2025-04-15", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "cost": { + "input": 0.25, + "output": 0.7 + } }, - "qwen-max-2025-01-25": { - "id": "qwen-max-2025-01-25", - "name": "Qwen2.5-Max-2025-01-25", - "attachment": false, + "gemini-2.0-pro-exp-02-05": { + "id": "gemini-2.0-pro-exp-02-05", + "name": "Gemini 2.0 Pro 0205", + "attachment": true, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-02-05", + "last_updated": "2025-02-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 2097152, + "input": 2097152, + "output": 8192 + }, + "cost": { + "input": 1.989, + "output": 7.956 + } }, - "claude-4.0-opus": { - "id": "claude-4.0-opus", - "name": "Claude 4.0 Opus", + "azure-gpt-4o-mini": { + "id": "azure-gpt-4o-mini", + "name": "Azure gpt-4o-mini", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0.1496, + "output": 0.595 + } + }, + "Llama-3.3-70B-MS-Nevoria": { + "id": "Llama-3.3-70B-MS-Nevoria", + "name": "Llama 3.3 70B MS Nevoria", + "attachment": false, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "gemini-2.5-flash": { - "id": "gemini-2.5-flash", - "name": "Gemini 2.5 Flash", + "claude-opus-4-thinking": { + "id": "claude-opus-4-thinking", + "name": "Claude 4 Opus Thinking", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, + "release_date": "2025-07-15", + "last_updated": "2025-07-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + }, + "cost": { + "input": 14.994, + "output": 75.004 + } + }, + "Llama-3.3-70B-Sapphira-0.1": { + "id": "Llama-3.3-70B-Sapphira-0.1", + "name": "Llama 3.3 70B Sapphira 0.1", + "attachment": false, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 1048576, "output": 64000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "qwen3-235b-a22b-thinking-2507": { - "id": "qwen3-235b-a22b-thinking-2507", - "name": "Qwen3 235B A22B Thinking 2507", + "doubao-seed-code-preview-latest": { + "id": "doubao-seed-code-preview-latest", + "name": "Doubao Seed Code Preview", "attachment": false, "reasoning": true, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-12", - "last_updated": "2025-08-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 262144, "output": 4096 } + "limit": { + "context": 256000, + "input": 256000, + "output": 16384 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } }, - "qwen-vl-max-2025-01-25": { - "id": "qwen-vl-max-2025-01-25", - "name": "Qwen VL-MAX-2025-01-25", - "attachment": true, + "qwen-3.6-plus": { + "id": "qwen-3.6-plus", + "name": "Qwen 3.6 Plus", + "family": "qwen3.6", + "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 991800, + "output": 65536 + }, + "cost": { + "input": 0.45, + "output": 2.7 + } }, - "qwen3-next-80b-a3b-instruct": { - "id": "qwen3-next-80b-a3b-instruct", - "name": "Qwen3 Next 80B A3B Instruct", + "Llama-3.3-70B-ArliAI-RPMax-v1.4": { + "id": "Llama-3.3-70B-ArliAI-RPMax-v1.4", + "name": "Llama 3.3 70B RPMax v1.4", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-09-12", - "last_updated": "2025-09-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "claude-3.5-haiku": { - "id": "claude-3.5-haiku", - "name": "Claude 3.5 Haiku", + "mistral-small-31-24b-instruct": { + "id": "mistral-small-31-24b-instruct", + "name": "Mistral Small 31 24b Instruct", "attachment": true, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-26", - "last_updated": "2025-08-26", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-04-15", + "last_updated": "2025-04-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 200000, "output": 8192 } + "limit": { + "context": 128000, + "input": 128000, + "output": 131072 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } }, - "kling-v2-6": { - "id": "kling-v2-6", - "name": "Kling-V2 6", + "glm-4.1v-thinking-flashx": { + "id": "glm-4.1v-thinking-flashx", + "name": "GLM 4.1V Thinking FlashX", "attachment": true, "reasoning": false, "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2026-01-13", - "last_updated": "2026-01-13", - "modalities": { "input": ["text", "image", "video"], "output": ["video"] }, + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 99999999, "output": 99999999 } + "limit": { + "context": 64000, + "input": 64000, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 0.3 + } }, - "deepseek-v3.1": { - "id": "deepseek-v3.1", - "name": "DeepSeek-V3.1", + "hunyuan-t1-latest": { + "id": "hunyuan-t1-latest", + "name": "Hunyuan T1", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-19", - "last_updated": "2025-08-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-03-22", + "last_updated": "2025-03-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 32000 } + "limit": { + "context": 256000, + "input": 256000, + "output": 16384 + }, + "cost": { + "input": 0.17, + "output": 0.66 + } }, - "gemini-2.0-flash": { - "id": "gemini-2.0-flash", - "name": "Gemini 2.0 Flash", + "doubao-1-5-thinking-vision-pro-250428": { + "id": "doubao-1-5-thinking-vision-pro-250428", + "name": "Doubao 1.5 Thinking Vision Pro", "attachment": true, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, + "release_date": "2025-05-15", + "last_updated": "2025-05-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 1048576, "output": 8192 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0.55, + "output": 1.43 + } }, - "doubao-seed-1.6-thinking": { - "id": "doubao-seed-1.6-thinking", - "name": "Doubao-Seed 1.6 Thinking", + "asi1-mini": { + "id": "asi1-mini", + "name": "ASI1 Mini", "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2025-08-15", - "last_updated": "2025-08-15", - "modalities": { "input": ["image", "text", "video"], "output": ["text"] }, - "open_weights": false, - "limit": { "context": 256000, "output": 32000 } - }, - "qwen3-235b-a22b": { - "id": "qwen3-235b-a22b", - "name": "Qwen 3 235B A22B", - "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-03-25", + "last_updated": "2025-03-25", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 32000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 1, + "output": 1 + } }, - "qwen3-vl-30b-a3b-thinking": { - "id": "qwen3-vl-30b-a3b-thinking", - "name": "Qwen3-Vl 30b A3b Thinking", + "ernie-5.0-thinking-latest": { + "id": "ernie-5.0-thinking-latest", + "name": "Ernie 5.0 Thinking", "attachment": true, - "reasoning": false, - "tool_call": true, + "reasoning": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2026-02-09", - "last_updated": "2026-02-09", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 32000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 1.1, + "output": 2 + } }, - "doubao-1.5-thinking-pro": { - "id": "doubao-1.5-thinking-pro", - "name": "Doubao 1.5 Thinking Pro", + "Llama-3.3-70B-Incandescent-Malevolence": { + "id": "Llama-3.3-70B-Incandescent-Malevolence", + "name": "Llama 3.3 70B Incandescent Malevolence", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 16000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "claude-4.1-opus": { - "id": "claude-4.1-opus", - "name": "Claude 4.1 Opus", - "attachment": true, - "reasoning": true, - "tool_call": true, + "Llama-3.3-70B-Damascus-R1": { + "id": "Llama-3.3-70B-Damascus-R1", + "name": "Damascus R1", + "attachment": false, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-06", - "last_updated": "2025-08-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "glm-4.5-air": { - "id": "glm-4.5-air", - "name": "GLM 4.5 Air", + "Gemma-3-27B-Nidum-Uncensored": { + "id": "Gemma-3-27B-Nidum-Uncensored", + "name": "Gemma 3 27B Nidum Uncensored", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-08-08", + "last_updated": "2025-08-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 131000, "output": 4096 } + "limit": { + "context": 32768, + "input": 32768, + "output": 96000 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "qwen3.5-397b-a17b": { - "id": "qwen3.5-397b-a17b", - "name": "Qwen3.5 397B A17B", + "gemini-2.5-flash-lite-preview-09-2025-thinking": { + "id": "gemini-2.5-flash-lite-preview-09-2025-thinking", + "name": "Gemini 2.5 Flash Lite Preview (09/2025) – Thinking", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2026-02-22", - "last_updated": "2026-02-22", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } }, - "gemini-2.5-flash-image": { - "id": "gemini-2.5-flash-image", - "name": "Gemini 2.5 Flash Image", - "attachment": true, + "doubao-seed-2-0-pro-260215": { + "id": "doubao-seed-2-0-pro-260215", + "name": "Doubao Seed 2.0 Pro", + "attachment": false, "reasoning": false, "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-10-22", - "last_updated": "2025-10-22", - "modalities": { "input": ["text", "image"], "output": ["image"] }, + "release_date": "2026-02-14", + "last_updated": "2026-02-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 32768, "output": 8192 } + "limit": { + "context": 256000, + "input": 256000, + "output": 128000 + }, + "cost": { + "input": 0.782, + "output": 3.876 + } }, - "claude-4.5-sonnet": { - "id": "claude-4.5-sonnet", - "name": "Claude 4.5 Sonnet", + "gemini-3-pro-image-preview": { + "id": "gemini-3-pro-image-preview", + "name": "Gemini 3 Pro Image", "attachment": true, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12 + } }, - "deepseek-r1-0528": { - "id": "deepseek-r1-0528", - "name": "DeepSeek-R1-0528", + "Gemma-3-27B-CardProjector-v4": { + "id": "Gemma-3-27B-CardProjector-v4", + "name": "Gemma 3 27B CardProjector v4", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-03-10", + "last_updated": "2025-03-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 32000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "qwen3-max": { - "id": "qwen3-max", - "name": "Qwen3 Max", + "jamba-mini-1.7": { + "id": "jamba-mini-1.7", + "name": "Jamba Mini 1.7", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-09-24", - "last_updated": "2025-09-24", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 256000, + "input": 256000, + "output": 4096 + }, + "cost": { + "input": 0.1989, + "output": 0.408 + } }, - "gpt-oss-20b": { - "id": "gpt-oss-20b", - "name": "gpt-oss-20b", + "Llama-3.3-70B-Forgotten-Safeword-3.6": { + "id": "Llama-3.3-70B-Forgotten-Safeword-3.6", + "name": "Llama 3.3 70B Forgotten Safeword 3.6", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-06", - "last_updated": "2025-08-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "qwen3-32b": { - "id": "qwen3-32b", - "name": "Qwen3 32B", - "attachment": false, - "reasoning": true, - "tool_call": true, + "doubao-1-5-thinking-pro-vision-250415": { + "id": "doubao-1-5-thinking-pro-vision-250415", + "name": "Doubao 1.5 Thinking Pro Vision", + "attachment": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-04-15", + "last_updated": "2025-04-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 40000, "output": 4096 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0.6, + "output": 2.4 + } }, - "claude-4.5-haiku": { - "id": "claude-4.5-haiku", - "name": "Claude 4.5 Haiku", + "gemini-2.5-pro-preview-06-05": { + "id": "gemini-2.5-pro-preview-06-05", + "name": "Gemini 2.5 Pro Preview 0605", "attachment": true, "reasoning": true, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-10-16", - "last_updated": "2025-10-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-06-05", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + }, + "cost": { + "input": 2.5, + "output": 10 + } }, - "qwen2.5-vl-7b-instruct": { - "id": "qwen2.5-vl-7b-instruct", - "name": "Qwen 2.5 VL 7B Instruct", - "attachment": true, + "gemini-2.0-pro-reasoner": { + "id": "gemini-2.0-pro-reasoner", + "name": "Gemini 2.0 Pro Reasoner", + "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, + "release_date": "2025-02-05", + "last_updated": "2025-02-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + }, + "cost": { + "input": 1.292, + "output": 4.998 + } }, - "claude-3.5-sonnet": { - "id": "claude-3.5-sonnet", - "name": "Claude 3.5 Sonnet", + "doubao-seed-2-0-lite-260215": { + "id": "doubao-seed-2-0-lite-260215", + "name": "Doubao Seed 2.0 Lite", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2026-02-14", + "last_updated": "2026-02-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "input": 256000, + "output": 32000 + }, + "cost": { + "input": 0.1462, + "output": 0.8738 + } + }, + "gemini-2.5-flash-lite-preview-06-17": { + "id": "gemini-2.5-flash-lite-preview-06-17", + "name": "Gemini 2.5 Flash Lite Preview", "attachment": true, "reasoning": true, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-09-09", - "last_updated": "2025-09-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 200000, "output": 8200 } + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "mimo-v2-flash": { - "id": "mimo-v2-flash", - "name": "Mimo-V2-Flash", + "sonar-deep-research": { + "id": "sonar-deep-research", + "name": "Perplexity Deep Research", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-02-25", + "last_updated": "2025-02-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 256000, "output": 256000 } + "limit": { + "context": 60000, + "input": 60000, + "output": 128000 + }, + "cost": { + "input": 3.4, + "output": 13.6 + } }, - "qwen3-30b-a3b-instruct-2507": { - "id": "qwen3-30b-a3b-instruct-2507", - "name": "Qwen3 30b A3b Instruct 2507", + "Gemma-3-27B-it": { + "id": "Gemma-3-27B-it", + "name": "Gemma 3 27B IT", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2026-02-04", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-03-10", + "last_updated": "2025-03-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 32000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "meituan/longcat-flash-lite": { - "id": "meituan/longcat-flash-lite", - "name": "Meituan/Longcat-Flash-Lite", + "Llama-3.3-70B-GeneticLemonade-Unleashed-v3": { + "id": "Llama-3.3-70B-GeneticLemonade-Unleashed-v3", + "name": "Llama 3.3 70B GeneticLemonade Unleashed v3", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2026-02-06", - "last_updated": "2026-02-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 256000, "output": 320000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "meituan/longcat-flash-chat": { - "id": "meituan/longcat-flash-chat", - "name": "Meituan/Longcat-Flash-Chat", + "Gemma-3-27B-Glitter": { + "id": "Gemma-3-27B-Glitter", + "name": "Gemma 3 27B Glitter", "attachment": false, "reasoning": false, "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-11-05", - "last_updated": "2025-11-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-03-10", + "last_updated": "2025-03-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "openai/gpt-5": { - "id": "openai/gpt-5", - "name": "OpenAI/GPT-5", + "Llama-3.3-70B-The-Omega-Directive-Unslop-v2.1": { + "id": "Llama-3.3-70B-The-Omega-Directive-Unslop-v2.1", + "name": "Llama 3.3 70B Omega Directive Unslop v2.1", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-09-19", - "last_updated": "2025-09-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "openai/gpt-5.2": { - "id": "openai/gpt-5.2", - "name": "OpenAI/GPT-5.2", - "attachment": true, - "reasoning": true, - "tool_call": true, + "qwen3-30b-a3b-instruct-2507": { + "id": "qwen3-30b-a3b-instruct-2507", + "name": "Qwen3 30B A3B Instruct 2507", + "attachment": false, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-02-20", + "last_updated": "2025-02-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 256000, + "input": 256000, + "output": 32768 + }, + "cost": { + "input": 0.2, + "output": 0.5 + } }, - "x-ai/grok-4-fast": { - "id": "x-ai/grok-4-fast", - "name": "x-AI/Grok-4-Fast", + "gemini-2.5-flash-preview-09-2025-thinking": { + "id": "gemini-2.5-flash-preview-09-2025-thinking", + "name": "Gemini 2.5 Flash Preview (09/2025) – Thinking", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2025-09-20", - "last_updated": "2025-09-20", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 2000000, "output": 2000000 } + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5 + } }, - "x-ai/grok-4.1-fast": { - "id": "x-ai/grok-4.1-fast", - "name": "x-AI/Grok-4.1-Fast", - "attachment": false, + "gemini-2.5-flash": { + "id": "gemini-2.5-flash", + "name": "Gemini 2.5 Flash", + "attachment": true, "reasoning": true, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-11-20", - "last_updated": "2025-11-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-06-05", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 2000000, "output": 2000000 } + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5 + } }, - "x-ai/grok-4-fast-non-reasoning": { - "id": "x-ai/grok-4-fast-non-reasoning", - "name": "X-Ai/Grok-4-Fast-Non-Reasoning", + "deepclaude": { + "id": "deepclaude", + "name": "DeepClaude", "attachment": true, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-12-18", - "last_updated": "2025-12-18", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, + "release_date": "2025-02-01", + "last_updated": "2025-02-01", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 2000000, "output": 2000000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "x-ai/grok-code-fast-1": { - "id": "x-ai/grok-code-fast-1", - "name": "x-AI/Grok-Code-Fast 1", + "ernie-4.5-8k-preview": { + "id": "ernie-4.5-8k-preview", + "name": "Ernie 4.5 8k Preview", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-09-02", - "last_updated": "2025-09-02", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-03-25", + "last_updated": "2025-03-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 256000, "output": 10000 } + "limit": { + "context": 8000, + "input": 8000, + "output": 16384 + }, + "cost": { + "input": 0.66, + "output": 2.6 + } }, - "x-ai/grok-4.1-fast-non-reasoning": { - "id": "x-ai/grok-4.1-fast-non-reasoning", - "name": "X-Ai/Grok 4.1 Fast Non Reasoning", - "attachment": true, - "reasoning": true, - "tool_call": true, + "doubao-seed-2-0-mini-260215": { + "id": "doubao-seed-2-0-mini-260215", + "name": "Doubao Seed 2.0 Mini", + "attachment": false, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-12-19", - "last_updated": "2025-12-19", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, + "release_date": "2026-02-14", + "last_updated": "2026-02-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 2000000, "output": 2000000 } + "limit": { + "context": 256000, + "input": 256000, + "output": 32000 + }, + "cost": { + "input": 0.0493, + "output": 0.4845 + } }, - "x-ai/grok-4.1-fast-reasoning": { - "id": "x-ai/grok-4.1-fast-reasoning", - "name": "X-Ai/Grok 4.1 Fast Reasoning", + "gemini-3-pro-preview-thinking": { + "id": "gemini-3-pro-preview-thinking", + "name": "Gemini 3 Pro Thinking", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2025-12-19", - "last_updated": "2025-12-19", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 20000000, "output": 2000000 } + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12 + } }, - "x-ai/grok-4-fast-reasoning": { - "id": "x-ai/grok-4-fast-reasoning", - "name": "X-Ai/Grok-4-Fast-Reasoning", - "attachment": true, - "reasoning": true, - "tool_call": true, + "Llama-3.3-70B-GeneticLemonade-Opus": { + "id": "Llama-3.3-70B-GeneticLemonade-Opus", + "name": "Llama 3.3 70B GeneticLemonade Opus", + "attachment": false, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-12-18", - "last_updated": "2025-12-18", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 2000000, "output": 2000000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "deepseek/deepseek-v3.1-terminus-thinking": { - "id": "deepseek/deepseek-v3.1-terminus-thinking", - "name": "DeepSeek/DeepSeek-V3.1-Terminus-Thinking", + "v0-1.5-lg": { + "id": "v0-1.5-lg", + "name": "v0 1.5 LG", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-09-22", - "last_updated": "2025-09-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-07-04", + "last_updated": "2025-07-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 32000 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 64000 + }, + "cost": { + "input": 15, + "output": 75 + } }, - "deepseek/deepseek-v3.1-terminus": { - "id": "deepseek/deepseek-v3.1-terminus", - "name": "DeepSeek/DeepSeek-V3.1-Terminus", - "attachment": false, + "ernie-4.5-turbo-128k": { + "id": "ernie-4.5-turbo-128k", + "name": "Ernie 4.5 Turbo 128k", + "attachment": true, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-09-22", - "last_updated": "2025-09-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-05-08", + "last_updated": "2025-05-08", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 32000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0.132, + "output": 0.55 + } }, - "deepseek/deepseek-v3.2-exp-thinking": { - "id": "deepseek/deepseek-v3.2-exp-thinking", - "name": "DeepSeek/DeepSeek-V3.2-Exp-Thinking", + "KAT-Coder-Pro-V1": { + "id": "KAT-Coder-Pro-V1", + "name": "KAT Coder Pro V1", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-10-28", + "last_updated": "2025-10-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 32000 } + "limit": { + "context": 256000, + "input": 256000, + "output": 32768 + }, + "cost": { + "input": 1.5, + "output": 6 + } }, - "deepseek/deepseek-v3.2-exp": { - "id": "deepseek/deepseek-v3.2-exp", - "name": "DeepSeek/DeepSeek-V3.2-Exp", - "attachment": false, + "claude-3-5-sonnet-20240620": { + "id": "claude-3-5-sonnet-20240620", + "name": "Claude 3.5 Sonnet Old", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "release_date": "2024-06-20", + "last_updated": "2024-06-20", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 32000 } + "limit": { + "context": 200000, + "input": 200000, + "output": 8192 + }, + "cost": { + "input": 2.992, + "output": 14.994 + } }, - "deepseek/deepseek-math-v2": { - "id": "deepseek/deepseek-math-v2", - "name": "Deepseek/Deepseek-Math-V2", - "attachment": false, + "claude-opus-4-1-thinking:8192": { + "id": "claude-opus-4-1-thinking:8192", + "name": "Claude 4.1 Opus Thinking (8K)", + "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2025-12-04", - "last_updated": "2025-12-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 160000, "output": 160000 } + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + }, + "cost": { + "input": 14.994, + "output": 75.004 + } }, - "deepseek/deepseek-v3.2-251201": { - "id": "deepseek/deepseek-v3.2-251201", - "name": "Deepseek/DeepSeek-V3.2", + "gemini-2.0-flash-exp-image-generation": { + "id": "gemini-2.0-flash-exp-image-generation", + "name": "Gemini Text + Image", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 32000 } + "limit": { + "context": 32767, + "input": 32767, + "output": 8192 + }, + "cost": { + "input": 0.2, + "output": 0.8 + } }, - "z-ai/glm-4.6": { - "id": "z-ai/glm-4.6", - "name": "Z-AI/GLM 4.6", + "Llama-3.3-70B-Magnum-v4-SE": { + "id": "Llama-3.3-70B-Magnum-v4-SE", + "name": "Llama 3.3 70B Magnum v4 SE", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-10-11", - "last_updated": "2025-10-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 200000, "output": 200000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "z-ai/glm-5": { - "id": "z-ai/glm-5", - "name": "Z-Ai/GLM 5", + "glm-zero-preview": { + "id": "glm-zero-preview", + "name": "GLM Zero Preview", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 200000, "output": 128000 } + "limit": { + "context": 8000, + "input": 8000, + "output": 4096 + }, + "cost": { + "input": 1.802, + "output": 1.802 + } }, - "z-ai/autoglm-phone-9b": { - "id": "z-ai/autoglm-phone-9b", - "name": "Z-Ai/Autoglm Phone 9b", + "study_gpt-chatgpt-4o-latest": { + "id": "study_gpt-chatgpt-4o-latest", + "name": "Study Mode", "attachment": true, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2024-05-13", + "last_updated": "2024-05-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 12800, "output": 4096 } + "limit": { + "context": 200000, + "input": 200000, + "output": 16384 + }, + "cost": { + "input": 4.998, + "output": 14.994 + } }, - "z-ai/glm-4.7": { - "id": "z-ai/glm-4.7", - "name": "Z-Ai/GLM 4.7", + "glm-4-airx": { + "id": "glm-4-airx", + "name": "GLM-4 AirX", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-06-05", + "last_updated": "2024-06-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 200000, "output": 200000 } + "limit": { + "context": 8000, + "input": 8000, + "output": 4096 + }, + "cost": { + "input": 2.006, + "output": 2.006 + } }, - "stepfun-ai/gelab-zero-4b-preview": { - "id": "stepfun-ai/gelab-zero-4b-preview", - "name": "Stepfun-Ai/Gelab Zero 4b Preview", - "attachment": true, + "step-2-mini": { + "id": "step-2-mini", + "name": "Step-2 Mini", + "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2024-07-05", + "last_updated": "2024-07-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 8192, "output": 4096 } + "limit": { + "context": 8000, + "input": 8000, + "output": 4096 + }, + "cost": { + "input": 0.2006, + "output": 0.408 + } }, - "minimax/minimax-m2": { - "id": "minimax/minimax-m2", - "name": "Minimax/Minimax-M2", - "attachment": false, + "gemini-2.5-flash-preview-04-17:thinking": { + "id": "gemini-2.5-flash-preview-04-17:thinking", + "name": "Gemini 2.5 Flash Preview Thinking", + "attachment": true, "reasoning": true, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-10-28", - "last_updated": "2025-10-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-04-17", + "last_updated": "2025-04-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 200000, "output": 128000 } + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + }, + "cost": { + "input": 0.15, + "output": 3.5 + } }, - "minimax/minimax-m2.1": { - "id": "minimax/minimax-m2.1", - "name": "Minimax/Minimax-M2.1", + "Llama-3.3-70B-Mokume-Gane-R1": { + "id": "Llama-3.3-70B-Mokume-Gane-R1", + "name": "Llama 3.3 70B Mokume Gane R1", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 204800, "output": 128000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "minimax/minimax-m2.5": { - "id": "minimax/minimax-m2.5", - "name": "Minimax/Minimax-M2.5", + "deepseek-reasoner": { + "id": "deepseek-reasoner", + "name": "DeepSeek Reasoner", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 204800, "output": 128000 } + "limit": { + "context": 64000, + "input": 64000, + "output": 65536 + }, + "cost": { + "input": 0.4, + "output": 1.7 + } }, - "minimax/minimax-m2.5-highspeed": { - "id": "minimax/minimax-m2.5-highspeed", - "name": "Minimax/Minimax-M2.5 Highspeed", + "glm-z1-airx": { + "id": "glm-z1-airx", + "name": "GLM Z1 AirX", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2026-02-14", - "last_updated": "2026-02-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-04-15", + "last_updated": "2025-04-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 204800, "output": 128000 } + "limit": { + "context": 32000, + "input": 32000, + "output": 16384 + }, + "cost": { + "input": 0.7, + "output": 0.7 + } }, - "xiaomi/mimo-v2-flash": { - "id": "xiaomi/mimo-v2-flash", - "name": "Xiaomi/Mimo-V2-Flash", + "jamba-mini-1.6": { + "id": "jamba-mini-1.6", + "name": "Jamba Mini 1.6", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-12-26", - "last_updated": "2025-12-26", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-03-01", + "last_updated": "2025-03-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 256000, "output": 256000 } + "limit": { + "context": 256000, + "input": 256000, + "output": 4096 + }, + "cost": { + "input": 0.1989, + "output": 0.408 + } }, - "stepfun/step-3.5-flash": { - "id": "stepfun/step-3.5-flash", - "name": "Stepfun/Step-3.5 Flash", + "claude-opus-4-1-thinking": { + "id": "claude-opus-4-1-thinking", + "name": "Claude 4.1 Opus Thinking", "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + }, + "cost": { + "input": 14.994, + "output": 75.004 + } + }, + "grok-3-beta": { + "id": "grok-3-beta", + "name": "Grok 3 Beta", + "attachment": false, "reasoning": false, "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2026-02-02", - "last_updated": "2026-02-02", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 64000, "output": 4096 } + "limit": { + "context": 131072, + "input": 131072, + "output": 131072 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "moonshotai/kimi-k2-thinking": { - "id": "moonshotai/kimi-k2-thinking", - "name": "Kimi K2 Thinking", + "Llama-3.3-70B-Legion-V2.1": { + "id": "Llama-3.3-70B-Legion-V2.1", + "name": "Llama 3.3 70B Legion V2.1", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-11-07", - "last_updated": "2025-11-07", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 256000, "output": 100000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "moonshotai/kimi-k2.5": { - "id": "moonshotai/kimi-k2.5", - "name": "Moonshotai/Kimi-K2.5", - "attachment": true, + "sonar": { + "id": "sonar", + "name": "Perplexity Simple", + "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2026-01-28", - "last_updated": "2026-01-28", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 256000, "output": 256000 } + "limit": { + "context": 127000, + "input": 127000, + "output": 128000 + }, + "cost": { + "input": 1.003, + "output": 1.003 + } }, - "moonshotai/kimi-k2-0905": { - "id": "moonshotai/kimi-k2-0905", - "name": "Kimi K2 0905", - "attachment": false, + "z-image-turbo": { + "id": "z-image-turbo", + "name": "Z Image Turbo", + "attachment": true, "reasoning": false, - "tool_call": true, + "tool_call": false, "structured_output": false, "temperature": true, - "release_date": "2025-09-08", - "last_updated": "2025-09-08", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-11-27", + "last_updated": "2025-11-27", + "modalities": { + "input": ["text"], + "output": ["image"] + }, "open_weights": false, - "limit": { "context": 256000, "output": 100000 } - } - } - }, - "morph": { - "id": "morph", - "env": ["MORPH_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.morphllm.com/v1", - "name": "Morph", - "doc": "https://docs.morphllm.com/api-reference/introduction", - "models": { - "morph-v3-large": { - "id": "morph-v3-large", - "name": "Morph v3 Large", - "family": "morph", + "limit": { + "context": 0, + "output": 0 + } + }, + "GLM-4.5-Air-Derestricted-Iceblink-v2": { + "id": "GLM-4.5-Air-Derestricted-Iceblink-v2", + "name": "GLM 4.5 Air Derestricted Iceblink v2", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": false, - "release_date": "2024-08-15", - "last_updated": "2024-08-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.9, "output": 1.9 }, - "limit": { "context": 32000, "output": 32000 } + "limit": { + "context": 158600, + "input": 158600, + "output": 65536 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "auto": { - "id": "auto", - "name": "Auto", - "family": "auto", - "attachment": false, + "jamba-large": { + "id": "jamba-large", + "name": "Jamba Large", + "attachment": false, "reasoning": false, "tool_call": false, - "temperature": false, - "release_date": "2024-06-01", - "last_updated": "2024-06-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.85, "output": 1.55 }, - "limit": { "context": 32000, "output": 32000 } + "limit": { + "context": 256000, + "input": 256000, + "output": 4096 + }, + "cost": { + "input": 1.989, + "output": 7.99 + } }, - "morph-v3-fast": { - "id": "morph-v3-fast", - "name": "Morph v3 Fast", - "family": "morph", - "attachment": false, + "claude-3-7-sonnet-reasoner": { + "id": "claude-3-7-sonnet-reasoner", + "name": "Claude 3.7 Sonnet Reasoner", + "attachment": true, "reasoning": false, "tool_call": false, - "temperature": false, - "release_date": "2024-08-15", - "last_updated": "2024-08-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-03-29", + "last_updated": "2025-03-29", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.8, "output": 1.2 }, - "limit": { "context": 16000, "output": 16000 } - } - } - }, - "dinference": { - "id": "dinference", - "env": ["DINFERENCE_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.dinference.com/v1", - "name": "DInference", - "doc": "https://dinference.com", - "models": { - "gpt-oss-120b": { - "id": "gpt-oss-120b", - "name": "GPT OSS 120B", + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15 + } + }, + "ernie-4.5-turbo-vl-32k": { + "id": "ernie-4.5-turbo-vl-32k", + "name": "Ernie 4.5 Turbo VL 32k", + "attachment": true, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-05-08", + "last_updated": "2025-05-08", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32000, + "input": 32000, + "output": 16384 + }, + "cost": { + "input": 0.495, + "output": 1.43 + } + }, + "Mistral-Nemo-12B-Instruct-2407": { + "id": "Mistral-Nemo-12B-Instruct-2407", + "name": "Mistral Nemo 12B Instruct 2407", + "attachment": true, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + }, + "cost": { + "input": 0.01, + "output": 0.01 + } + }, + "doubao-seed-1-6-flash-250615": { + "id": "doubao-seed-1-6-flash-250615", + "name": "Doubao Seed 1.6 Flash", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2025-08", - "last_updated": "2025-08", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.0675, "output": 0.27 }, - "limit": { "context": 131072, "output": 32768 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-06-15", + "last_updated": "2025-06-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "input": 256000, + "output": 16384 + }, + "cost": { + "input": 0.0374, + "output": 0.374 + } }, - "glm-5": { - "id": "glm-5", - "name": "GLM-5", - "family": "glm", + "qwq-32b": { + "id": "qwq-32b", + "name": "Qwen: QwQ 32B", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-02", - "last_updated": "2026-02", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.75, "output": 2.4 }, - "limit": { "context": 200000, "output": 128000 } + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-04-15", + "last_updated": "2025-04-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 32768 + }, + "cost": { + "input": 0.25599999, + "output": 0.30499999 + } }, - "glm-4.7": { - "id": "glm-4.7", - "name": "GLM-4.7", - "family": "glm", + "Llama-3.3-70B-Strawberrylemonade-v1.2": { + "id": "Llama-3.3-70B-Strawberrylemonade-v1.2", + "name": "Llama 3.3 70B StrawberryLemonade v1.2", "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } + }, + "gemini-2.5-flash-preview-04-17": { + "id": "gemini-2.5-flash-preview-04-17", + "name": "Gemini 2.5 Flash Preview", + "attachment": true, "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12", - "last_updated": "2025-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.45, "output": 1.65 }, - "limit": { "context": 200000, "output": 128000 } - } - } - }, - "meganova": { - "id": "meganova", - "env": ["MEGANOVA_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.meganova.ai/v1", - "name": "Meganova", - "doc": "https://docs.meganova.ai", - "models": { - "zai-org/GLM-4.7": { - "id": "zai-org/GLM-4.7", - "name": "GLM-4.7", - "family": "glm", + "tool_call": false, + "structured_output": false, + "release_date": "2025-04-17", + "last_updated": "2025-04-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } + }, + "ernie-x1-turbo-32k": { + "id": "ernie-x1-turbo-32k", + "name": "Ernie X1 Turbo 32k", + "attachment": true, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-05-08", + "last_updated": "2025-05-08", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32000, + "input": 32000, + "output": 16384 + }, + "cost": { + "input": 0.165, + "output": 0.66 + } + }, + "deepseek-math-v2": { + "id": "deepseek-math-v2", + "name": "DeepSeek Math V2", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 202752, "output": 131072 } + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-03", + "last_updated": "2025-12-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + }, + "cost": { + "input": 0.6, + "output": 2.2 + } }, - "zai-org/GLM-5": { - "id": "zai-org/GLM-5", - "name": "GLM-5", - "family": "glm", + "Llama-3.3-70B-Electranova-v1.0": { + "id": "Llama-3.3-70B-Electranova-v1.0", + "name": "Llama 3.3 70B Electranova v1.0", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.8, "output": 2.56 }, - "limit": { "context": 202752, "output": 131072 } + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "zai-org/GLM-4.6": { - "id": "zai-org/GLM-4.6", - "name": "GLM-4.6", - "family": "glm", + "Llama-3.3-70B-ArliAI-RPMax-v2": { + "id": "Llama-3.3-70B-ArliAI-RPMax-v2", + "name": "Llama 3.3 70B ArliAI RPMax v2", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-08", + "last_updated": "2025-08-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } + }, + "qwen-image": { + "id": "qwen-image", + "name": "Qwen Image", + "attachment": true, + "reasoning": false, + "tool_call": false, + "structured_output": false, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.45, "output": 1.9 }, - "limit": { "context": 202752, "output": 131072 } + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, + "open_weights": false, + "limit": { + "context": 0, + "output": 0 + } }, - "meta-llama/Llama-3.3-70B-Instruct": { - "id": "meta-llama/Llama-3.3-70B-Instruct", - "name": "Llama 3.3 70B Instruct", - "family": "llama", + "Llama-3.3-70B-Cu-Mai-R1": { + "id": "Llama-3.3-70B-Cu-Mai-R1", + "name": "Llama 3.3 70B Cu Mai R1", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, + "tool_call": false, + "structured_output": false, "release_date": "2024-12-06", "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 131072, "output": 16384 } + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "MiniMaxAI/MiniMax-M2.1": { - "id": "MiniMaxAI/MiniMax-M2.1", - "name": "MiniMax M2.1", - "family": "minimax", + "GLM-4.5-Air-Derestricted-Iceblink": { + "id": "GLM-4.5-Air-Derestricted-Iceblink", + "name": "GLM 4.5 Air Derestricted Iceblink", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.28, "output": 1.2 }, - "limit": { "context": 196608, "output": 131072 } + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "input": 131072, + "output": 98304 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "MiniMaxAI/MiniMax-M2.5": { - "id": "MiniMaxAI/MiniMax-M2.5", - "name": "MiniMax M2.5", - "family": "minimax", + "Llama-3.3-70B-Bigger-Body": { + "id": "Llama-3.3-70B-Bigger-Body", + "name": "Llama 3.3 70B Bigger Body", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 204800, "output": 131072 } + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "deepseek-ai/DeepSeek-V3.1": { - "id": "deepseek-ai/DeepSeek-V3.1", - "name": "DeepSeek V3.1", - "family": "deepseek", + "Llama-3.3+(3.1v3.3)-70B-Hanami-x1": { + "id": "Llama-3.3+(3.1v3.3)-70B-Hanami-x1", + "name": "Llama 3.3+ 70B Hanami x1", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-08-25", - "last_updated": "2025-08-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.27, "output": 1 }, - "limit": { "context": 164000, "output": 164000 } + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "deepseek-ai/DeepSeek-V3.2-Exp": { - "id": "deepseek-ai/DeepSeek-V3.2-Exp", - "name": "DeepSeek V3.2 Exp", - "family": "deepseek", + "hunyuan-turbos-20250226": { + "id": "hunyuan-turbos-20250226", + "name": "Hunyuan Turbo S", "attachment": false, "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-02-27", + "last_updated": "2025-02-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 24000, + "input": 24000, + "output": 8192 + }, + "cost": { + "input": 0.187, + "output": 0.374 + } + }, + "gemini-2.5-flash-preview-09-2025": { + "id": "gemini-2.5-flash-preview-09-2025", + "name": "Gemini 2.5 Flash Preview (09/2025)", + "attachment": true, + "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-10-10", - "last_updated": "2025-10-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.27, "output": 0.4 }, - "limit": { "context": 164000, "output": 164000 } + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5 + } }, - "deepseek-ai/DeepSeek-V3-0324": { - "id": "deepseek-ai/DeepSeek-V3-0324", - "name": "DeepSeek V3 0324", - "family": "deepseek", + "GLM-4.6-Derestricted-v5": { + "id": "GLM-4.6-Derestricted-v5", + "name": "GLM 4.6 Derestricted v5", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2025-03-24", - "last_updated": "2025-03-24", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.25, "output": 0.88 }, - "limit": { "context": 163840, "output": 163840 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "input": 131072, + "output": 8192 + }, + "cost": { + "input": 0.4, + "output": 1.5 + } }, - "deepseek-ai/DeepSeek-R1-0528": { - "id": "deepseek-ai/DeepSeek-R1-0528", - "name": "DeepSeek R1 0528", - "family": "deepseek-thinking", + "glm-4-plus": { + "id": "glm-4-plus", + "name": "GLM-4 Plus", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": false, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-05-28", - "last_updated": "2025-05-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.5, "output": 2.15 }, - "limit": { "context": 163840, "output": 64000 } + "structured_output": false, + "release_date": "2024-08-01", + "last_updated": "2024-08-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + }, + "cost": { + "input": 7.497, + "output": 7.497 + } }, - "deepseek-ai/DeepSeek-V3.2": { - "id": "deepseek-ai/DeepSeek-V3.2", - "name": "DeepSeek V3.2", - "family": "deepseek", + "Gemma-3-27B-Big-Tiger-v3": { + "id": "Gemma-3-27B-Big-Tiger-v3", + "name": "Gemma 3 27B Big Tiger v3", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-12-03", - "last_updated": "2025-12-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.26, "output": 0.38 }, - "limit": { "context": 164000, "output": 164000 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-08", + "last_updated": "2025-08-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "Qwen/Qwen3.5-Plus": { - "id": "Qwen/Qwen3.5-Plus", - "name": "Qwen3.5 Plus", - "family": "qwen", + "brave-research": { + "id": "brave-research", + "name": "Brave (Research)", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-02", - "last_updated": "2026-02", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2023-03-02", + "last_updated": "2024-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 2.4, "reasoning": 2.4 }, - "limit": { "context": 1000000, "output": 65536 } + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + }, + "cost": { + "input": 5, + "output": 5 + } }, - "Qwen/Qwen2.5-VL-32B-Instruct": { - "id": "Qwen/Qwen2.5-VL-32B-Instruct", - "name": "Qwen2.5 VL 32B Instruct", - "family": "qwen", + "hidream": { + "id": "hidream", + "name": "Hidream", "attachment": true, "reasoning": false, - "tool_call": true, - "structured_output": true, + "tool_call": false, + "structured_output": false, "temperature": true, - "release_date": "2025-03-24", - "last_updated": "2025-03-24", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.6 }, - "limit": { "context": 16384, "output": 16384 } + "release_date": "2024-01-01", + "last_updated": "2024-01-01", + "modalities": { + "input": ["text"], + "output": ["image"] + }, + "open_weights": false, + "limit": { + "context": 0, + "output": 0 + } }, - "Qwen/Qwen3-235B-A22B-Instruct-2507": { - "id": "Qwen/Qwen3-235B-A22B-Instruct-2507", - "name": "Qwen3 235B A22B Instruct 2507", - "family": "qwen", + "qwen3-max-2026-01-23": { + "id": "qwen3-max-2026-01-23", + "name": "Qwen3 Max 2026-01-23", "attachment": false, "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2026-01-26", + "last_updated": "2026-01-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "input": 256000, + "output": 32768 + }, + "cost": { + "input": 1.2002, + "output": 6.001 + } + }, + "claude-opus-4-1-20250805": { + "id": "claude-opus-4-1-20250805", + "name": "Claude 4.1 Opus", + "attachment": true, + "reasoning": false, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.09, "output": 0.6 }, - "limit": { "context": 262000, "output": 262000 } + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + }, + "cost": { + "input": 14.994, + "output": 75.004 + } }, - "moonshotai/Kimi-K2.5": { - "id": "moonshotai/Kimi-K2.5", - "name": "Kimi K2.5", - "family": "kimi", - "attachment": false, - "reasoning": true, + "claude-haiku-4-5-20251001": { + "id": "claude-haiku-4-5-20251001", + "name": "Claude Haiku 4.5", + "attachment": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2026-01", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.45, "output": 2.8 }, - "limit": { "context": 262144, "output": 262144 } + "structured_output": true, + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "input": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5 + } }, - "moonshotai/Kimi-K2-Thinking": { - "id": "moonshotai/Kimi-K2-Thinking", - "name": "Kimi K2 Thinking", - "family": "kimi-thinking", + "MiniMax-M1": { + "id": "MiniMax-M1", + "name": "MiniMax M1", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-11-06", - "last_updated": "2025-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.6 }, - "limit": { "context": 262144, "output": 262144 } + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-06-16", + "last_updated": "2025-06-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 131072 + }, + "cost": { + "input": 0.1394, + "output": 1.3328 + } }, - "XiaomiMiMo/MiMo-V2-Flash": { - "id": "XiaomiMiMo/MiMo-V2-Flash", - "name": "MiMo V2 Flash", - "family": "mimo", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-12-01", - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 262144, "output": 32000 } + "gemini-2.5-flash-nothinking": { + "id": "gemini-2.5-flash-nothinking", + "name": "Gemini 2.5 Flash (No Thinking)", + "attachment": true, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-06-05", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5 + } }, - "mistralai/Mistral-Nemo-Instruct-2407": { - "id": "mistralai/Mistral-Nemo-Instruct-2407", - "name": "Mistral Nemo Instruct 2407", - "family": "mistral", + "exa-research-pro": { + "id": "exa-research-pro", + "name": "Exa (Research Pro)", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.02, "output": 0.04 }, - "limit": { "context": 131072, "output": 65536 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-06-04", + "last_updated": "2025-06-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 2.5 + } }, - "mistralai/Mistral-Small-3.2-24B-Instruct-2506": { - "id": "mistralai/Mistral-Small-3.2-24B-Instruct-2506", - "name": "Mistral Small 3.2 24B Instruct", - "family": "mistral-small", + "grok-3-fast-beta": { + "id": "grok-3-fast-beta", + "name": "Grok 3 Fast Beta", "attachment": true, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-06-20", - "last_updated": "2025-06-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 32768, "output": 8192 } - } - } - }, - "zai-coding-plan": { - "id": "zai-coding-plan", - "env": ["ZHIPU_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.z.ai/api/coding/paas/v4", - "name": "Z.AI Coding Plan", - "doc": "https://docs.z.ai/devpack/overview", - "models": { - "glm-4.7-flash": { - "id": "glm-4.7-flash", - "name": "GLM-4.7-Flash", - "family": "glm-flash", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-01-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 200000, "output": 131072 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "input": 131072, + "output": 131072 + }, + "cost": { + "input": 5, + "output": 25 + } }, - "glm-5-turbo": { - "id": "glm-5-turbo", - "name": "GLM-5-Turbo", - "family": "glm", - "attachment": false, + "claude-opus-4-5-20251101:thinking": { + "id": "claude-opus-4-5-20251101:thinking", + "name": "Claude 4.5 Opus Thinking", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, - "temperature": true, - "release_date": "2026-03-16", - "last_updated": "2026-03-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-11-01", + "last_updated": "2025-11-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 200000, "output": 131072 } + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + }, + "cost": { + "input": 4.998, + "output": 25.007 + } }, - "glm-4.5": { - "id": "glm-4.5", - "name": "GLM-4.5", - "family": "glm", - "attachment": false, + "gemini-2.5-pro-exp-03-25": { + "id": "gemini-2.5-pro-exp-03-25", + "name": "Gemini 2.5 Pro Experimental 0325", + "attachment": true, "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 131072, "output": 98304 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-03-25", + "last_updated": "2025-03-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + }, + "cost": { + "input": 2.5, + "output": 10 + } }, - "glm-4.7-flashx": { - "id": "glm-4.7-flashx", - "name": "GLM-4.7-FlashX", - "family": "glm-flash", - "attachment": false, + "claude-3-7-sonnet-thinking": { + "id": "claude-3-7-sonnet-thinking", + "name": "Claude 3.7 Sonnet Thinking", + "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-01-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.07, "output": 0.4, "cache_read": 0.01, "cache_write": 0 }, - "limit": { "context": 200000, "output": 131072 } + "structured_output": true, + "release_date": "2025-02-24", + "last_updated": "2025-02-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "input": 200000, + "output": 16000 + }, + "cost": { + "input": 2.992, + "output": 14.994 + } }, - "glm-4.6": { - "id": "glm-4.6", - "name": "GLM-4.6", - "family": "glm", - "attachment": false, + "claude-opus-4-thinking:8192": { + "id": "claude-opus-4-thinking:8192", + "name": "Claude 4 Opus Thinking (8K)", + "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } + "structured_output": true, + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + }, + "cost": { + "input": 14.994, + "output": 75.004 + } }, - "glm-4.6v": { - "id": "glm-4.6v", - "name": "GLM-4.6V", - "family": "glm", + "claude-sonnet-4-thinking:1024": { + "id": "claude-sonnet-4-thinking:1024", + "name": "Claude 4 Sonnet Thinking (1K)", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-08", - "last_updated": "2025-12-08", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 32768 } + "structured_output": true, + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 64000 + }, + "cost": { + "input": 2.992, + "output": 14.994 + } }, - "glm-4.5-flash": { - "id": "glm-4.5-flash", - "name": "GLM-4.5-Flash", - "family": "glm-flash", + "Llama-3.3-70B-Magnum-v4-SE-Cirrus-x1-SLERP": { + "id": "Llama-3.3-70B-Magnum-v4-SE-Cirrus-x1-SLERP", + "name": "Llama 3.3 70B Magnum v4 SE Cirrus x1 SLERP", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 131072, "output": 98304 } + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-26", + "last_updated": "2025-07-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "glm-5": { - "id": "glm-5", - "name": "GLM-5", - "family": "glm", + "step-r1-v-mini": { + "id": "step-r1-v-mini", + "name": "Step R1 V Mini", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-04-08", + "last_updated": "2025-04-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + }, + "cost": { + "input": 2.5, + "output": 11 + } }, - "glm-4.5-air": { - "id": "glm-4.5-air", - "name": "GLM-4.5-Air", - "family": "glm-air", + "ernie-x1-32k-preview": { + "id": "ernie-x1-32k-preview", + "name": "Ernie X1 32k", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 131072, "output": 98304 } - }, - "glm-4.5v": { - "id": "glm-4.5v", - "name": "GLM-4.5V", - "family": "glm", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-08-11", - "last_updated": "2025-08-11", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 64000, "output": 16384 } + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32000, + "input": 32000, + "output": 16384 + }, + "cost": { + "input": 0.33, + "output": 1.32 + } }, - "glm-5.1": { - "id": "glm-5.1", - "name": "GLM-5.1", - "family": "glm", + "Llama-3.3-70B-StrawberryLemonade-v1.0": { + "id": "Llama-3.3-70B-StrawberryLemonade-v1.0", + "name": "Llama 3.3 70B StrawberryLemonade v1.0", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, - "temperature": true, - "release_date": "2026-03-27", - "last_updated": "2026-03-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 200000, "output": 131072 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "glm-4.7": { - "id": "glm-4.7", - "name": "GLM-4.7", - "family": "glm", + "KAT-Coder-Exp-72B-1010": { + "id": "KAT-Coder-Exp-72B-1010", + "name": "KAT Coder Exp 72B 1010", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } - } - } - }, - "anthropic": { - "id": "anthropic", - "env": ["ANTHROPIC_API_KEY"], - "npm": "@ai-sdk/anthropic", - "name": "Anthropic", - "doc": "https://docs.anthropic.com/en/docs/about-claude/models", - "models": { - "claude-opus-4-5-20251101": { - "id": "claude-opus-4-5-20251101", - "name": "Claude Opus 4.5", - "family": "claude-opus", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-11-01", - "last_updated": "2025-11-01", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-10-28", + "last_updated": "2025-10-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.2 + } }, - "claude-opus-4-20250514": { - "id": "claude-opus-4-20250514", - "name": "Claude Opus 4", - "family": "claude-opus", + "gemini-2.5-pro-preview-03-25": { + "id": "gemini-2.5-pro-preview-03-25", + "name": "Gemini 2.5 Pro Preview 0325", "attachment": true, "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-03-25", + "last_updated": "2025-03-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + }, + "cost": { + "input": 2.5, + "output": 10 + } }, - "claude-opus-4-5": { - "id": "claude-opus-4-5", - "name": "Claude Opus 4.5 (latest)", - "family": "claude-opus", + "claude-opus-4-thinking:1024": { + "id": "claude-opus-4-thinking:1024", + "name": "Claude 4 Opus Thinking (1K)", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-11-24", - "last_updated": "2025-11-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + }, + "cost": { + "input": 14.994, + "output": 75.004 + } }, - "claude-3-7-sonnet-20250219": { - "id": "claude-3-7-sonnet-20250219", - "name": "Claude Sonnet 3.7", - "family": "claude-sonnet", + "claude-sonnet-4-20250514": { + "id": "claude-sonnet-4-20250514", + "name": "Claude 4 Sonnet", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2024-10-31", - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 200000, + "input": 200000, + "output": 64000 + }, + "cost": { + "input": 2.992, + "output": 14.994 + } }, - "claude-opus-4-6": { - "id": "claude-opus-4-6", - "name": "Claude Opus 4.6", - "family": "claude-opus", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-05", - "release_date": "2026-02-05", - "last_updated": "2026-03-13", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 1000000, "output": 128000 } - }, - "claude-sonnet-4-5-20250929": { - "id": "claude-sonnet-4-5-20250929", - "name": "Claude Sonnet 4.5", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } - }, - "claude-sonnet-4-6": { - "id": "claude-sonnet-4-6", - "name": "Claude Sonnet 4.6", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-08", - "release_date": "2026-02-17", - "last_updated": "2026-03-13", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "Llama-3.3-70B-Progenitor-V3.3": { + "id": "Llama-3.3-70B-Progenitor-V3.3", + "name": "Llama 3.3 70B Progenitor V3.3", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-26", + "last_updated": "2025-07-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 1000000, "output": 64000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "claude-3-5-haiku-20241022": { - "id": "claude-3-5-haiku-20241022", - "name": "Claude Haiku 3.5", - "family": "claude-haiku", - "attachment": true, + "Qwen2.5-32B-EVA-v0.2": { + "id": "Qwen2.5-32B-EVA-v0.2", + "name": "Qwen 2.5 32b EVA", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-07-31", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-09-01", + "last_updated": "2024-09-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.8, "output": 4, "cache_read": 0.08, "cache_write": 1 }, - "limit": { "context": 200000, "output": 8192 } + "limit": { + "context": 24576, + "input": 24576, + "output": 8192 + }, + "cost": { + "input": 0.493, + "output": 0.493 + } }, - "claude-sonnet-4-0": { - "id": "claude-sonnet-4-0", - "name": "Claude Sonnet 4 (latest)", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "brave-pro": { + "id": "brave-pro", + "name": "Brave (Pro)", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2023-03-02", + "last_updated": "2024-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 8192, + "input": 8192, + "output": 8192 + }, + "cost": { + "input": 5, + "output": 5 + } }, - "claude-3-haiku-20240307": { - "id": "claude-3-haiku-20240307", - "name": "Claude Haiku 3", - "family": "claude-haiku", - "attachment": true, + "step-2-16k-exp": { + "id": "step-2-16k-exp", + "name": "Step-2 16k Exp", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-08-31", - "release_date": "2024-03-13", - "last_updated": "2024-03-13", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-05", + "last_updated": "2024-07-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 1.25, "cache_read": 0.03, "cache_write": 0.3 }, - "limit": { "context": 200000, "output": 4096 } + "limit": { + "context": 16000, + "input": 16000, + "output": 8192 + }, + "cost": { + "input": 7.004, + "output": 19.992 + } }, - "claude-sonnet-4-20250514": { - "id": "claude-sonnet-4-20250514", - "name": "Claude Sonnet 4", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "Llama-3.3-70B-Fallen-R1-v1": { + "id": "Llama-3.3-70B-Fallen-R1-v1", + "name": "Llama 3.3 70B Fallen R1 v1", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "claude-opus-4-1": { - "id": "claude-opus-4-1", - "name": "Claude Opus 4.1 (latest)", - "family": "claude-opus", + "claude-sonnet-4-thinking": { + "id": "claude-sonnet-4-thinking", + "name": "Claude 4 Sonnet Thinking", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-02-24", + "last_updated": "2025-02-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 64000 + }, + "cost": { + "input": 2.992, + "output": 14.994 + } }, - "claude-3-opus-20240229": { - "id": "claude-3-opus-20240229", - "name": "Claude Opus 3", - "family": "claude-opus", - "attachment": true, + "doubao-1.5-pro-256k": { + "id": "doubao-1.5-pro-256k", + "name": "Doubao 1.5 Pro 256k", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-08-31", - "release_date": "2024-02-29", - "last_updated": "2024-02-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-03-12", + "last_updated": "2025-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 4096 } + "limit": { + "context": 256000, + "input": 256000, + "output": 16384 + }, + "cost": { + "input": 0.799, + "output": 1.445 + } }, - "claude-3-5-haiku-latest": { - "id": "claude-3-5-haiku-latest", - "name": "Claude Haiku 3.5 (latest)", - "family": "claude-haiku", + "claude-3-7-sonnet-20250219": { + "id": "claude-3-7-sonnet-20250219", + "name": "Claude 3.7 Sonnet", "attachment": true, "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2024-07-31", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.8, "output": 4, "cache_read": 0.08, "cache_write": 1 }, - "limit": { "context": 200000, "output": 8192 } + "limit": { + "context": 200000, + "input": 200000, + "output": 16000 + }, + "cost": { + "input": 2.992, + "output": 14.994 + } }, - "claude-3-5-sonnet-20240620": { - "id": "claude-3-5-sonnet-20240620", - "name": "Claude Sonnet 3.5", - "family": "claude-sonnet", - "attachment": true, + "learnlm-1.5-pro-experimental": { + "id": "learnlm-1.5-pro-experimental", + "name": "Gemini LearnLM Experimental", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-04-30", - "release_date": "2024-06-20", - "last_updated": "2024-06-20", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-05-14", + "last_updated": "2024-05-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 8192 } + "limit": { + "context": 32767, + "input": 32767, + "output": 8192 + }, + "cost": { + "input": 3.502, + "output": 10.506 + } }, - "claude-opus-4-1-20250805": { - "id": "claude-opus-4-1-20250805", - "name": "Claude Opus 4.1", - "family": "claude-opus", - "attachment": true, - "reasoning": true, + "qwen3-coder-30b-a3b-instruct": { + "id": "qwen3-coder-30b-a3b-instruct", + "name": "Qwen3 Coder 30B A3B Instruct", + "attachment": false, + "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", + "structured_output": true, "release_date": "2025-08-05", "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } }, - "claude-opus-4-0": { - "id": "claude-opus-4-0", - "name": "Claude Opus 4 (latest)", - "family": "claude-opus", + "chroma": { + "id": "chroma", + "name": "Chroma", "attachment": true, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, + "structured_output": false, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-08-12", + "last_updated": "2025-08-12", + "modalities": { + "input": ["text"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 0, + "output": 0 + } }, - "claude-haiku-4-5-20251001": { - "id": "claude-haiku-4-5-20251001", - "name": "Claude Haiku 4.5", - "family": "claude-haiku", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-02-28", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "Llama-3.3-70B-Predatorial-Extasy": { + "id": "Llama-3.3-70B-Predatorial-Extasy", + "name": "Llama 3.3 70B Predatorial Extasy", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "claude-sonnet-4-5": { - "id": "claude-sonnet-4-5", - "name": "Claude Sonnet 4.5 (latest)", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "Llama-3.3-70B-Aurora-Borealis": { + "id": "Llama-3.3-70B-Aurora-Borealis", + "name": "Llama 3.3 70B Aurora Borealis", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "claude-3-7-sonnet-latest": { - "id": "claude-3-7-sonnet-latest", - "name": "Claude Sonnet 3.7 (latest)", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-10-31", - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "Llama-3.3-70B-ArliAI-RPMax-v3": { + "id": "Llama-3.3-70B-ArliAI-RPMax-v3", + "name": "Llama 3.3 70B ArliAI RPMax v3", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "claude-haiku-4-5": { - "id": "claude-haiku-4-5", - "name": "Claude Haiku 4.5 (latest)", - "family": "claude-haiku", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-02-28", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "venice-uncensored": { + "id": "venice-uncensored", + "name": "Venice Uncensored", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-02-24", + "last_updated": "2025-02-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0.4, + "output": 0.4 + } }, - "claude-3-sonnet-20240229": { - "id": "claude-3-sonnet-20240229", - "name": "Claude Sonnet 3", - "family": "claude-sonnet", + "step-3": { + "id": "step-3", + "name": "Step-3", "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-08-31", - "release_date": "2024-03-04", - "last_updated": "2024-03-04", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-31", + "last_updated": "2025-07-31", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 0.3 }, - "limit": { "context": 200000, "output": 4096 } + "limit": { + "context": 65536, + "input": 65536, + "output": 8192 + }, + "cost": { + "input": 0.2499, + "output": 0.6494 + } }, - "claude-3-5-sonnet-20241022": { - "id": "claude-3-5-sonnet-20241022", - "name": "Claude Sonnet 3.5 v2", - "family": "claude-sonnet", - "attachment": true, + "Llama-3.3-70B-The-Omega-Directive-Unslop-v2.0": { + "id": "Llama-3.3-70B-The-Omega-Directive-Unslop-v2.0", + "name": "Llama 3.3 70B Omega Directive Unslop v2.0", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-04-30", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 8192 } - } - } - }, - "nova": { - "id": "nova", - "env": ["NOVA_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.nova.amazon.com/v1", - "name": "Nova", - "doc": "https://nova.amazon.com/dev/documentation", - "models": { - "nova-2-lite-v1": { - "id": "nova-2-lite-v1", - "name": "Nova 2 Lite", - "family": "nova-lite", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text", "image", "video", "pdf"], "output": ["text"] }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } + }, + "auto-model": { + "id": "auto-model", + "name": "Auto model", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-06-01", + "last_updated": "2024-06-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0, "reasoning": 0 }, - "limit": { "context": 1000000, "output": 64000 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 1000000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "nova-2-pro-v1": { - "id": "nova-2-pro-v1", - "name": "Nova 2 Pro", - "family": "nova-pro", + "claude-opus-4-1-thinking:32768": { + "id": "claude-opus-4-1-thinking:32768", + "name": "Claude 4.1 Opus Thinking (32K)", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2025-12-03", - "last_updated": "2026-01-03", - "modalities": { "input": ["text", "image", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0, "reasoning": 0 }, - "limit": { "context": 1000000, "output": 64000 } - } - } - }, - "upstage": { - "id": "upstage", - "env": ["UPSTAGE_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.upstage.ai/v1/solar", - "name": "Upstage", - "doc": "https://developers.upstage.ai/docs/apis/chat", - "models": { - "solar-pro3": { - "id": "solar-pro3", - "name": "solar-pro3", - "family": "solar-pro", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-03", - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 0.25 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + }, + "cost": { + "input": 14.994, + "output": 75.004 + } }, - "solar-pro2": { - "id": "solar-pro2", - "name": "solar-pro2", - "family": "solar-pro", + "Llama-3.3-70B-Shakudo": { + "id": "Llama-3.3-70B-Shakudo", + "name": "Llama 3.3 70B Shakudo", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-05-20", - "last_updated": "2025-05-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 0.25 }, - "limit": { "context": 65536, "output": 8192 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "solar-mini": { - "id": "solar-mini", - "name": "solar-mini", - "family": "solar-mini", + "Baichuan4-Air": { + "id": "Baichuan4-Air", + "name": "Baichuan 4 Air", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-09", - "release_date": "2024-06-12", - "last_updated": "2025-04-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-19", + "last_updated": "2025-08-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.15 }, - "limit": { "context": 32768, "output": 4096 } - } - } - }, - "tencent-coding-plan": { - "id": "tencent-coding-plan", - "env": ["TENCENT_CODING_PLAN_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.lkeap.cloud.tencent.com/coding/v3", - "name": "Tencent Coding Plan (China)", - "doc": "https://cloud.tencent.com/document/product/1772/128947", - "models": { - "hunyuan-2.0-instruct": { - "id": "hunyuan-2.0-instruct", - "name": "Tencent HY 2.0 Instruct", - "family": "hunyuan", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2026-03-08", - "last_updated": "2026-03-08", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 131072, "output": 16384 } + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + }, + "cost": { + "input": 0.157, + "output": 0.157 + } }, - "kimi-k2.5": { - "id": "kimi-k2.5", - "name": "Kimi-K2.5", - "family": "kimi", + "kimi-thinking-preview": { + "id": "kimi-thinking-preview", + "name": "Kimi Thinking Preview", "attachment": true, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 262144, "output": 32768 } - }, - "hunyuan-t1": { - "id": "hunyuan-t1", - "name": "Hunyuan-T1", - "family": "hunyuan", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "release_date": "2026-03-08", - "last_updated": "2026-03-08", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-05-07", + "last_updated": "2025-05-07", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 131072, "output": 16384 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 31.46, + "output": 31.46 + } }, - "hunyuan-2.0-thinking": { - "id": "hunyuan-2.0-thinking", - "name": "Tencent HY 2.0 Think", - "family": "hunyuan", + "qwen-turbo": { + "id": "qwen-turbo", + "name": "Qwen Turbo", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "release_date": "2026-03-08", - "last_updated": "2026-03-08", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-11-01", + "last_updated": "2024-11-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 131072, "output": 16384 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 8192 + }, + "cost": { + "input": 0.04998, + "output": 0.2006 + } }, - "tc-code-latest": { - "id": "tc-code-latest", - "name": "Auto", - "family": "auto", + "Llama-3.3-70B-Mhnnn-x1": { + "id": "Llama-3.3-70B-Mhnnn-x1", + "name": "Llama 3.3 70B Mhnnn x1", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2026-03-08", - "last_updated": "2026-03-08", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 131072, "output": 16384 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "glm-5": { - "id": "glm-5", - "name": "GLM-5", - "family": "glm", - "attachment": false, + "claude-opus-4-thinking:32768": { + "id": "claude-opus-4-thinking:32768", + "name": "Claude 4 Opus Thinking (32K)", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 202752, "output": 16384 } + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + }, + "cost": { + "input": 14.994, + "output": 75.004 + } }, - "hunyuan-turbos": { - "id": "hunyuan-turbos", - "name": "Hunyuan-TurboS", - "family": "hunyuan", + "Llama-3.3-70B-Argunaut-1-SFT": { + "id": "Llama-3.3-70B-Argunaut-1-SFT", + "name": "Llama 3.3 70B Argunaut 1 SFT", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2026-03-08", - "last_updated": "2026-03-08", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 131072, "output": 16384 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "minimax-m2.5": { - "id": "minimax-m2.5", - "name": "MiniMax-M2.5", - "family": "minimax", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 204800, "output": 32768 } - } - } - }, - "jiekou": { - "id": "jiekou", - "env": ["JIEKOU_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.jiekou.ai/openai", - "name": "Jiekou.AI", - "doc": "https://docs.jiekou.ai/docs/support/quickstart?utm_source=github_models.dev", - "models": { - "gpt-5.2-codex": { - "id": "gpt-5.2-codex", - "name": "gpt-5.2-codex", - "family": "gpt-codex", + "claude-opus-4-1-thinking:1024": { + "id": "claude-opus-4-1-thinking:1024", + "name": "Claude 4.1 Opus Thinking (1K)", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + }, + "cost": { + "input": 14.994, + "output": 75.004 + } }, "gemini-2.5-flash-lite": { "id": "gemini-2.5-flash-lite", - "name": "gemini-2.5-flash-lite", - "family": "gemini-flash-lite", + "name": "Gemini 2.5 Flash Lite", "attachment": true, + "reasoning": true, + "tool_call": false, + "structured_output": false, + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } + }, + "phi-4-multimodal-instruct": { + "id": "phi-4-multimodal-instruct", + "name": "Phi 4 Multimodal", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image", "video", "audio"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-26", + "last_updated": "2025-07-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.09, "output": 0.36 }, - "limit": { "context": 1048576, "output": 65535 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0.07, + "output": 0.11 + } }, - "gpt-5.1-codex-mini": { - "id": "gpt-5.1-codex-mini", - "name": "gpt-5.1-codex-mini", - "family": "gpt-codex", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "doubao-seed-2-0-code-preview-260215": { + "id": "doubao-seed-2-0-code-preview-260215", + "name": "Doubao Seed 2.0 Code Preview", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2026-02-14", + "last_updated": "2026-02-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.225, "output": 1.8 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 256000, + "input": 256000, + "output": 128000 + }, + "cost": { + "input": 0.782, + "output": 3.893 + } }, - "claude-opus-4-5-20251101": { - "id": "claude-opus-4-5-20251101", - "name": "claude-opus-4-5-20251101", - "family": "claude-opus", - "attachment": true, + "deepseek-reasoner-cheaper": { + "id": "deepseek-reasoner-cheaper", + "name": "Deepseek R1 Cheaper", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 4.5, "output": 22.5 }, - "limit": { "context": 200000, "output": 65536 } + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + }, + "cost": { + "input": 0.4, + "output": 1.7 + } }, - "o3-mini": { - "id": "o3-mini", - "name": "o3-mini", - "family": "o", - "attachment": true, + "exa-answer": { + "id": "exa-answer", + "name": "Exa (Answer)", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-06-04", + "last_updated": "2025-06-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 4096, + "input": 4096, + "output": 4096 + }, + "cost": { + "input": 2.5, + "output": 2.5 + } }, - "gpt-5-pro": { - "id": "gpt-5-pro", - "name": "gpt-5-pro", - "family": "gpt-pro", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "v0-1.0-md": { + "id": "v0-1.0-md", + "name": "v0 1.0 MD", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-04", + "last_updated": "2025-07-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 13.5, "output": 108 }, - "limit": { "context": 400000, "output": 272000 } + "limit": { + "context": 200000, + "input": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "gemini-2.5-flash-lite-preview-09-2025": { - "id": "gemini-2.5-flash-lite-preview-09-2025", - "name": "gemini-2.5-flash-lite-preview-09-2025", - "family": "gemini-flash-lite", + "glm-4.1v-thinking-flash": { + "id": "glm-4.1v-thinking-flash", + "name": "GLM 4.1V Thinking Flash", "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image", "video", "audio"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.09, "output": 0.36 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 64000, + "input": 64000, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 0.3 + } }, - "claude-opus-4-20250514": { - "id": "claude-opus-4-20250514", - "name": "claude-opus-4-20250514", - "family": "claude-opus", - "attachment": true, + "azure-o1": { + "id": "azure-o1", + "name": "Azure o1", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-17", + "last_updated": "2024-12-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 13.5, "output": 67.5 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 200000, + "input": 200000, + "output": 100000 + }, + "cost": { + "input": 14.994, + "output": 59.993 + } }, - "gemini-2.5-flash-preview-05-20": { - "id": "gemini-2.5-flash-preview-05-20", - "name": "gemini-2.5-flash-preview-05-20", - "family": "gemini-flash", - "attachment": true, + "GLM-4.5-Air-Derestricted": { + "id": "GLM-4.5-Air-Derestricted", + "name": "GLM 4.5 Air Derestricted", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image", "video", "audio"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.135, "output": 3.15 }, - "limit": { "context": 1048576, "output": 200000 } + "limit": { + "context": 202600, + "input": 202600, + "output": 98304 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "gemini-3-pro-preview": { - "id": "gemini-3-pro-preview", - "name": "gemini-3-pro-preview", - "family": "gemini-pro", - "attachment": true, + "azure-o3-mini": { + "id": "azure-o3-mini", + "name": "Azure o3-mini", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image", "video", "audio"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-01-31", + "last_updated": "2025-01-31", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.8, "output": 10.8 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 200000, + "input": 200000, + "output": 65536 + }, + "cost": { + "input": 1.088, + "output": 4.3996 + } }, - "grok-4-fast-non-reasoning": { - "id": "grok-4-fast-non-reasoning", - "name": "grok-4-fast-non-reasoning", - "family": "grok", - "attachment": true, + "qwen3.6-max-preview": { + "id": "qwen3.6-max-preview", + "name": "Qwen3.6 Max Preview", + "family": "qwen3.6", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2026-04-20", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.18, "output": 0.45 }, - "limit": { "context": 2000000, "output": 2000000 } + "limit": { + "context": 245800, + "output": 65536 + }, + "cost": { + "input": 1.3, + "output": 7.8 + } }, - "grok-code-fast-1": { - "id": "grok-code-fast-1", - "name": "grok-code-fast-1", - "family": "grok", - "attachment": true, + "Llama-3.3-70B-Sapphira-0.2": { + "id": "Llama-3.3-70B-Sapphira-0.2", + "name": "Llama 3.3 70B Sapphira 0.2", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.18, "output": 1.35 }, - "limit": { "context": 256000, "output": 256000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "gpt-5-mini": { - "id": "gpt-5-mini", - "name": "gpt-5-mini", - "family": "gpt-mini", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "Llama-3.3-70B-Anthrobomination": { + "id": "Llama-3.3-70B-Anthrobomination", + "name": "Llama 3.3 70B Anthrobomination", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.225, "output": 1.8 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "claude-opus-4-6": { - "id": "claude-opus-4-6", - "name": "claude-opus-4-6", - "family": "claude-opus", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-02", - "last_updated": "2026-02", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "QwQ-32B-ArliAI-RpR-v1": { + "id": "QwQ-32B-ArliAI-RpR-v1", + "name": "QwQ 32b Arli V1", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25 }, - "limit": { "context": 1000000, "output": 128000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + }, + "cost": { + "input": 0.2, + "output": 0.2 + } }, - "claude-sonnet-4-5-20250929": { - "id": "claude-sonnet-4-5-20250929", - "name": "claude-sonnet-4-5-20250929", - "family": "claude-sonnet", + "claude-opus-4-20250514": { + "id": "claude-opus-4-20250514", + "name": "Claude 4 Opus", "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-05-14", + "last_updated": "2025-05-14", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.7, "output": 13.5 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + }, + "cost": { + "input": 14.994, + "output": 75.004 + } }, - "gpt-5.1-codex-max": { - "id": "gpt-5.1-codex-max", - "name": "gpt-5.1-codex-max", - "family": "gpt-codex", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "yi-lightning": { + "id": "yi-lightning", + "name": "Yi Lightning", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-10-16", + "last_updated": "2024-10-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.125, "output": 9 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 12000, + "input": 12000, + "output": 4096 + }, + "cost": { + "input": 0.2006, + "output": 0.2006 + } }, - "gemini-2.5-pro-preview-06-05": { - "id": "gemini-2.5-pro-preview-06-05", - "name": "gemini-2.5-pro-preview-06-05", - "family": "gemini-pro", - "attachment": true, + "Llama-3.3-70B-Electra-R1": { + "id": "Llama-3.3-70B-Electra-R1", + "name": "Llama 3.3 70B Electra R1", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image", "video", "audio"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.125, "output": 9 }, - "limit": { "context": 1048576, "output": 200000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "o3": { - "id": "o3", - "name": "o3", - "attachment": true, + "Llama-3.3-70B-Forgotten-Abomination-v5.0": { + "id": "Llama-3.3-70B-Forgotten-Abomination-v5.0", + "name": "Llama 3.3 70B Forgotten Abomination v5.0", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 10, "output": 40 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "grok-4-1-fast-non-reasoning": { - "id": "grok-4-1-fast-non-reasoning", - "name": "grok-4-1-fast-non-reasoning", - "family": "grok", - "attachment": true, + "Llama-3.3-70B-Cirrus-x1": { + "id": "Llama-3.3-70B-Cirrus-x1", + "name": "Llama 3.3 70B Cirrus x1", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.18, "output": 0.45 }, - "limit": { "context": 2000000, "output": 2000000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "claude-sonnet-4-20250514": { - "id": "claude-sonnet-4-20250514", - "name": "claude-sonnet-4-20250514", - "family": "claude-sonnet", - "attachment": true, + "grok-3-mini-beta": { + "id": "grok-3-mini-beta", + "name": "Grok 3 Mini Beta", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.7, "output": 13.5 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 131072, + "input": 131072, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 0.5 + } }, - "grok-4-0709": { - "id": "grok-4-0709", - "name": "grok-4-0709", - "family": "grok", - "attachment": true, + "auto-model-standard": { + "id": "auto-model-standard", + "name": "Auto model (Standard)", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-06-01", + "last_updated": "2024-06-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.7, "output": 13.5 }, - "limit": { "context": 256000, "output": 8192 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 1000000 + }, + "cost": { + "input": 9.996, + "output": 19.992 + } }, - "gemini-3-flash-preview": { - "id": "gemini-3-flash-preview", - "name": "gemini-3-flash-preview", - "family": "gemini-flash", + "claude-sonnet-4-5-20250929-thinking": { + "id": "claude-sonnet-4-5-20250929-thinking", + "name": "Claude Sonnet 4.5 Thinking", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image", "video", "audio"], "output": ["text"] }, + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 3 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 64000 + }, + "cost": { + "input": 2.992, + "output": 14.994 + } }, - "gemini-2.5-pro": { - "id": "gemini-2.5-pro", - "name": "gemini-2.5-pro", - "family": "gemini-pro", - "attachment": true, + "v0-1.5-md": { + "id": "v0-1.5-md", + "name": "v0 1.5 MD", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image", "video", "audio"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-04", + "last_updated": "2025-07-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.125, "output": 9 }, - "limit": { "context": 1048576, "output": 65535 } + "limit": { + "context": 200000, + "input": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "claude-opus-4-1-20250805": { - "id": "claude-opus-4-1-20250805", - "name": "claude-opus-4-1-20250805", - "family": "claude-opus", + "kimi-k2-instruct-fast": { + "id": "kimi-k2-instruct-fast", + "name": "Kimi K2 0711 Fast", "attachment": true, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-15", + "last_updated": "2025-07-15", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 13.5, "output": 67.5 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 131072, + "input": 131072, + "output": 16384 + }, + "cost": { + "input": 0.1, + "output": 2 + } }, - "gemini-2.5-flash": { - "id": "gemini-2.5-flash", - "name": "gemini-2.5-flash", - "family": "gemini-flash", - "attachment": true, + "glm-4-long": { + "id": "glm-4-long", + "name": "GLM-4 Long", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image", "video", "audio"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-08-01", + "last_updated": "2024-08-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.27, "output": 2.25 }, - "limit": { "context": 1048576, "output": 65535 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 4096 + }, + "cost": { + "input": 0.2006, + "output": 0.2006 + } }, - "gpt-5.2": { - "id": "gpt-5.2", - "name": "gpt-5.2", - "family": "gpt", - "attachment": true, + "jamba-large-1.7": { + "id": "jamba-large-1.7", + "name": "Jamba Large 1.7", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.575, "output": 12.6 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 256000, + "input": 256000, + "output": 4096 + }, + "cost": { + "input": 1.989, + "output": 7.99 + } }, - "grok-4-1-fast-reasoning": { - "id": "grok-4-1-fast-reasoning", - "name": "grok-4-1-fast-reasoning", - "family": "grok", + "qvq-max": { + "id": "qvq-max", + "name": "Qwen: QvQ Max", "attachment": true, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-03-28", + "last_updated": "2025-03-28", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.18, "output": 0.45 }, - "limit": { "context": 2000000, "output": 2000000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "cost": { + "input": 1.4, + "output": 5.3 + } }, - "gpt-5.1": { - "id": "gpt-5.1", - "name": "gpt-5.1", - "family": "gpt", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-02", - "last_updated": "2026-02", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "gemini-2.0-flash-thinking-exp-1219": { + "id": "gemini-2.0-flash-thinking-exp-1219", + "name": "Gemini 2.0 Flash Thinking 1219", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-19", + "last_updated": "2024-12-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.125, "output": 9 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 32767, + "input": 32767, + "output": 8192 + }, + "cost": { + "input": 0.1003, + "output": 0.408 + } }, - "gpt-5-chat-latest": { - "id": "gpt-5-chat-latest", - "name": "gpt-5-chat-latest", - "family": "gpt", + "gemini-2.0-flash-lite": { + "id": "gemini-2.0-flash-lite", + "name": "Gemini 2.0 Flash Lite", "attachment": true, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.125, "output": 9 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 8192 + }, + "cost": { + "input": 0.0748, + "output": 0.306 + } }, - "grok-4-fast-reasoning": { - "id": "grok-4-fast-reasoning", - "name": "grok-4-fast-reasoning", - "family": "grok", - "attachment": true, + "azure-gpt-4-turbo": { + "id": "azure-gpt-4-turbo", + "name": "Azure gpt-4-turbo", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2023-11-06", + "last_updated": "2024-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.18, "output": 0.45 }, - "limit": { "context": 2000000, "output": 2000000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + }, + "cost": { + "input": 9.996, + "output": 30.005 + } }, - "gpt-5-nano": { - "id": "gpt-5-nano", - "name": "gpt-5-nano", - "family": "gpt-nano", - "attachment": true, + "Baichuan-M2": { + "id": "Baichuan-M2", + "name": "Baichuan M2 32B Medical", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-19", + "last_updated": "2025-08-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.045, "output": 0.36 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + }, + "cost": { + "input": 15.73, + "output": 15.73 + } }, - "gemini-2.5-flash-lite-preview-06-17": { - "id": "gemini-2.5-flash-lite-preview-06-17", - "name": "gemini-2.5-flash-lite-preview-06-17", - "family": "gemini-flash-lite", + "qwen-long": { + "id": "qwen-long", + "name": "Qwen Long 10M", "attachment": true, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "video", "image", "audio"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-01-25", + "last_updated": "2025-01-25", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.09, "output": 0.36 }, - "limit": { "context": 1048576, "output": 65535 } + "limit": { + "context": 10000000, + "input": 10000000, + "output": 8192 + }, + "cost": { + "input": 0.1003, + "output": 0.408 + } }, - "claude-haiku-4-5-20251001": { - "id": "claude-haiku-4-5-20251001", - "name": "claude-haiku-4-5-20251001", - "family": "claude-haiku", - "attachment": true, - "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "sonar-reasoning-pro": { + "id": "sonar-reasoning-pro", + "name": "Perplexity Reasoning Pro", + "attachment": false, + "reasoning": true, + "tool_call": false, + "structured_output": false, + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.9, "output": 4.5 }, - "limit": { "context": 20000, "output": 64000 } + "limit": { + "context": 127000, + "input": 127000, + "output": 128000 + }, + "cost": { + "input": 2.006, + "output": 7.9985 + } }, - "gpt-5-codex": { - "id": "gpt-5-codex", - "name": "gpt-5-codex", - "family": "gpt-codex", + "gemini-2.5-flash-preview-05-20:thinking": { + "id": "gemini-2.5-flash-preview-05-20:thinking", + "name": "Gemini 2.5 Flash 0520 Thinking", "attachment": true, + "reasoning": true, + "tool_call": false, + "structured_output": false, + "release_date": "2025-05-20", + "last_updated": "2025-05-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048000, + "input": 1048000, + "output": 65536 + }, + "cost": { + "input": 0.15, + "output": 3.5 + } + }, + "GLM-4.5-Air-Derestricted-Steam-ReExtract": { + "id": "GLM-4.5-Air-Derestricted-Steam-ReExtract", + "name": "GLM 4.5 Air Derestricted Steam ReExtract", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-12", + "last_updated": "2025-12-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.125, "output": 9 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 131072, + "input": 131072, + "output": 65536 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "o4-mini": { - "id": "o4-mini", - "name": "o4-mini", - "family": "o", - "attachment": true, + "Llama-3.3-70B-Dark-Ages-v0.1": { + "id": "Llama-3.3-70B-Dark-Ages-v0.1", + "name": "Llama 3.3 70B Dark Ages v0.1", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "gpt-5.1-codex": { - "id": "gpt-5.1-codex", - "name": "gpt-5.1-codex", - "family": "gpt-codex", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "Baichuan4-Turbo": { + "id": "Baichuan4-Turbo", + "name": "Baichuan 4 Turbo", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-19", + "last_updated": "2025-08-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.125, "output": 9 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 32768 + }, + "cost": { + "input": 2.42, + "output": 2.42 + } }, - "gpt-5.2-pro": { - "id": "gpt-5.2-pro", - "name": "gpt-5.2-pro", - "family": "gpt-pro", + "doubao-1.5-vision-pro-32k": { + "id": "doubao-1.5-vision-pro-32k", + "name": "Doubao 1.5 Vision Pro 32k", "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-01-22", + "last_updated": "2025-01-22", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 18.9, "output": 151.2 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 32000, + "input": 32000, + "output": 8192 + }, + "cost": { + "input": 0.459, + "output": 1.377 + } }, - "deepseek/deepseek-v3-0324": { - "id": "deepseek/deepseek-v3-0324", - "name": "DeepSeek V3 0324", - "family": "deepseek", + "alibaba/qwen3.6-flash": { + "id": "alibaba/qwen3.6-flash", + "name": "Qwen3.6 Flash", + "family": "qwen3.6", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.28, "output": 1.14 }, - "limit": { "context": 163840, "output": 163840 } + "tool_call": false, + "structured_output": false, + "release_date": "2026-04-17", + "last_updated": "2026-04-17", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 991800, + "output": 65536 + }, + "cost": { + "input": 0.19, + "output": 1.16 + } }, - "deepseek/deepseek-v3.1": { - "id": "deepseek/deepseek-v3.1", - "name": "DeepSeek V3.1", - "family": "deepseek", + "inflection/inflection-3-pi": { + "id": "inflection/inflection-3-pi", + "name": "Inflection 3 Pi", + "family": "gpt", "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.27, "output": 1 }, - "limit": { "context": 163840, "output": 32768 } + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-10-11", + "last_updated": "2024-10-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8000, + "input": 8000, + "output": 4096 + }, + "cost": { + "input": 2.499, + "output": 9.996 + } }, - "deepseek/deepseek-r1-0528": { - "id": "deepseek/deepseek-r1-0528", - "name": "DeepSeek R1 0528", - "family": "deepseek-thinking", + "inflection/inflection-3-productivity": { + "id": "inflection/inflection-3-productivity", + "name": "Inflection 3 Productivity", + "family": "gpt", "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.7, "output": 2.5 }, - "limit": { "context": 163840, "output": 32768 } + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-10-11", + "last_updated": "2024-10-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8000, + "input": 8000, + "output": 4096 + }, + "cost": { + "input": 2.499, + "output": 9.996 + } }, - "zai-org/glm-4.7-flash": { - "id": "zai-org/glm-4.7-flash", - "name": "GLM-4.7-Flash", - "family": "glm", + "essentialai/rnj-1-instruct": { + "id": "essentialai/rnj-1-instruct", + "name": "RNJ-1 Instruct 8B", + "family": "rnj", "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.07, "output": 0.4 }, - "limit": { "context": 200000, "output": 128000 } + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-13", + "last_updated": "2025-12-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "cost": { + "input": 0.15, + "output": 0.15 + } }, - "zai-org/glm-4.5": { - "id": "zai-org/glm-4.5", - "name": "GLM-4.5", - "family": "glm", + "LLM360/K2-Think": { + "id": "LLM360/K2-Think", + "name": "K2-Think", + "family": "kimi-thinking", "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.2 }, - "limit": { "context": 131072, "output": 98304 } + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-26", + "last_updated": "2025-07-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 32768 + }, + "cost": { + "input": 0.17, + "output": 0.68 + } }, - "zai-org/glm-4.5v": { - "id": "zai-org/glm-4.5v", - "name": "GLM 4.5V", - "family": "glmv", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 1.8 }, - "limit": { "context": 65536, "output": 16384 } + "TEE/kimi-k2.5": { + "id": "TEE/kimi-k2.5", + "name": "Kimi K2.5 TEE", + "family": "kimi", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2026-01-29", + "last_updated": "2026-01-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 65535 + }, + "cost": { + "input": 0.3, + "output": 1.9 + } }, - "zai-org/glm-4.7": { - "id": "zai-org/glm-4.7", - "name": "GLM-4.7", + "TEE/glm-4.7": { + "id": "TEE/glm-4.7", + "name": "GLM 4.7 TEE", "family": "glm", "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.2 }, - "limit": { "context": 204800, "output": 131072 } + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2026-01-29", + "last_updated": "2026-01-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "input": 131000, + "output": 65535 + }, + "cost": { + "input": 0.85, + "output": 3.3 + } }, - "baidu/ernie-4.5-300b-a47b-paddle": { - "id": "baidu/ernie-4.5-300b-a47b-paddle", - "name": "ERNIE 4.5 300B A47B", - "family": "ernie", + "TEE/qwen3.5-397b-a17b": { + "id": "TEE/qwen3.5-397b-a17b", + "name": "Qwen3.5 397B A17B TEE", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.28, "output": 1.1 }, - "limit": { "context": 123000, "output": 12000 } - }, - "baidu/ernie-4.5-vl-424b-a47b": { - "id": "baidu/ernie-4.5-vl-424b-a47b", - "name": "ERNIE 4.5 VL 424B A47B", - "family": "ernie", - "attachment": true, - "reasoning": true, - "tool_call": true, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.42, "output": 1.25 }, - "limit": { "context": 123000, "output": 16000 } + "release_date": "2026-02-28", + "last_updated": "2026-02-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 258048, + "input": 258048, + "output": 65536 + }, + "cost": { + "input": 0.6, + "output": 3.6 + } }, - "minimaxai/minimax-m1-80k": { - "id": "minimaxai/minimax-m1-80k", - "name": "MiniMax M1", - "family": "minimax", + "TEE/glm-5": { + "id": "TEE/glm-5", + "name": "GLM 5 TEE", + "family": "glm", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.55, "output": 2.2 }, - "limit": { "context": 1000000, "output": 40000 } + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 203000, + "input": 203000, + "output": 65535 + }, + "cost": { + "input": 1.2, + "output": 3.5 + } }, - "minimax/minimax-m2.1": { - "id": "minimax/minimax-m2.1", - "name": "Minimax M2.1", + "TEE/qwen2.5-vl-72b-instruct": { + "id": "TEE/qwen2.5-vl-72b-instruct", + "name": "Qwen2.5 VL 72B TEE", + "family": "qwen", + "attachment": true, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-02-01", + "last_updated": "2025-02-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 65536, + "input": 65536, + "output": 8192 + }, + "cost": { + "input": 0.7, + "output": 0.7 + } + }, + "TEE/minimax-m2.1": { + "id": "TEE/minimax-m2.1", + "name": "MiniMax M2.1 TEE", "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 204800, "output": 131072 } + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "input": 200000, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "qwen/qwen3-235b-a22b-instruct-2507": { - "id": "qwen/qwen3-235b-a22b-instruct-2507", - "name": "Qwen3 235B A22B Instruct 2507", + "TEE/qwen3-30b-a3b-instruct-2507": { + "id": "TEE/qwen3-30b-a3b-instruct-2507", + "name": "Qwen3 30B A3B Instruct 2507 TEE", "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.8 }, - "limit": { "context": 131072, "output": 16384 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-29", + "last_updated": "2025-07-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "input": 262000, + "output": 32768 + }, + "cost": { + "input": 0.15, + "output": 0.44999999999999996 + } }, - "qwen/qwen3-coder-next": { - "id": "qwen/qwen3-coder-next", - "name": "qwen/qwen3-coder-next", - "family": "qwen", + "TEE/deepseek-v3.1": { + "id": "TEE/deepseek-v3.1", + "name": "DeepSeek V3.1 TEE", + "family": "deepseek", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-02", - "last_updated": "2026-02", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 1.5 }, - "limit": { "context": 262144, "output": 65536 } - }, - "qwen/qwen3-30b-a3b-fp8": { - "id": "qwen/qwen3-30b-a3b-fp8", - "name": "Qwen3 30B A3B", - "family": "qwen", - "attachment": false, - "reasoning": true, "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.09, "output": 0.45 }, - "limit": { "context": 40960, "output": 20000 } + "release_date": "2025-08-21", + "last_updated": "2025-08-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 164000, + "input": 164000, + "output": 8192 + }, + "cost": { + "input": 1, + "output": 2.5 + } }, - "qwen/qwen3-235b-a22b-fp8": { - "id": "qwen/qwen3-235b-a22b-fp8", - "name": "Qwen3 235B A22B", - "family": "qwen", + "TEE/llama3-3-70b": { + "id": "TEE/llama3-3-70b", + "name": "Llama 3.3 70B", + "family": "llama", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 40960, "output": 20000 } + "release_date": "2025-07-03", + "last_updated": "2025-07-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 2, + "output": 2 + } }, - "qwen/qwen3-coder-480b-a35b-instruct": { - "id": "qwen/qwen3-coder-480b-a35b-instruct", - "name": "Qwen3 Coder 480B A35B Instruct", - "family": "qwen", + "TEE/glm-4.6": { + "id": "TEE/glm-4.6", + "name": "GLM 4.6 TEE", + "family": "glm", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.29, "output": 1.2 }, - "limit": { "context": 262144, "output": 65536 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 203000, + "input": 203000, + "output": 65535 + }, + "cost": { + "input": 0.75, + "output": 2 + } }, - "qwen/qwen3-next-80b-a3b-thinking": { - "id": "qwen/qwen3-next-80b-a3b-thinking", - "name": "Qwen3 Next 80B A3B Thinking", - "family": "qwen", + "TEE/kimi-k2.5-thinking": { + "id": "TEE/kimi-k2.5-thinking", + "name": "Kimi K2.5 Thinking TEE", + "family": "kimi-thinking", "attachment": false, "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 1.5 }, - "limit": { "context": 65536, "output": 65536 } + "tool_call": false, + "structured_output": false, + "release_date": "2026-01-29", + "last_updated": "2026-01-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 65535 + }, + "cost": { + "input": 0.3, + "output": 1.9 + } }, - "qwen/qwen3-235b-a22b-thinking-2507": { - "id": "qwen/qwen3-235b-a22b-thinking-2507", - "name": "Qwen3 235B A22b Thinking 2507", - "family": "qwen", + "TEE/gemma-3-27b-it": { + "id": "TEE/gemma-3-27b-it", + "name": "Gemma 3 27B TEE", + "family": "gemma", "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 3 }, - "limit": { "context": 131072, "output": 131072 } + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-03-10", + "last_updated": "2025-03-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "input": 131072, + "output": 8192 + }, + "cost": { + "input": 0.2, + "output": 0.8 + } }, - "qwen/qwen3-next-80b-a3b-instruct": { - "id": "qwen/qwen3-next-80b-a3b-instruct", - "name": "Qwen3 Next 80B A3B Instruct", - "family": "qwen", + "TEE/deepseek-v3.2": { + "id": "TEE/deepseek-v3.2", + "name": "DeepSeek V3.2 TEE", + "family": "deepseek", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 1.5 }, - "limit": { "context": 65536, "output": 65536 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 164000, + "input": 164000, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 1 + } }, - "qwen/qwen3-32b-fp8": { - "id": "qwen/qwen3-32b-fp8", - "name": "Qwen3 32B", - "family": "qwen", + "TEE/gpt-oss-20b": { + "id": "TEE/gpt-oss-20b", + "name": "GPT-OSS 20B TEE", + "family": "gpt-oss", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.45 }, - "limit": { "context": 40960, "output": 20000 } - }, - "moonshotai/kimi-k2.5": { - "id": "moonshotai/kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 3 }, - "limit": { "context": 262144, "output": 262144 } + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "input": 131072, + "output": 8192 + }, + "cost": { + "input": 0.2, + "output": 0.8 + } }, - "moonshotai/kimi-k2-0905": { - "id": "moonshotai/kimi-k2-0905", - "name": "Kimi K2 0905", - "family": "kimi", + "TEE/qwen3-coder": { + "id": "TEE/qwen3-coder", + "name": "Qwen3 Coder 480B TEE", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.5 }, - "limit": { "context": 262144, "output": 262144 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 32768 + }, + "cost": { + "input": 1.5, + "output": 2 + } }, - "moonshotai/kimi-k2-instruct": { - "id": "moonshotai/kimi-k2-instruct", - "name": "Kimi K2 Instruct", - "family": "kimi", + "TEE/glm-4.7-flash": { + "id": "TEE/glm-4.7-flash", + "name": "GLM 4.7 Flash TEE", + "family": "glm-flash", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.57, "output": 2.3 }, - "limit": { "context": 131072, "output": 131072 } + "tool_call": false, + "structured_output": false, + "release_date": "2026-01-19", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 203000, + "input": 203000, + "output": 65535 + }, + "cost": { + "input": 0.15, + "output": 0.5 + } }, - "xiaomimimo/mimo-v2-flash": { - "id": "xiaomimimo/mimo-v2-flash", - "name": "XiaomiMiMo/MiMo-V2-Flash", - "family": "mimo", + "TEE/gpt-oss-120b": { + "id": "TEE/gpt-oss-120b", + "name": "GPT-OSS 120B TEE", + "family": "gpt-oss", "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 131072 } - } - } - }, - "deepseek": { - "id": "deepseek", - "env": ["DEEPSEEK_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.deepseek.com", - "name": "DeepSeek", - "doc": "https://api-docs.deepseek.com/quick_start/pricing", - "models": { - "deepseek-chat": { - "id": "deepseek-chat", - "name": "DeepSeek Chat", - "family": "deepseek", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-12-01", - "last_updated": "2026-02-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.28, "output": 0.42, "cache_read": 0.028 }, - "limit": { "context": 128000, "output": 8192 } - }, - "deepseek-reasoner": { - "id": "deepseek-reasoner", - "name": "DeepSeek Reasoner", - "family": "deepseek-thinking", - "attachment": true, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-12-01", - "last_updated": "2026-02-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.28, "output": 0.42, "cache_read": 0.028 }, - "limit": { "context": 128000, "output": 64000 } - } - } - }, - "llama": { - "id": "llama", - "env": ["LLAMA_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.llama.com/compat/v1/", - "name": "Llama", - "doc": "https://llama.developer.meta.com/docs/models", - "models": { - "cerebras-llama-4-scout-17b-16e-instruct": { - "id": "cerebras-llama-4-scout-17b-16e-instruct", - "name": "Cerebras-Llama-4-Scout-17B-16E-Instruct", - "family": "llama", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } - }, - "llama-3.3-70b-instruct": { - "id": "llama-3.3-70b-instruct", - "name": "Llama-3.3-70B-Instruct", - "family": "llama", - "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } - }, - "cerebras-llama-4-maverick-17b-128e-instruct": { - "id": "cerebras-llama-4-maverick-17b-128e-instruct", - "name": "Cerebras-Llama-4-Maverick-17B-128E-Instruct", - "family": "llama", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } - }, - "groq-llama-4-maverick-17b-128e-instruct": { - "id": "groq-llama-4-maverick-17b-128e-instruct", - "name": "Groq-Llama-4-Maverick-17B-128E-Instruct", - "family": "llama", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "input": 131072, + "output": 16384 + }, + "cost": { + "input": 2, + "output": 2 + } }, - "llama-4-scout-17b-16e-instruct-fp8": { - "id": "llama-4-scout-17b-16e-instruct-fp8", - "name": "Llama-4-Scout-17B-16E-Instruct-FP8", - "family": "llama", - "attachment": true, + "TEE/deepseek-r1-0528": { + "id": "TEE/deepseek-r1-0528", + "name": "DeepSeek R1 0528 TEE", + "family": "deepseek", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-05-28", + "last_updated": "2025-05-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 2 + } }, - "llama-3.3-8b-instruct": { - "id": "llama-3.3-8b-instruct", - "name": "Llama-3.3-8B-Instruct", - "family": "llama", - "attachment": true, + "TEE/kimi-k2-thinking": { + "id": "TEE/kimi-k2-thinking", + "name": "Kimi K2 Thinking TEE", + "family": "kimi-thinking", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-11-06", + "last_updated": "2025-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 65535 + }, + "cost": { + "input": 2, + "output": 2 + } }, - "llama-4-maverick-17b-128e-instruct-fp8": { - "id": "llama-4-maverick-17b-128e-instruct-fp8", - "name": "Llama-4-Maverick-17B-128E-Instruct-FP8", + "CrucibleLab/L3.3-70B-Loki-V2.0": { + "id": "CrucibleLab/L3.3-70B-Loki-V2.0", + "name": "L3.3 70B Loki v2.0", "family": "llama", - "attachment": true, + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } - } - } - }, - "azure-cognitive-services": { - "id": "azure-cognitive-services", - "env": ["AZURE_COGNITIVE_SERVICES_RESOURCE_NAME", "AZURE_COGNITIVE_SERVICES_API_KEY"], - "npm": "@ai-sdk/azure", - "name": "Azure Cognitive Services", - "doc": "https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models", - "models": { - "gpt-5.4-mini": { - "id": "gpt-5.4-mini", - "name": "GPT-5.4 Mini", - "family": "gpt-mini", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-17", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2026-01-22", + "last_updated": "2026-01-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.75, "output": 4.5, "cache_read": 0.075 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + }, + "cost": { + "input": 0.49299999999999994, + "output": 0.49299999999999994 + } }, - "claude-opus-4-5": { - "id": "claude-opus-4-5", - "name": "Claude Opus 4.5", - "family": "claude-opus", + "deepseek/deepseek-v3.2:thinking": { + "id": "deepseek/deepseek-v3.2:thinking", + "name": "DeepSeek V3.2 Thinking", + "family": "deepseek", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-11-24", - "last_updated": "2025-08-01", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 200000, "output": 64000 }, - "provider": { - "npm": "@ai-sdk/anthropic", - "api": "https://${AZURE_COGNITIVE_SERVICES_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + "limit": { + "context": 163000, + "input": 163000, + "output": 65536 + }, + "cost": { + "input": 0.27999999999999997, + "output": 0.42000000000000004 } }, - "claude-opus-4-6": { - "id": "claude-opus-4-6", - "name": "Claude Opus 4.6", - "family": "claude-opus", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-05", - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "deepseek/deepseek-prover-v2-671b": { + "id": "deepseek/deepseek-prover-v2-671b", + "name": "DeepSeek Prover v2 671B", + "family": "deepseek", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-04-30", + "last_updated": "2025-04-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { - "input": 5, - "output": 25, - "cache_read": 0.5, - "cache_write": 6.25, - "context_over_200k": { "input": 10, "output": 37.5, "cache_read": 1, "cache_write": 12.5 } + "limit": { + "context": 160000, + "input": 160000, + "output": 16384 }, - "limit": { "context": 200000, "output": 128000 }, - "provider": { - "npm": "@ai-sdk/anthropic", - "api": "https://${AZURE_COGNITIVE_SERVICES_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + "cost": { + "input": 1, + "output": 2.5 } }, - "claude-opus-4-1": { - "id": "claude-opus-4-1", - "name": "Claude Opus 4.1", - "family": "claude-opus", + "deepseek/deepseek-v3.2-speciale": { + "id": "deepseek/deepseek-v3.2-speciale", + "name": "DeepSeek V3.2 Speciale", + "family": "deepseek", "attachment": true, "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-02", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 }, - "provider": { - "npm": "@ai-sdk/anthropic", - "api": "https://${AZURE_COGNITIVE_SERVICES_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + "limit": { + "context": 163000, + "input": 163000, + "output": 65536 + }, + "cost": { + "input": 0.27999999999999997, + "output": 0.42000000000000004 } }, - "gpt-5.4-nano": { - "id": "gpt-5.4-nano", - "name": "GPT-5.4 Nano", - "family": "gpt-nano", + "deepseek/deepseek-v3.2": { + "id": "deepseek/deepseek-v3.2", + "name": "DeepSeek V3.2", + "family": "deepseek", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-17", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 1.25, "cache_read": 0.02 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 163000, + "input": 163000, + "output": 65536 + }, + "cost": { + "input": 0.27999999999999997, + "output": 0.42000000000000004 + } }, - "claude-sonnet-4-5": { - "id": "claude-sonnet-4-5", - "name": "Claude Sonnet 4.5", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "Doctor-Shotgun/MS3.2-24B-Magnum-Diamond": { + "id": "Doctor-Shotgun/MS3.2-24B-Magnum-Diamond", + "name": "MS3.2 24B Magnum Diamond", + "family": "mistral", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-11-24", + "last_updated": "2025-11-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 }, - "provider": { - "npm": "@ai-sdk/anthropic", - "api": "https://${AZURE_COGNITIVE_SERVICES_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + "limit": { + "context": 16384, + "input": 16384, + "output": 32768 + }, + "cost": { + "input": 0.49299999999999994, + "output": 0.49299999999999994 } }, - "claude-haiku-4-5": { - "id": "claude-haiku-4-5", - "name": "Claude Haiku 4.5", - "family": "claude-haiku", + "NeverSleep/Llama-3-Lumimaid-70B-v0.1": { + "id": "NeverSleep/Llama-3-Lumimaid-70B-v0.1", + "name": "Lumimaid 70b", + "family": "llama", "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-02-31", - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-01", + "last_updated": "2024-07-01", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 64000 }, - "provider": { - "npm": "@ai-sdk/anthropic", - "api": "https://${AZURE_COGNITIVE_SERVICES_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + }, + "cost": { + "input": 2.006, + "output": 2.006 } }, - "o1-mini": { - "id": "o1-mini", - "name": "o1-mini", - "family": "o-mini", + "NeverSleep/Lumimaid-v0.2-70B": { + "id": "NeverSleep/Lumimaid-v0.2-70B", + "name": "Lumimaid v0.2", + "family": "llama", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2023-09", - "release_date": "2024-09-12", - "last_updated": "2024-09-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-01", + "last_updated": "2024-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.55 }, - "limit": { "context": 128000, "output": 65536 } + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + }, + "cost": { + "input": 1, + "output": 1.5 + } }, - "gpt-5.1-codex": { - "id": "gpt-5.1-codex", - "name": "GPT-5.1 Codex", - "family": "gpt-codex", + "Steelskull/L3.3-Cu-Mai-R1-70b": { + "id": "Steelskull/L3.3-Cu-Mai-R1-70b", + "name": "Llama 3.3 70B Cu Mai", + "family": "llama", "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text", "image", "audio"], "output": ["text", "image", "audio"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } - }, - "o4-mini": { - "id": "o4-mini", - "name": "o4-mini", - "family": "o-mini", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.28 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + }, + "cost": { + "input": 0.49299999999999994, + "output": 0.49299999999999994 + } }, - "gpt-5-codex": { - "id": "gpt-5-codex", - "name": "GPT-5-Codex", - "family": "gpt-codex", + "Steelskull/L3.3-Nevoria-R1-70b": { + "id": "Steelskull/L3.3-Nevoria-R1-70b", + "name": "Steelskull Nevoria R1 70b", + "family": "llama", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.13 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + }, + "cost": { + "input": 0.49299999999999994, + "output": 0.49299999999999994 + } }, - "text-embedding-3-small": { - "id": "text-embedding-3-small", - "name": "text-embedding-3-small", - "family": "text-embedding", + "Steelskull/L3.3-MS-Evayale-70B": { + "id": "Steelskull/L3.3-MS-Evayale-70B", + "name": "Evayale 70b ", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": false, - "release_date": "2024-01-25", - "last_updated": "2024-01-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.02, "output": 0 }, - "limit": { "context": 8191, "output": 1536 } + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + }, + "cost": { + "input": 0.49299999999999994, + "output": 0.49299999999999994 + } }, - "gpt-3.5-turbo-0125": { - "id": "gpt-3.5-turbo-0125", - "name": "GPT-3.5 Turbo 0125", - "family": "gpt", + "Steelskull/L3.3-Electra-R1-70b": { + "id": "Steelskull/L3.3-Electra-R1-70b", + "name": "Steelskull Electra R1 70b", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2021-08", - "release_date": "2024-01-25", - "last_updated": "2024-01-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 1.5 }, - "limit": { "context": 16384, "output": 16384 } - }, - "deepseek-v3.2": { - "id": "deepseek-v3.2", - "name": "DeepSeek-V3.2", - "family": "deepseek", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.58, "output": 1.68 }, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + }, + "cost": { + "input": 0.69989, + "output": 0.69989 + } }, - "o1-preview": { - "id": "o1-preview", - "name": "o1-preview", - "family": "o", + "Steelskull/L3.3-MS-Nevoria-70b": { + "id": "Steelskull/L3.3-MS-Nevoria-70b", + "name": "Steelskull Nevoria 70b", + "family": "llama", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2023-09", - "release_date": "2024-09-12", - "last_updated": "2024-09-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 16.5, "output": 66, "cache_read": 8.25 }, - "limit": { "context": 128000, "output": 32768 } - }, - "gpt-4.1-nano": { - "id": "gpt-4.1-nano", - "name": "GPT-4.1 nano", - "family": "gpt-nano", - "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-05", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.03 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + }, + "cost": { + "input": 0.49299999999999994, + "output": 0.49299999999999994 + } }, - "meta-llama-3.1-70b-instruct": { - "id": "meta-llama-3.1-70b-instruct", - "name": "Meta-Llama-3.1-70B-Instruct", + "Steelskull/L3.3-MS-Evalebis-70b": { + "id": "Steelskull/L3.3-MS-Evalebis-70b", + "name": "MS Evalebis 70b", "family": "llama", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2.68, "output": 3.54 }, - "limit": { "context": 128000, "output": 32768 } + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + }, + "cost": { + "input": 0.49299999999999994, + "output": 0.49299999999999994 + } }, - "phi-3-small-128k-instruct": { - "id": "phi-3-small-128k-instruct", - "name": "Phi-3-small-instruct (128k)", - "family": "phi", + "miromind-ai/mirothinker-v1.5-235b": { + "id": "miromind-ai/mirothinker-v1.5-235b", + "name": "MiroThinker v1.5 235B", + "family": "gpt", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-04-23", - "last_updated": "2024-04-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 128000, "output": 4096 } + "structured_output": false, + "release_date": "2026-01-07", + "last_updated": "2026-01-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "input": 32768, + "output": 4000 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "deepseek-r1-0528": { - "id": "deepseek-r1-0528", - "name": "DeepSeek-R1-0528", - "family": "deepseek-thinking", + "pamanseau/OpenReasoning-Nemotron-32B": { + "id": "pamanseau/OpenReasoning-Nemotron-32B", + "name": "OpenReasoning Nemotron 32B", + "family": "nemotron", "attachment": false, "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-05-28", - "last_updated": "2025-05-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1.35, "output": 5.4 }, - "limit": { "context": 163840, "output": 163840 } - }, - "llama-4-maverick-17b-128e-instruct-fp8": { - "id": "llama-4-maverick-17b-128e-instruct-fp8", - "name": "Llama 4 Maverick 17B 128E Instruct FP8", - "family": "llama", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.25, "output": 1 }, - "limit": { "context": 128000, "output": 8192 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-21", + "last_updated": "2025-08-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "input": 32768, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } }, - "cohere-embed-v-4-0": { - "id": "cohere-embed-v-4-0", - "name": "Embed v4", - "family": "cohere-embed", - "attachment": true, + "arcee-ai/trinity-mini": { + "id": "arcee-ai/trinity-mini", + "name": "Trinity Mini", + "family": "trinity-mini", + "attachment": false, "reasoning": false, "tool_call": false, - "temperature": false, - "release_date": "2025-04-15", - "last_updated": "2025-04-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.12, "output": 0 }, - "limit": { "context": 128000, "output": 1536 } + "structured_output": false, + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "input": 131072, + "output": 8192 + }, + "cost": { + "input": 0.045000000000000005, + "output": 0.15 + } }, - "phi-4-reasoning-plus": { - "id": "phi-4-reasoning-plus", - "name": "Phi-4-reasoning-plus", - "family": "phi", + "arcee-ai/trinity-large": { + "id": "arcee-ai/trinity-large", + "name": "Trinity Large", + "family": "trinity", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.125, "output": 0.5 }, - "limit": { "context": 32000, "output": 4096 } + "structured_output": false, + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "input": 131072, + "output": 8192 + }, + "cost": { + "input": 0.25, + "output": 1 + } }, - "grok-3": { - "id": "grok-3", - "name": "Grok 3", - "family": "grok", + "cognitivecomputations/dolphin-2.9.2-qwen2-72b": { + "id": "cognitivecomputations/dolphin-2.9.2-qwen2-72b", + "name": "Dolphin 72b", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-02-27", + "last_updated": "2025-02-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.75 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 8192, + "input": 8192, + "output": 4096 + }, + "cost": { + "input": 0.306, + "output": 0.306 + } }, - "cohere-command-a": { - "id": "cohere-command-a", - "name": "Command A", - "family": "command-a", + "deepcogito/cogito-v1-preview-qwen-32B": { + "id": "deepcogito/cogito-v1-preview-qwen-32B", + "name": "Cogito v1 Preview Qwen 32B", + "family": "qwen", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-06-01", - "release_date": "2025-03-13", - "last_updated": "2025-03-13", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 256000, "output": 8000 } - }, - "gpt-5-nano": { - "id": "gpt-5-nano", - "name": "GPT-5 Nano", - "family": "gpt-nano", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-05-10", + "last_updated": "2025-05-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.05, "output": 0.4, "cache_read": 0.01 }, - "limit": { "context": 272000, "output": 128000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 32768 + }, + "cost": { + "input": 1.7999999999999998, + "output": 1.7999999999999998 + } }, - "grok-4-fast-reasoning": { - "id": "grok-4-fast-reasoning", - "name": "Grok 4 Fast (Reasoning)", - "family": "grok", - "attachment": true, + "deepcogito/cogito-v2.1-671b": { + "id": "deepcogito/cogito-v2.1-671b", + "name": "Cogito v2.1 671B MoE", + "family": "cogito", + "attachment": false, "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-09-19", - "last_updated": "2025-09-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-11-19", + "last_updated": "2025-11-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 1.25, + "output": 1.25 + } }, - "phi-3.5-moe-instruct": { - "id": "phi-3.5-moe-instruct", - "name": "Phi-3.5-MoE-instruct", - "family": "phi", + "Salesforce/Llama-xLAM-2-70b-fc-r": { + "id": "Salesforce/Llama-xLAM-2-70b-fc-r", + "name": "Llama-xLAM-2 70B fc-r", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-08-20", - "last_updated": "2024-08-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.16, "output": 0.64 }, - "limit": { "context": 128000, "output": 4096 } + "structured_output": false, + "release_date": "2025-04-13", + "last_updated": "2025-04-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 2.5 + } }, - "meta-llama-3.1-405b-instruct": { - "id": "meta-llama-3.1-405b-instruct", - "name": "Meta-Llama-3.1-405B-Instruct", - "family": "llama", + "NousResearch 2/hermes-4-405b:thinking": { + "id": "NousResearch 2/hermes-4-405b:thinking", + "name": "Hermes 4 Large (Thinking)", + "family": "nousresearch", "attachment": false, "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 5.33, "output": 16 }, - "limit": { "context": 128000, "output": 32768 } + "structured_output": true, + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "phi-3-medium-128k-instruct": { - "id": "phi-3-medium-128k-instruct", - "name": "Phi-3-medium-instruct (128k)", - "family": "phi", + "NousResearch 2/DeepHermes-3-Mistral-24B-Preview": { + "id": "NousResearch 2/DeepHermes-3-Mistral-24B-Preview", + "name": "DeepHermes-3 Mistral 24B (Preview)", + "family": "nousresearch", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-04-23", - "last_updated": "2024-04-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.17, "output": 0.68 }, - "limit": { "context": 128000, "output": 4096 } - }, - "gpt-4.1-mini": { - "id": "gpt-4.1-mini", - "name": "GPT-4.1 mini", - "family": "gpt-mini", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-05", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-05-10", + "last_updated": "2025-05-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 1.6, "cache_read": 0.1 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 128000, + "input": 128000, + "output": 32768 + }, + "cost": { + "input": 0.3, + "output": 0.3 + } }, - "cohere-command-r-08-2024": { - "id": "cohere-command-r-08-2024", - "name": "Command R", - "family": "command-r", + "NousResearch 2/Hermes-4-70B:thinking": { + "id": "NousResearch 2/Hermes-4-70B:thinking", + "name": "Hermes 4 (Thinking)", + "family": "nousresearch", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-06-01", - "release_date": "2024-08-30", - "last_updated": "2024-08-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 128000, "output": 4000 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-09-17", + "last_updated": "2025-09-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "cost": { + "input": 0.2006, + "output": 0.39949999999999997 + } }, - "deepseek-v3.1": { - "id": "deepseek-v3.1", - "name": "DeepSeek-V3.1", - "family": "deepseek", + "NousResearch 2/hermes-4-405b": { + "id": "NousResearch 2/hermes-4-405b", + "name": "Hermes 4 Large", + "family": "nousresearch", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-08-21", - "last_updated": "2025-08-21", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.56, "output": 1.68 }, - "limit": { "context": 131072, "output": 131072 } - }, - "gpt-5.1": { - "id": "gpt-5.1", - "name": "GPT-5.1", - "family": "gpt", - "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text", "image", "audio"], "output": ["text", "image", "audio"] }, + "release_date": "2025-08-26", + "last_updated": "2025-08-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 272000, "output": 128000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "phi-4": { - "id": "phi-4", - "name": "Phi-4", - "family": "phi", + "NousResearch 2/hermes-3-llama-3.1-70b": { + "id": "NousResearch 2/hermes-3-llama-3.1-70b", + "name": "Hermes 3 70B", + "family": "nousresearch", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.125, "output": 0.5 }, - "limit": { "context": 128000, "output": 4096 } + "structured_output": false, + "release_date": "2026-01-07", + "last_updated": "2026-01-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 65536, + "input": 65536, + "output": 8192 + }, + "cost": { + "input": 0.408, + "output": 0.408 + } }, - "gpt-3.5-turbo-0301": { - "id": "gpt-3.5-turbo-0301", - "name": "GPT-3.5 Turbo 0301", - "family": "gpt", + "NousResearch 2/hermes-4-70b": { + "id": "NousResearch 2/hermes-4-70b", + "name": "Hermes 4 Medium", + "family": "nousresearch", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2021-08", - "release_date": "2023-03-01", - "last_updated": "2023-03-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.5, "output": 2 }, - "limit": { "context": 4096, "output": 4096 } - }, - "gpt-5.2": { - "id": "gpt-5.2", - "name": "GPT-5.2", - "family": "gpt", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-07-03", + "last_updated": "2025-07-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "cost": { + "input": 0.2006, + "output": 0.39949999999999997 + } }, - "gpt-5.2-chat": { - "id": "gpt-5.2-chat", - "name": "GPT-5.2 Chat", - "family": "gpt-codex", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "soob3123/Veiled-Calla-12B": { + "id": "soob3123/Veiled-Calla-12B", + "name": "Veiled Calla 12B", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-04-13", + "last_updated": "2025-04-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 32768, + "input": 32768, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 0.3 + } }, - "mistral-nemo": { - "id": "mistral-nemo", - "name": "Mistral Nemo", - "family": "mistral-nemo", + "soob3123/GrayLine-Qwen3-8B": { + "id": "soob3123/GrayLine-Qwen3-8B", + "name": "Grayline Qwen3 8B", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-07", - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.15 }, - "limit": { "context": 128000, "output": 128000 } - }, - "ministral-3b": { - "id": "ministral-3b", - "name": "Ministral 3B", - "family": "ministral", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-03", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.04, "output": 0.04 }, - "limit": { "context": 128000, "output": 8192 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16384, + "input": 16384, + "output": 32768 + }, + "cost": { + "input": 0.3, + "output": 0.3 + } }, - "text-embedding-ada-002": { - "id": "text-embedding-ada-002", - "name": "text-embedding-ada-002", - "family": "text-embedding", + "soob3123/amoral-gemma3-27B-v2": { + "id": "soob3123/amoral-gemma3-27B-v2", + "name": "Amoral Gemma3 27B v2", + "family": "gemma", "attachment": false, "reasoning": false, "tool_call": false, - "release_date": "2022-12-15", - "last_updated": "2022-12-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-05-23", + "last_updated": "2025-05-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0 }, - "limit": { "context": 8192, "output": 1536 } + "limit": { + "context": 32768, + "input": 32768, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 0.3 + } }, - "deepseek-v3.2-speciale": { - "id": "deepseek-v3.2-speciale", - "name": "DeepSeek-V3.2-Speciale", + "nex-agi/deepseek-v3.1-nex-n1": { + "id": "nex-agi/deepseek-v3.1-nex-n1", + "name": "DeepSeek V3.1 Nex N1", "family": "deepseek", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.58, "output": 1.68 }, - "limit": { "context": 128000, "output": 128000 } + "structured_output": false, + "release_date": "2025-12-10", + "last_updated": "2025-12-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "cost": { + "input": 0.27999999999999997, + "output": 0.42000000000000004 + } }, - "llama-3.2-90b-vision-instruct": { - "id": "llama-3.2-90b-vision-instruct", - "name": "Llama-3.2-90B-Vision-Instruct", + "Envoid/Llama-3.05-NT-Storybreaker-Ministral-70B": { + "id": "Envoid/Llama-3.05-NT-Storybreaker-Ministral-70B", + "name": "Llama 3.05 Storybreaker Ministral 70b", "family": "llama", - "attachment": true, + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2.04, "output": 2.04 }, - "limit": { "context": 128000, "output": 8192 } + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + }, + "cost": { + "input": 0.49299999999999994, + "output": 0.49299999999999994 + } }, - "gpt-4": { - "id": "gpt-4", - "name": "GPT-4", - "family": "gpt", + "Envoid/Llama-3.05-Nemotron-Tenyxchat-Storybreaker-70B": { + "id": "Envoid/Llama-3.05-Nemotron-Tenyxchat-Storybreaker-70B", + "name": "Nemotron Tenyxchat Storybreaker 70b", + "family": "nemotron", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-11", - "release_date": "2023-03-14", - "last_updated": "2023-03-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 60, "output": 120 }, - "limit": { "context": 8192, "output": 8192 } + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + }, + "cost": { + "input": 0.49299999999999994, + "output": 0.49299999999999994 + } }, - "mistral-medium-2505": { - "id": "mistral-medium-2505", - "name": "Mistral Medium 3", - "family": "mistral-medium", + "anthracite-org/magnum-v4-72b": { + "id": "anthracite-org/magnum-v4-72b", + "name": "Magnum v4 72B", + "family": "llama", "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-05", - "release_date": "2025-05-07", - "last_updated": "2025-05-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + }, + "cost": { + "input": 2.006, + "output": 2.992 + } }, - "gpt-4-32k": { - "id": "gpt-4-32k", - "name": "GPT-4 32K", - "family": "gpt", + "anthracite-org/magnum-v2-72b": { + "id": "anthracite-org/magnum-v2-72b", + "name": "Magnum V2 72B", + "family": "llama", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-11", - "release_date": "2023-03-14", - "last_updated": "2023-03-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 60, "output": 120 }, - "limit": { "context": 32768, "output": 32768 } - }, - "grok-3-mini": { - "id": "grok-3-mini", - "name": "Grok 3 Mini", - "family": "grok", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-01", + "last_updated": "2024-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 0.5, "reasoning": 0.5, "cache_read": 0.075 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + }, + "cost": { + "input": 2.006, + "output": 2.992 + } }, - "meta-llama-3-8b-instruct": { - "id": "meta-llama-3-8b-instruct", - "name": "Meta-Llama-3-8B-Instruct", + "ReadyArt/MS3.2-The-Omega-Directive-24B-Unslop-v2.0": { + "id": "ReadyArt/MS3.2-The-Omega-Directive-24B-Unslop-v2.0", + "name": "Omega Directive 24B Unslop v2.0", "family": "llama", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-04-18", - "last_updated": "2024-04-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 0.61 }, - "limit": { "context": 8192, "output": 2048 } + "structured_output": false, + "release_date": "2025-12-08", + "last_updated": "2025-12-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16384, + "input": 16384, + "output": 32768 + }, + "cost": { + "input": 0.5, + "output": 0.5 + } }, - "phi-3.5-mini-instruct": { - "id": "phi-3.5-mini-instruct", - "name": "Phi-3.5-mini-instruct", - "family": "phi", + "ReadyArt/The-Omega-Abomination-L-70B-v1.0": { + "id": "ReadyArt/The-Omega-Abomination-L-70B-v1.0", + "name": "The Omega Abomination V1", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-08-20", - "last_updated": "2024-08-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.13, "output": 0.52 }, - "limit": { "context": 128000, "output": 4096 } + "structured_output": false, + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + }, + "cost": { + "input": 0.7, + "output": 0.95 + } }, - "meta-llama-3-70b-instruct": { - "id": "meta-llama-3-70b-instruct", - "name": "Meta-Llama-3-70B-Instruct", + "undi95/remm-slerp-l2-13b": { + "id": "undi95/remm-slerp-l2-13b", + "name": "ReMM SLERP 13B", "family": "llama", - "attachment": false, + "attachment": true, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-04-18", - "last_updated": "2024-04-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2.68, "output": 3.54 }, - "limit": { "context": 8192, "output": 2048 } + "structured_output": false, + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 6144, + "input": 6144, + "output": 4096 + }, + "cost": { + "input": 0.7989999999999999, + "output": 1.2069999999999999 + } }, - "cohere-command-r-plus-08-2024": { - "id": "cohere-command-r-plus-08-2024", - "name": "Command R+", - "family": "command-r", + "MarinaraSpaghetti/NemoMix-Unleashed-12B": { + "id": "MarinaraSpaghetti/NemoMix-Unleashed-12B", + "name": "NemoMix 12B Unleashed", + "family": "mistral-nemo", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-06-01", - "release_date": "2024-08-30", - "last_updated": "2024-08-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 128000, "output": 4000 } + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-01", + "last_updated": "2024-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "input": 32768, + "output": 8192 + }, + "cost": { + "input": 0.49299999999999994, + "output": 0.49299999999999994 + } }, - "gpt-5-chat": { - "id": "gpt-5-chat", - "name": "GPT-5 Chat", - "family": "gpt-codex", + "allenai/molmo-2-8b": { + "id": "allenai/molmo-2-8b", + "name": "Molmo 2 8B", + "family": "allenai", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": false, - "temperature": false, - "knowledge": "2024-10-24", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": false, + "release_date": "2026-02-14", + "last_updated": "2026-02-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.13 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 36864, + "input": 36864, + "output": 36864 + }, + "cost": { + "input": 0.2, + "output": 0.2 + } }, - "mistral-small-2503": { - "id": "mistral-small-2503", - "name": "Mistral Small 3.1", - "family": "mistral-small", - "attachment": true, + "allenai/olmo-3.1-32b-instruct": { + "id": "allenai/olmo-3.1-32b-instruct", + "name": "Olmo 3.1 32B Instruct", + "family": "allenai", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-09", - "release_date": "2025-03-01", - "last_updated": "2025-03-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2026-01-25", + "last_updated": "2026-01-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 65536, + "input": 65536, + "output": 8192 + }, + "cost": { + "input": 0.2, + "output": 0.6 + } }, - "mai-ds-r1": { - "id": "mai-ds-r1", - "name": "MAI-DS-R1", - "family": "mai", + "allenai/olmo-3.1-32b-think": { + "id": "allenai/olmo-3.1-32b-think", + "name": "Olmo 3.1 32B Think", + "family": "allenai", "attachment": false, "reasoning": true, "tool_call": false, - "temperature": true, - "knowledge": "2024-06", - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2026-01-25", + "last_updated": "2026-01-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.35, "output": 5.4 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 65536, + "input": 65536, + "output": 8192 + }, + "cost": { + "input": 0.15, + "output": 0.5 + } }, - "o3": { - "id": "o3", - "name": "o3", - "family": "o", - "attachment": true, + "allenai/olmo-3-32b-think": { + "id": "allenai/olmo-3-32b-think", + "name": "Olmo 3 32B Think", + "family": "allenai", + "attachment": false, "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-11-01", + "last_updated": "2025-11-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 0.44999999999999996 + } }, - "o1": { - "id": "o1", - "name": "o1", - "family": "o", + "stepfun-ai/step-3.5-flash:thinking": { + "id": "stepfun-ai/step-3.5-flash:thinking", + "name": "Step 3.5 Flash Thinking", + "family": "step", "attachment": false, "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2023-09", - "release_date": "2024-12-05", - "last_updated": "2024-12-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2026-02-02", + "last_updated": "2026-02-02", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 60, "cache_read": 7.5 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 256000, + "input": 256000, + "output": 256000 + }, + "cost": { + "input": 0.2, + "output": 0.5 + } }, - "gpt-5.4": { - "id": "gpt-5.4", - "name": "GPT-5.4", - "family": "gpt", - "attachment": true, + "stepfun-ai/step-3.5-flash": { + "id": "stepfun-ai/step-3.5-flash", + "name": "Step 3.5 Flash", + "family": "step", + "attachment": false, "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2026-02-02", + "last_updated": "2026-02-02", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 15, "cache_read": 0.25 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 256000, + "input": 256000, + "output": 256000 + }, + "cost": { + "input": 0.2, + "output": 0.5 + } }, - "gpt-5.1-chat": { - "id": "gpt-5.1-chat", - "name": "GPT-5.1 Chat", - "family": "gpt-codex", - "attachment": true, + "zai-org/glm-4.7": { + "id": "zai-org/glm-4.7", + "name": "GLM 4.7", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text", "image", "audio"], "output": ["text", "image", "audio"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 128000, "output": 16384 } + "release_date": "2026-01-29", + "last_updated": "2026-01-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 200000, + "input": 200000, + "output": 128000 + }, + "cost": { + "input": 0.15, + "output": 0.8 + } }, - "gpt-3.5-turbo-instruct": { - "id": "gpt-3.5-turbo-instruct", - "name": "GPT-3.5 Turbo Instruct", - "family": "gpt", + "zai-org/glm-5": { + "id": "zai-org/glm-5", + "name": "GLM 5", + "family": "glm", "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2021-08", - "release_date": "2023-09-21", - "last_updated": "2023-09-21", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.5, "output": 2 }, - "limit": { "context": 4096, "output": 4096 } - }, - "model-router": { - "id": "model-router", - "name": "Model Router", - "family": "model-router", - "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "release_date": "2025-05-19", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.14, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } - }, - "phi-3-medium-4k-instruct": { - "id": "phi-3-medium-4k-instruct", - "name": "Phi-3-medium-instruct (4k)", - "family": "phi", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-04-23", - "last_updated": "2024-04-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.17, "output": 0.68 }, - "limit": { "context": 4096, "output": 1024 } + "limit": { + "context": 200000, + "input": 200000, + "output": 128000 + }, + "cost": { + "input": 0.3, + "output": 2.55 + } }, - "phi-3-mini-4k-instruct": { - "id": "phi-3-mini-4k-instruct", - "name": "Phi-3-mini-instruct (4k)", - "family": "phi", + "zai-org/glm-5.1": { + "id": "zai-org/glm-5.1", + "name": "GLM 5.1", + "family": "glm", "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-04-23", - "last_updated": "2024-04-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "release_date": "2026-03-27", + "last_updated": "2026-03-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.13, "output": 0.52 }, - "limit": { "context": 4096, "output": 1024 } + "limit": { + "context": 200000, + "input": 200000, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 2.55 + } }, - "llama-3.2-11b-vision-instruct": { - "id": "llama-3.2-11b-vision-instruct", - "name": "Llama-3.2-11B-Vision-Instruct", - "family": "llama", - "attachment": true, - "reasoning": false, + "zai-org/glm-5.1:thinking": { + "id": "zai-org/glm-5.1:thinking", + "name": "GLM 5.1 Thinking", + "family": "glm", + "attachment": false, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "release_date": "2026-03-27", + "last_updated": "2026-03-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.37, "output": 0.37 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 200000, + "input": 200000, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 2.55 + } }, - "llama-4-scout-17b-16e-instruct": { - "id": "llama-4-scout-17b-16e-instruct", - "name": "Llama 4 Scout 17B 16E Instruct", - "family": "llama", - "attachment": true, - "reasoning": false, + "zai-org/glm-5:thinking": { + "id": "zai-org/glm-5:thinking", + "name": "GLM 5 Thinking", + "family": "glm", + "attachment": false, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 0.78 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 200000, + "input": 200000, + "output": 128000 + }, + "cost": { + "input": 0.3, + "output": 2.55 + } }, - "phi-4-mini-reasoning": { - "id": "phi-4-mini-reasoning", - "name": "Phi-4-mini-reasoning", - "family": "phi", + "zai-org/glm-4.7-flash": { + "id": "zai-org/glm-4.7-flash", + "name": "GLM 4.7 Flash", + "family": "glm-flash", "attachment": false, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "release_date": "2026-01-19", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.075, "output": 0.3 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 200000, + "input": 200000, + "output": 128000 + }, + "cost": { + "input": 0.07, + "output": 0.4 + } }, - "codex-mini": { - "id": "codex-mini", - "name": "Codex Mini", - "family": "gpt-codex-mini", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2024-04", - "release_date": "2025-05-16", - "last_updated": "2025-05-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "featherless-ai/Qwerky-72B": { + "id": "featherless-ai/Qwerky-72B", + "name": "Qwerky 72B", + "family": "qwerky", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-03-20", + "last_updated": "2025-03-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.5, "output": 6, "cache_read": 0.375 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 32000, + "input": 32000, + "output": 8192 + }, + "cost": { + "input": 0.5, + "output": 0.5 + } }, - "gpt-4.1": { - "id": "gpt-4.1", - "name": "GPT-4.1", - "family": "gpt", - "attachment": true, + "mlabonne/NeuralDaredevil-8B-abliterated": { + "id": "mlabonne/NeuralDaredevil-8B-abliterated", + "name": "Neural Daredevil 8B abliterated", + "family": "llama", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-05", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 8192, + "input": 8192, + "output": 8192 + }, + "cost": { + "input": 0.44, + "output": 0.44 + } }, - "gpt-4o-mini": { - "id": "gpt-4o-mini", - "name": "GPT-4o mini", - "family": "gpt-mini", + "raifle/sorcererlm-8x22b": { + "id": "raifle/sorcererlm-8x22b", + "name": "SorcererLM 8x22B", + "family": "mixtral", "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-09", - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.6, "cache_read": 0.08 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 16000, + "input": 16000, + "output": 8192 + }, + "cost": { + "input": 4.505, + "output": 4.505 + } }, - "mistral-large-2411": { - "id": "mistral-large-2411", - "name": "Mistral Large 24.11", - "family": "mistral-large", + "mistralai/mixtral-8x7b-instruct-v0.1": { + "id": "mistralai/mixtral-8x7b-instruct-v0.1", + "name": "Mixtral 8x7B", + "family": "mixtral", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-09", - "release_date": "2024-11-01", - "last_updated": "2024-11-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 6 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + }, + "cost": { + "input": 0.27, + "output": 0.27 + } }, - "gpt-4-turbo-vision": { - "id": "gpt-4-turbo-vision", - "name": "GPT-4 Turbo Vision", - "family": "gpt", - "attachment": true, + "mistralai/mistral-saba": { + "id": "mistralai/mistral-saba", + "name": "Mistral Saba", + "family": "mistral", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-11", - "release_date": "2023-11-06", - "last_updated": "2024-04-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 10, "output": 30 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 32000, + "input": 32000, + "output": 32768 + }, + "cost": { + "input": 0.1989, + "output": 0.595 + } }, - "gpt-5-mini": { - "id": "gpt-5-mini", - "name": "GPT-5 Mini", - "family": "gpt-mini", + "mistralai/mistral-large-3-675b-instruct-2512": { + "id": "mistralai/mistral-large-3-675b-instruct-2512", + "name": "Mistral Large 3 675B", + "family": "mistral-large", "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-02", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.03 }, - "limit": { "context": 272000, "output": 128000 } + "limit": { + "context": 262144, + "input": 262144, + "output": 256000 + }, + "cost": { + "input": 1, + "output": 3 + } }, - "gpt-5.3-codex": { - "id": "gpt-5.3-codex", - "name": "GPT-5.3 Codex", - "family": "gpt-codex", + "mistralai/devstral-2-123b-instruct-2512": { + "id": "mistralai/devstral-2-123b-instruct-2512", + "name": "Devstral 2 123B", + "family": "devstral", "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-02-24", - "last_updated": "2026-02-24", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-09", + "last_updated": "2025-12-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 262144, + "input": 262144, + "output": 65536 + }, + "cost": { + "input": 0.4, + "output": 1.4 + } }, - "codestral-2501": { - "id": "codestral-2501", - "name": "Codestral 25.01", + "mistralai/codestral-2508": { + "id": "mistralai/codestral-2508", + "name": "Codestral 2508", "family": "codestral", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-03", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-01", + "last_updated": "2025-08-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 0.9 }, - "limit": { "context": 256000, "output": 256000 } + "limit": { + "context": 256000, + "input": 256000, + "output": 32768 + }, + "cost": { + "input": 0.3, + "output": 0.8999999999999999 + } }, - "grok-code-fast-1": { - "id": "grok-code-fast-1", - "name": "Grok Code Fast 1", - "family": "grok", + "mistralai/ministral-14b-instruct-2512": { + "id": "mistralai/ministral-14b-instruct-2512", + "name": "Ministral 3 14B", + "family": "ministral", + "attachment": true, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-02", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "input": 262144, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } + }, + "mistralai/mistral-tiny": { + "id": "mistralai/mistral-tiny", + "name": "Mistral Tiny", + "family": "mistral", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2025-08-28", - "last_updated": "2025-08-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2023-12-11", + "last_updated": "2024-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 1.5, "cache_read": 0.02 }, - "limit": { "context": 256000, "output": 10000 } + "limit": { + "context": 32000, + "input": 32000, + "output": 8192 + }, + "cost": { + "input": 0.25499999999999995, + "output": 0.25499999999999995 + } }, - "phi-4-reasoning": { - "id": "phi-4-reasoning", - "name": "Phi-4-reasoning", - "family": "phi", + "mistralai/ministral-8b-2512": { + "id": "mistralai/ministral-8b-2512", + "name": "Ministral 8B", + "family": "ministral", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.125, "output": 0.5 }, - "limit": { "context": 32000, "output": 4096 } + "structured_output": false, + "release_date": "2025-12-04", + "last_updated": "2025-12-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "input": 262144, + "output": 32768 + }, + "cost": { + "input": 0.15, + "output": 0.15 + } }, - "phi-4-mini": { - "id": "phi-4-mini", - "name": "Phi-4-mini", - "family": "phi", + "mistralai/mixtral-8x22b-instruct-v0.1": { + "id": "mistralai/mixtral-8x22b-instruct-v0.1", + "name": "Mixtral 8x22B", + "family": "mixtral", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.075, "output": 0.3 }, - "limit": { "context": 128000, "output": 4096 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 65536, + "input": 65536, + "output": 32768 + }, + "cost": { + "input": 0.8999999999999999, + "output": 0.8999999999999999 + } }, - "gpt-3.5-turbo-1106": { - "id": "gpt-3.5-turbo-1106", - "name": "GPT-3.5 Turbo 1106", - "family": "gpt", + "mistralai/mistral-medium-3.1": { + "id": "mistralai/mistral-medium-3.1", + "name": "Mistral Medium 3.1", + "family": "mistral-medium", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2021-08", - "release_date": "2023-11-06", - "last_updated": "2023-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 2 }, - "limit": { "context": 16384, "output": 16384 } + "limit": { + "context": 131072, + "input": 131072, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 2 + } }, - "grok-4": { - "id": "grok-4", - "name": "Grok 4", - "family": "grok", + "mistralai/ministral-3b-2512": { + "id": "mistralai/ministral-3b-2512", + "name": "Ministral 3B", + "family": "ministral", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-04", + "last_updated": "2025-12-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "reasoning": 15, "cache_read": 0.75 }, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 131072, + "input": 131072, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "grok-4-fast-non-reasoning": { - "id": "grok-4-fast-non-reasoning", - "name": "Grok 4 Fast (Non-Reasoning)", - "family": "grok", - "attachment": true, + "mistralai/Mistral-Nemo-Instruct-2407": { + "id": "mistralai/Mistral-Nemo-Instruct-2407", + "name": "Mistral Nemo", + "family": "mistral-nemo", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-09-19", - "last_updated": "2025-09-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + }, + "cost": { + "input": 0.1003, + "output": 0.1207 + } }, - "gpt-4o": { - "id": "gpt-4o", - "name": "GPT-4o", - "family": "gpt", + "mistralai/mistral-medium-3": { + "id": "mistralai/mistral-medium-3", + "name": "Mistral Medium 3", + "family": "mistral-medium", "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-09", - "release_date": "2024-05-13", - "last_updated": "2024-05-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 10, "cache_read": 1.25 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 131072, + "input": 131072, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 2 + } }, - "gpt-4-turbo": { - "id": "gpt-4-turbo", - "name": "GPT-4 Turbo", - "family": "gpt", + "mistralai/mistral-7b-instruct": { + "id": "mistralai/mistral-7b-instruct", + "name": "Mistral 7B Instruct", + "family": "mistral", "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-11", - "release_date": "2023-11-06", - "last_updated": "2024-04-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-05-27", + "last_updated": "2024-05-27", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 10, "output": 30 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 32768, + "input": 32768, + "output": 8192 + }, + "cost": { + "input": 0.0544, + "output": 0.0544 + } }, - "kimi-k2.5": { - "id": "kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi", + "mistralai/Devstral-Small-2505": { + "id": "mistralai/Devstral-Small-2505", + "name": "Mistral Devstral Small 2505", + "family": "devstral", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-02-06", - "last_updated": "2026-02-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 3 }, - "limit": { "context": 262144, "output": 262144 }, - "provider": { - "npm": "@ai-sdk/openai-compatible", - "api": "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/models", - "shape": "completions" + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-02", + "last_updated": "2025-08-02", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "input": 32768, + "output": 8192 + }, + "cost": { + "input": 0.060000000000000005, + "output": 0.060000000000000005 } }, - "phi-3-small-8k-instruct": { - "id": "phi-3-small-8k-instruct", - "name": "Phi-3-small-instruct (8k)", - "family": "phi", + "mistralai/mistral-small-creative": { + "id": "mistralai/mistral-small-creative", + "name": "Mistral Small Creative", + "family": "mistral-small", "attachment": false, "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-04-23", - "last_updated": "2024-04-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 8192, "output": 2048 } + "tool_call": true, + "structured_output": true, + "release_date": "2025-12-16", + "last_updated": "2025-12-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } }, - "phi-3-mini-128k-instruct": { - "id": "phi-3-mini-128k-instruct", - "name": "Phi-3-mini-instruct (128k)", - "family": "phi", + "mistralai/mistral-large": { + "id": "mistralai/mistral-large", + "name": "Mistral Large 2411", + "family": "mistral-large", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-04-23", - "last_updated": "2024-04-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.13, "output": 0.52 }, - "limit": { "context": 128000, "output": 4096 } + "structured_output": false, + "release_date": "2024-02-26", + "last_updated": "2024-02-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 256000 + }, + "cost": { + "input": 2.006, + "output": 6.001 + } }, - "llama-3.3-70b-instruct": { - "id": "llama-3.3-70b-instruct", - "name": "Llama-3.3-70B-Instruct", - "family": "llama", + "mistralai/ministral-14b-2512": { + "id": "mistralai/ministral-14b-2512", + "name": "Ministral 14B", + "family": "ministral", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-12", + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-04", + "last_updated": "2025-12-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "input": 262144, + "output": 32768 + }, + "cost": { + "input": 0.2, + "output": 0.2 + } + }, + "shisa-ai/shisa-v2.1-llama3.3-70b": { + "id": "shisa-ai/shisa-v2.1-llama3.3-70b", + "name": "Shisa V2.1 Llama 3.3 70B", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, "release_date": "2024-12-06", "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.71, "output": 0.71 }, - "limit": { "context": 128000, "output": 32768 } - }, - "gpt-5": { - "id": "gpt-5", - "name": "GPT-5", - "family": "gpt", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.13 }, - "limit": { "context": 272000, "output": 128000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 4096 + }, + "cost": { + "input": 0.5, + "output": 0.5 + } }, - "deepseek-r1": { - "id": "deepseek-r1", - "name": "DeepSeek-R1", - "family": "deepseek-thinking", + "shisa-ai/shisa-v2-llama3.3-70b": { + "id": "shisa-ai/shisa-v2-llama3.3-70b", + "name": "Shisa V2 Llama 3.3 70B", + "family": "llama", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1.35, "output": 5.4 }, - "limit": { "context": 163840, "output": 163840 } + "structured_output": false, + "release_date": "2025-07-26", + "last_updated": "2025-07-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0.5, + "output": 0.5 + } }, - "kimi-k2-thinking": { - "id": "kimi-k2-thinking", - "name": "Kimi K2 Thinking", - "family": "kimi-thinking", + "meta-llama/llama-3.3-70b-instruct": { + "id": "meta-llama/llama-3.3-70b-instruct", + "name": "Llama 3.3 70b Instruct", + "family": "llama", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-11-06", - "last_updated": "2025-12-02", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.5, "cache_read": 0.15 }, - "limit": { "context": 262144, "output": 262144 } + "structured_output": true, + "release_date": "2025-02-27", + "last_updated": "2025-02-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "input": 131072, + "output": 16384 + }, + "cost": { + "input": 0.05, + "output": 0.23 + } }, - "meta-llama-3.1-8b-instruct": { - "id": "meta-llama-3.1-8b-instruct", - "name": "Meta-Llama-3.1-8B-Instruct", + "meta-llama/llama-4-scout": { + "id": "meta-llama/llama-4-scout", + "name": "Llama 4 Scout", "family": "llama", - "attachment": false, + "attachment": true, "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 0.61 }, - "limit": { "context": 128000, "output": 32768 } + "structured_output": true, + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 328000, + "input": 328000, + "output": 65536 + }, + "cost": { + "input": 0.085, + "output": 0.46 + } }, - "deepseek-v3-0324": { - "id": "deepseek-v3-0324", - "name": "DeepSeek-V3-0324", - "family": "deepseek", - "attachment": false, + "meta-llama/llama-4-maverick": { + "id": "meta-llama/llama-4-maverick", + "name": "Llama 4 Maverick", + "family": "llama", + "attachment": true, "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-03-24", - "last_updated": "2025-03-24", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1.14, "output": 4.56 }, - "limit": { "context": 131072, "output": 131072 } + "structured_output": true, + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "input": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.18000000000000002, + "output": 0.8 + } }, - "gpt-3.5-turbo-0613": { - "id": "gpt-3.5-turbo-0613", - "name": "GPT-3.5 Turbo 0613", - "family": "gpt", + "meta-llama/llama-3.2-90b-vision-instruct": { + "id": "meta-llama/llama-3.2-90b-vision-instruct", + "name": "Llama 3.2 Medium", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2021-08", - "release_date": "2023-06-13", - "last_updated": "2023-06-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 4 }, - "limit": { "context": 16384, "output": 16384 } + "limit": { + "context": 131072, + "input": 131072, + "output": 16384 + }, + "cost": { + "input": 0.9009999999999999, + "output": 0.9009999999999999 + } }, - "gpt-5-pro": { - "id": "gpt-5-pro", - "name": "GPT-5 Pro", - "family": "gpt-pro", + "meta-llama/llama-3.2-3b-instruct": { + "id": "meta-llama/llama-3.2-3b-instruct", + "name": "Llama 3.2 3b Instruct", + "family": "llama", "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-10-06", - "last_updated": "2025-10-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-09-25", + "last_updated": "2024-09-25", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 120 }, - "limit": { "context": 400000, "output": 272000 } + "limit": { + "context": 131072, + "input": 131072, + "output": 8192 + }, + "cost": { + "input": 0.0306, + "output": 0.0493 + } }, - "o3-mini": { - "id": "o3-mini", - "name": "o3-mini", - "family": "o-mini", + "meta-llama/llama-3.1-8b-instruct": { + "id": "meta-llama/llama-3.1-8b-instruct", + "name": "Llama 3.1 8b Instruct", + "family": "llama", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2024-12-20", - "last_updated": "2025-01-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.55 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 131072, + "input": 131072, + "output": 16384 + }, + "cost": { + "input": 0.0544, + "output": 0.0544 + } }, - "cohere-embed-v3-english": { - "id": "cohere-embed-v3-english", - "name": "Embed v3 English", - "family": "cohere-embed", + "GalrionSoftworks/MN-LooseCannon-12B-v1": { + "id": "GalrionSoftworks/MN-LooseCannon-12B-v1", + "name": "MN-LooseCannon-12B-v1", + "family": "mistral-nemo", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": false, - "release_date": "2023-11-07", - "last_updated": "2023-11-07", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0 }, - "limit": { "context": 512, "output": 1024 } - }, - "gpt-5.4-pro": { - "id": "gpt-5.4-pro", - "name": "GPT-5.4 Pro", - "family": "gpt-pro", - "attachment": true, - "reasoning": true, - "tool_call": true, "structured_output": false, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2024-07-01", + "last_updated": "2024-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 30, "output": 180 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + }, + "cost": { + "input": 0.49299999999999994, + "output": 0.49299999999999994 + } }, - "cohere-embed-v3-multilingual": { - "id": "cohere-embed-v3-multilingual", - "name": "Embed v3 Multilingual", - "family": "cohere-embed", + "baseten/Kimi-K2-Instruct-FP4": { + "id": "baseten/Kimi-K2-Instruct-FP4", + "name": "Kimi K2 0711 Instruct FP4", + "family": "kimi", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": false, - "release_date": "2023-11-07", - "last_updated": "2023-11-07", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0 }, - "limit": { "context": 512, "output": 1024 } + "structured_output": false, + "release_date": "2025-07-11", + "last_updated": "2025-07-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 131072 + }, + "cost": { + "input": 0.1, + "output": 2 + } }, - "phi-4-multimodal": { - "id": "phi-4-multimodal", - "name": "Phi-4-multimodal", - "family": "phi", - "attachment": true, + "Gryphe/MythoMax-L2-13b": { + "id": "Gryphe/MythoMax-L2-13b", + "name": "MythoMax 13B", + "family": "llama", + "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text", "image", "audio"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.08, "output": 0.32, "input_audio": 4 }, - "limit": { "context": 128000, "output": 4096 } + "structured_output": false, + "release_date": "2025-08-08", + "last_updated": "2025-08-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 4000, + "input": 4000, + "output": 4096 + }, + "cost": { + "input": 0.1003, + "output": 0.1003 + } }, - "gpt-5.1-codex-mini": { - "id": "gpt-5.1-codex-mini", - "name": "GPT-5.1 Codex Mini", - "family": "gpt-codex", - "attachment": false, + "x-ai/grok-4-fast:thinking": { + "id": "x-ai/grok-4-fast:thinking", + "name": "Grok 4 Fast Thinking", + "family": "grok", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.025 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 2000000, + "input": 2000000, + "output": 131072 + }, + "cost": { + "input": 0.2, + "output": 0.5 + } }, - "text-embedding-3-large": { - "id": "text-embedding-3-large", - "name": "text-embedding-3-large", - "family": "text-embedding", - "attachment": false, - "reasoning": false, + "x-ai/grok-4-07-09": { + "id": "x-ai/grok-4-07-09", + "name": "Grok 4", + "family": "grok", + "attachment": true, + "reasoning": true, "tool_call": false, - "release_date": "2024-01-25", - "last_updated": "2024-01-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.13, "output": 0 }, - "limit": { "context": 8191, "output": 3072 } + "limit": { + "context": 256000, + "input": 256000, + "output": 131072 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "gpt-5.2-codex": { - "id": "gpt-5.2-codex", - "name": "GPT-5.2 Codex", - "family": "gpt-codex", - "attachment": false, + "x-ai/grok-4-fast": { + "id": "x-ai/grok-4-fast", + "name": "Grok 4 Fast", + "family": "grok", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-01-14", - "last_updated": "2026-01-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-09-20", + "last_updated": "2025-09-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "output": 128000 } - } - } - }, - "zai": { - "id": "zai", - "env": ["ZHIPU_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.z.ai/api/paas/v4", - "name": "Z.AI", - "doc": "https://docs.z.ai/guides/overview/pricing", - "models": { - "glm-4.7-flash": { - "id": "glm-4.7-flash", - "name": "GLM-4.7-Flash", - "family": "glm-flash", + "limit": { + "context": 2000000, + "input": 2000000, + "output": 131072 + }, + "cost": { + "input": 0.2, + "output": 0.5 + } + }, + "x-ai/grok-code-fast-1": { + "id": "x-ai/grok-code-fast-1", + "name": "Grok Code Fast 1", + "family": "grok", "attachment": false, "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-01-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 200000, "output": 131072 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-28", + "last_updated": "2025-08-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "input": 256000, + "output": 131072 + }, + "cost": { + "input": 0.2, + "output": 1.5 + } }, - "glm-5-turbo": { - "id": "glm-5-turbo", - "name": "GLM-5-Turbo", - "family": "glm", - "attachment": false, + "x-ai/grok-4.1-fast": { + "id": "x-ai/grok-4.1-fast", + "name": "Grok 4.1 Fast", + "family": "grok", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, - "temperature": true, - "release_date": "2026-03-16", - "last_updated": "2026-03-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-11-20", + "last_updated": "2025-11-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.2, "output": 4, "cache_read": 0.24, "cache_write": 0 }, - "limit": { "context": 200000, "output": 131072 } + "limit": { + "context": 2000000, + "input": 2000000, + "output": 131072 + }, + "cost": { + "input": 0.2, + "output": 0.5 + } }, - "glm-4.5": { - "id": "glm-4.5", - "name": "GLM-4.5", - "family": "glm", - "attachment": false, + "x-ai/grok-4.1-fast-reasoning": { + "id": "x-ai/grok-4.1-fast-reasoning", + "name": "Grok 4.1 Fast Reasoning", + "family": "grok", + "attachment": true, "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.2, "cache_read": 0.11, "cache_write": 0 }, - "limit": { "context": 131072, "output": 98304 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-11-20", + "last_updated": "2025-11-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "input": 2000000, + "output": 131072 + }, + "cost": { + "input": 0.2, + "output": 0.5 + } }, - "glm-4.7-flashx": { - "id": "glm-4.7-flashx", - "name": "GLM-4.7-FlashX", - "family": "glm-flash", + "tencent/Hunyuan-MT-7B": { + "id": "tencent/Hunyuan-MT-7B", + "name": "Hunyuan MT 7B", + "family": "hunyuan", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-01-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.07, "output": 0.4, "cache_read": 0.01, "cache_write": 0 }, - "limit": { "context": 200000, "output": 131072 } + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-09-18", + "last_updated": "2025-09-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8192, + "input": 8192, + "output": 8192 + }, + "cost": { + "input": 10, + "output": 20 + } }, - "glm-4.6": { - "id": "glm-4.6", - "name": "GLM-4.6", - "family": "glm", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.2, "cache_read": 0.11, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } + "microsoft/wizardlm-2-8x22b": { + "id": "microsoft/wizardlm-2-8x22b", + "name": "WizardLM-2 8x22B", + "family": "gpt", + "attachment": true, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-04-15", + "last_updated": "2025-04-15", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 65536, + "input": 65536, + "output": 8192 + }, + "cost": { + "input": 0.49299999999999994, + "output": 0.49299999999999994 + } }, - "glm-4.6v": { - "id": "glm-4.6v", - "name": "GLM-4.6V", - "family": "glm", + "microsoft/MAI-DS-R1-FP8": { + "id": "microsoft/MAI-DS-R1-FP8", + "name": "Microsoft DeepSeek R1", + "family": "deepseek", "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-08", - "last_updated": "2025-12-08", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 0.9 }, - "limit": { "context": 128000, "output": 32768 } + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 0.3 + } }, - "glm-4.5-flash": { - "id": "glm-4.5-flash", - "name": "GLM-4.5-Flash", - "family": "glm-flash", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 131072, "output": 98304 } - }, - "glm-5": { - "id": "glm-5", - "name": "GLM-5", - "family": "glm", + "cohere/command-r": { + "id": "cohere/command-r", + "name": "Cohere: Command R", + "family": "command-r", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1, "output": 3.2, "cache_read": 0.2, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-03-11", + "last_updated": "2024-03-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + }, + "cost": { + "input": 0.476, + "output": 1.428 + } }, - "glm-4.5-air": { - "id": "glm-4.5-air", - "name": "GLM-4.5-Air", - "family": "glm-air", + "cohere/command-r-plus-08-2024": { + "id": "cohere/command-r-plus-08-2024", + "name": "Cohere: Command R+", + "family": "command-r", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 1.1, "cache_read": 0.03, "cache_write": 0 }, - "limit": { "context": 131072, "output": 98304 } - }, - "glm-4.5v": { - "id": "glm-4.5v", - "name": "GLM-4.5V", - "family": "glm", - "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-08-11", - "last_updated": "2025-08-11", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 1.8 }, - "limit": { "context": 64000, "output": 16384 } + "structured_output": false, + "release_date": "2024-08-30", + "last_updated": "2024-08-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + }, + "cost": { + "input": 2.856, + "output": 14.246 + } }, - "glm-4.7": { - "id": "glm-4.7", - "name": "GLM-4.7", - "family": "glm", + "chutesai/Mistral-Small-3.2-24B-Instruct-2506": { + "id": "chutesai/Mistral-Small-3.2-24B-Instruct-2506", + "name": "Mistral Small 3.2 24b Instruct", + "family": "chutesai", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.2, "cache_read": 0.11, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } - } - } - }, - "xai": { - "id": "xai", - "env": ["XAI_API_KEY"], - "npm": "@ai-sdk/xai", - "name": "xAI", - "doc": "https://docs.x.ai/docs/models", - "models": { - "grok-2-vision-1212": { - "id": "grok-2-vision-1212", - "name": "Grok 2 Vision (1212)", - "family": "grok", - "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2024-08-20", - "last_updated": "2024-12-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-04-15", + "last_updated": "2025-04-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 10, "cache_read": 2 }, - "limit": { "context": 8192, "output": 4096 } + "limit": { + "context": 128000, + "input": 128000, + "output": 131072 + }, + "cost": { + "input": 0.2, + "output": 0.4 + } }, - "grok-4-fast": { - "id": "grok-4-fast", - "name": "Grok 4 Fast", - "family": "grok", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-09-19", - "last_updated": "2025-09-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "nvidia/Llama-3.1-Nemotron-Ultra-253B-v1": { + "id": "nvidia/Llama-3.1-Nemotron-Ultra-253B-v1", + "name": "Nvidia Nemotron Ultra 253B", + "family": "nemotron", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-03", + "last_updated": "2025-07-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0.4, + "output": 0.8 + } }, - "grok-2-latest": { - "id": "grok-2-latest", - "name": "Grok 2 Latest", - "family": "grok", + "nvidia/nemotron-3-nano-30b-a3b": { + "id": "nvidia/nemotron-3-nano-30b-a3b", + "name": "Nvidia Nemotron 3 Nano 30B", + "family": "nemotron", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2024-08-20", - "last_updated": "2024-12-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-15", + "last_updated": "2025-12-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 10, "cache_read": 2 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 256000, + "input": 256000, + "output": 262144 + }, + "cost": { + "input": 0.17, + "output": 0.68 + } }, - "grok-4-fast-non-reasoning": { - "id": "grok-4-fast-non-reasoning", - "name": "Grok 4 Fast (Non-Reasoning)", - "family": "grok", - "attachment": true, + "nvidia/nvidia-nemotron-nano-9b-v2": { + "id": "nvidia/nvidia-nemotron-nano-9b-v2", + "name": "Nvidia Nemotron Nano 9B v2", + "family": "nemotron", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-09-19", - "last_updated": "2025-09-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-18", + "last_updated": "2025-08-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0.17, + "output": 0.68 + } }, - "grok-4": { - "id": "grok-4", - "name": "Grok 4", - "family": "grok", + "nvidia/Llama-3.1-Nemotron-70B-Instruct-HF": { + "id": "nvidia/Llama-3.1-Nemotron-70B-Instruct-HF", + "name": "Nvidia Nemotron 70b", + "family": "nemotron", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-04-15", + "last_updated": "2025-04-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "reasoning": 15, "cache_read": 0.75 }, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + }, + "cost": { + "input": 0.357, + "output": 0.408 + } }, - "grok-code-fast-1": { - "id": "grok-code-fast-1", - "name": "Grok Code Fast 1", - "family": "grok", + "nvidia/Llama-3.3-Nemotron-Super-49B-v1": { + "id": "nvidia/Llama-3.3-Nemotron-Super-49B-v1", + "name": "Nvidia Nemotron Super 49B", + "family": "nemotron", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2025-08-28", - "last_updated": "2025-08-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-08", + "last_updated": "2025-08-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 1.5, "cache_read": 0.02 }, - "limit": { "context": 256000, "output": 10000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.15 + } }, - "grok-3-mini-fast": { - "id": "grok-3-mini-fast", - "name": "Grok 3 Mini Fast", - "family": "grok", + "nvidia/Llama-3_3-Nemotron-Super-49B-v1_5": { + "id": "nvidia/Llama-3_3-Nemotron-Super-49B-v1_5", + "name": "Nvidia Nemotron Super 49B v1.5", + "family": "nemotron", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-08", + "last_updated": "2025-08-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 4, "reasoning": 4, "cache_read": 0.15 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0.05, + "output": 0.25 + } }, - "grok-beta": { - "id": "grok-beta", - "name": "Grok Beta", - "family": "grok-beta", + "TheDrummer 2/Anubis-70B-v1": { + "id": "TheDrummer 2/Anubis-70B-v1", + "name": "Anubis 70B v1", + "family": "llama", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2024-11-01", - "last_updated": "2024-11-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-01", + "last_updated": "2024-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 15, "cache_read": 5 }, - "limit": { "context": 131072, "output": 4096 } + "limit": { + "context": 65536, + "input": 65536, + "output": 16384 + }, + "cost": { + "input": 0.31, + "output": 0.31 + } }, - "grok-2-vision-latest": { - "id": "grok-2-vision-latest", - "name": "Grok 2 Vision Latest", - "family": "grok", - "attachment": true, + "TheDrummer 2/Cydonia-24B-v4.3": { + "id": "TheDrummer 2/Cydonia-24B-v4.3", + "name": "The Drummer Cydonia 24B v4.3", + "family": "llama", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2024-08-20", - "last_updated": "2024-12-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-25", + "last_updated": "2025-12-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 10, "cache_read": 2 }, - "limit": { "context": 8192, "output": 4096 } + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + }, + "cost": { + "input": 0.1003, + "output": 0.1207 + } }, - "grok-4-1-fast-non-reasoning": { - "id": "grok-4-1-fast-non-reasoning", - "name": "Grok 4.1 Fast (Non-Reasoning)", - "family": "grok", - "attachment": true, + "TheDrummer 2/Magidonia-24B-v4.3": { + "id": "TheDrummer 2/Magidonia-24B-v4.3", + "name": "The Drummer Magidonia 24B v4.3", + "family": "llama", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-11-19", - "last_updated": "2025-11-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-25", + "last_updated": "2025-12-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + }, + "cost": { + "input": 0.1003, + "output": 0.1207 + } }, - "grok-2": { - "id": "grok-2", - "name": "Grok 2", - "family": "grok", + "TheDrummer 2/Cydonia-24B-v4": { + "id": "TheDrummer 2/Cydonia-24B-v4", + "name": "The Drummer Cydonia 24B v4", + "family": "llama", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2024-08-20", - "last_updated": "2024-08-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-22", + "last_updated": "2025-07-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 10, "cache_read": 2 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 16384, + "input": 16384, + "output": 32768 + }, + "cost": { + "input": 0.2006, + "output": 0.2414 + } }, - "grok-3-fast-latest": { - "id": "grok-3-fast-latest", - "name": "Grok 3 Fast Latest", - "family": "grok", + "TheDrummer 2/Anubis-70B-v1.1": { + "id": "TheDrummer 2/Anubis-70B-v1.1", + "name": "Anubis 70B v1.1", + "family": "llama", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-01", + "last_updated": "2024-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 1.25 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 131072, + "input": 131072, + "output": 16384 + }, + "cost": { + "input": 0.31, + "output": 0.31 + } }, - "grok-2-1212": { - "id": "grok-2-1212", - "name": "Grok 2 (1212)", - "family": "grok", + "TheDrummer 2/Rocinante-12B-v1.1": { + "id": "TheDrummer 2/Rocinante-12B-v1.1", + "name": "Rocinante 12b", + "family": "llama", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2024-12-12", - "last_updated": "2024-12-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-01", + "last_updated": "2024-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 10, "cache_read": 2 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + }, + "cost": { + "input": 0.408, + "output": 0.595 + } }, - "grok-3-mini": { - "id": "grok-3-mini", - "name": "Grok 3 Mini", - "family": "grok", + "TheDrummer 2/Cydonia-24B-v4.1": { + "id": "TheDrummer 2/Cydonia-24B-v4.1", + "name": "The Drummer Cydonia 24B v4.1", + "family": "llama", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-19", + "last_updated": "2025-08-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 0.5, "reasoning": 0.5, "cache_read": 0.075 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 16384, + "input": 16384, + "output": 32768 + }, + "cost": { + "input": 0.1003, + "output": 0.1207 + } }, - "grok-2-vision": { - "id": "grok-2-vision", - "name": "Grok 2 Vision", - "family": "grok", + "TheDrummer 2/UnslopNemo-12B-v4.1": { + "id": "TheDrummer 2/UnslopNemo-12B-v4.1", + "name": "UnslopNemo 12b v4", + "family": "llama", "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2024-08-20", - "last_updated": "2024-08-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-01", + "last_updated": "2024-07-01", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 10, "cache_read": 2 }, - "limit": { "context": 8192, "output": 4096 } + "limit": { + "context": 32768, + "input": 32768, + "output": 8192 + }, + "cost": { + "input": 0.49299999999999994, + "output": 0.49299999999999994 + } }, - "grok-3-latest": { - "id": "grok-3-latest", - "name": "Grok 3 Latest", - "family": "grok", + "TheDrummer 2/Cydonia-24B-v2": { + "id": "TheDrummer 2/Cydonia-24B-v2", + "name": "The Drummer Cydonia 24B v2", + "family": "llama", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-11", + "tool_call": false, + "structured_output": false, "release_date": "2025-02-17", "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.75 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 16384, + "input": 16384, + "output": 32768 + }, + "cost": { + "input": 0.1003, + "output": 0.1207 + } }, - "grok-4.20-multi-agent-0309": { - "id": "grok-4.20-multi-agent-0309", - "name": "Grok 4.20 Multi-Agent", - "family": "grok", + "TheDrummer 2/skyfall-36b-v2": { + "id": "TheDrummer 2/skyfall-36b-v2", + "name": "TheDrummer Skyfall 36B V2", + "family": "llama", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2026-03-09", - "last_updated": "2026-03-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": false, + "release_date": "2025-03-10", + "last_updated": "2025-03-10", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { - "input": 2, - "output": 6, - "cache_read": 0.2, - "context_over_200k": { "input": 4, "output": 12, "cache_read": 0.4 } + "limit": { + "context": 64000, + "input": 64000, + "output": 32768 }, - "limit": { "context": 2000000, "output": 30000 } + "cost": { + "input": 0.49299999999999994, + "output": 0.49299999999999994 + } }, - "grok-3-fast": { - "id": "grok-3-fast", - "name": "Grok 3 Fast", - "family": "grok", + "deepseek-ai/DeepSeek-V3.1:thinking": { + "id": "deepseek-ai/DeepSeek-V3.1:thinking", + "name": "DeepSeek V3.1 Thinking", + "family": "deepseek-thinking", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-21", + "last_updated": "2025-08-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 1.25 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + }, + "cost": { + "input": 0.2, + "output": 0.7 + } }, - "grok-3-mini-fast-latest": { - "id": "grok-3-mini-fast-latest", - "name": "Grok 3 Mini Fast Latest", - "family": "grok", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "deepseek-ai/DeepSeek-V3.1": { + "id": "deepseek-ai/DeepSeek-V3.1", + "name": "DeepSeek V3.1", + "family": "deepseek", + "attachment": true, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-26", + "last_updated": "2025-07-26", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 4, "reasoning": 4, "cache_read": 0.15 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + }, + "cost": { + "input": 0.2, + "output": 0.7 + } }, - "grok-3": { - "id": "grok-3", - "name": "Grok 3", - "family": "grok", + "deepseek-ai/DeepSeek-V3.1-Terminus:thinking": { + "id": "deepseek-ai/DeepSeek-V3.1-Terminus:thinking", + "name": "DeepSeek V3.1 Terminus (Thinking)", + "family": "deepseek-thinking", "attachment": false, "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-09-22", + "last_updated": "2025-09-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.75 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + }, + "cost": { + "input": 0.25, + "output": 0.7 + } }, - "grok-3-mini-latest": { - "id": "grok-3-mini-latest", - "name": "Grok 3 Mini Latest", - "family": "grok", + "deepseek-ai/deepseek-v3.2-exp-thinking": { + "id": "deepseek-ai/deepseek-v3.2-exp-thinking", + "name": "DeepSeek V3.2 Exp Thinking", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 0.5, "reasoning": 0.5, "cache_read": 0.075 }, - "limit": { "context": 131072, "output": 8192 } - }, - "grok-4.20-0309-reasoning": { - "id": "grok-4.20-0309-reasoning", - "name": "Grok 4.20 (Reasoning)", - "family": "grok", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2026-03-09", - "last_updated": "2026-03-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { - "input": 2, - "output": 6, - "cache_read": 0.2, - "context_over_200k": { "input": 4, "output": 12, "cache_read": 0.4 } + "tool_call": false, + "structured_output": false, + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text"], + "output": ["text"] }, - "limit": { "context": 2000000, "output": 30000 } - }, - "grok-4.20-0309-non-reasoning": { - "id": "grok-4.20-0309-non-reasoning", - "name": "Grok 4.20 (Non-Reasoning)", - "family": "grok", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2026-03-09", - "last_updated": "2026-03-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, "open_weights": false, - "cost": { - "input": 2, - "output": 6, - "cache_read": 0.2, - "context_over_200k": { "input": 4, "output": 12, "cache_read": 0.4 } + "limit": { + "context": 163840, + "input": 163840, + "output": 65536 }, - "limit": { "context": 2000000, "output": 30000 } + "cost": { + "input": 0.27999999999999997, + "output": 0.42000000000000004 + } }, - "grok-vision-beta": { - "id": "grok-vision-beta", - "name": "Grok Vision Beta", - "family": "grok-vision", - "attachment": true, + "deepseek-ai/deepseek-v3.2-exp": { + "id": "deepseek-ai/deepseek-v3.2-exp", + "name": "DeepSeek V3.2 Exp", + "family": "deepseek", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2024-11-01", - "last_updated": "2024-11-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 15, "cache_read": 5 }, - "limit": { "context": 8192, "output": 4096 } + "limit": { + "context": 163840, + "input": 163840, + "output": 65536 + }, + "cost": { + "input": 0.27999999999999997, + "output": 0.42000000000000004 + } }, - "grok-4-1-fast": { - "id": "grok-4-1-fast", - "name": "Grok 4.1 Fast", - "family": "grok", - "attachment": true, + "deepseek-ai/DeepSeek-R1-0528": { + "id": "deepseek-ai/DeepSeek-R1-0528", + "name": "DeepSeek R1 0528", + "family": "deepseek", + "attachment": false, "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-11-19", - "last_updated": "2025-11-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } - } - } - }, - "poe": { - "id": "poe", - "env": ["POE_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.poe.com/v1", - "name": "Poe", - "doc": "https://creator.poe.com/docs/external-applications/openai-compatible-api", - "models": { - "runwayml/runway": { - "id": "runwayml/runway", - "name": "Runway", - "family": "runway", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2024-10-11", - "last_updated": "2024-10-11", - "modalities": { "input": ["text", "image"], "output": ["video"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-05-28", + "last_updated": "2025-05-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 256, "output": 0 } + "limit": { + "context": 128000, + "input": 128000, + "output": 163840 + }, + "cost": { + "input": 0.4, + "output": 1.7 + } }, - "runwayml/runway-gen-4-turbo": { - "id": "runwayml/runway-gen-4-turbo", - "name": "Runway-Gen-4-Turbo", - "family": "runway", - "attachment": true, + "deepseek-ai/DeepSeek-V3.1-Terminus": { + "id": "deepseek-ai/DeepSeek-V3.1-Terminus", + "name": "DeepSeek V3.1 Terminus", + "family": "deepseek", + "attachment": false, "reasoning": false, "tool_call": true, - "temperature": false, - "release_date": "2025-05-09", - "last_updated": "2025-05-09", - "modalities": { "input": ["text", "image"], "output": ["video"] }, - "open_weights": false, - "limit": { "context": 256, "output": 0 } - }, - "openai/gpt-5.2-codex": { - "id": "openai/gpt-5.2-codex", - "name": "GPT-5.2-Codex", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2026-01-14", - "last_updated": "2026-01-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.6, "output": 13, "cache_read": 0.16 }, - "limit": { "context": 400000, "output": 128000 } - }, - "openai/o1-pro": { - "id": "openai/o1-pro", - "name": "o1-pro", - "family": "o-pro", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-03-19", - "last_updated": "2025-03-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-08-02", + "last_updated": "2025-08-02", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 140, "output": 540 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + }, + "cost": { + "input": 0.25, + "output": 0.7 + } }, - "openai/gpt-5.1-codex-mini": { - "id": "openai/gpt-5.1-codex-mini", - "name": "GPT-5.1-Codex-Mini", + "openai/gpt-5.1-codex-max": { + "id": "openai/gpt-5.1-codex-max", + "name": "GPT 5.1 Codex Max", "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-11-12", - "last_updated": "2025-11-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.22, "output": 1.8, "cache_read": 0.022 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 400000, + "input": 400000, + "output": 128000 + }, + "cost": { + "input": 2.5, + "output": 20 + } }, - "openai/gpt-5.4-pro": { - "id": "openai/gpt-5.4-pro", - "name": "GPT-5.4-Pro", + "openai/gpt-5.2-chat": { + "id": "openai/gpt-5.2-chat", + "name": "GPT 5.2 Chat", + "family": "gpt", "attachment": true, "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { "input": ["text", "image"], "output": ["image"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2026-01-01", + "last_updated": "2026-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 27, "output": 160 }, - "limit": { "context": 1050000, "input": 922000, "output": 128000 } + "limit": { + "context": 400000, + "input": 400000, + "output": 16384 + }, + "cost": { + "input": 1.75, + "output": 14 + } }, - "openai/sora-2": { - "id": "openai/sora-2", - "name": "Sora-2", - "family": "sora", - "attachment": true, + "openai/gpt-4o-mini-search-preview": { + "id": "openai/gpt-4o-mini-search-preview", + "name": "GPT-4o mini Search Preview", + "family": "gpt-mini", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-10-06", - "last_updated": "2025-10-06", - "modalities": { "input": ["text", "image"], "output": ["video"] }, - "open_weights": false, - "limit": { "context": 0, "output": 0 } - }, - "openai/o3-mini": { - "id": "openai/o3-mini", - "name": "o3-mini", - "family": "o-mini", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-01-31", - "last_updated": "2025-01-31", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.99, "output": 4 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0.088, + "output": 0.35 + } }, - "openai/gpt-5.4-mini": { - "id": "openai/gpt-5.4-mini", - "name": "GPT-5.4-Mini", + "openai/chatgpt-4o-latest": { + "id": "openai/chatgpt-4o-latest", + "name": "ChatGPT 4o", + "family": "gpt", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "temperature": false, - "release_date": "2026-03-12", - "last_updated": "2026-03-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "release_date": "2024-05-13", + "last_updated": "2024-05-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.68, "output": 4, "cache_read": 0.068 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 4.998, + "output": 14.993999999999998 + } }, - "openai/gpt-5-pro": { - "id": "openai/gpt-5-pro", - "name": "GPT-5-Pro", + "openai/gpt-5.2-pro": { + "id": "openai/gpt-5.2-pro", + "name": "GPT 5.2 Pro", "family": "gpt-pro", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-10-06", - "last_updated": "2025-10-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "release_date": "2026-01-01", + "last_updated": "2026-01-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 14, "output": 110 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 400000, + "input": 400000, + "output": 128000 + }, + "cost": { + "input": 21, + "output": 168 + } }, - "openai/gpt-5": { - "id": "openai/gpt-5", - "name": "GPT-5", - "family": "gpt", + "openai/gpt-5-mini": { + "id": "openai/gpt-5-mini", + "name": "GPT 5 Mini", + "family": "gpt-mini", "attachment": true, "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 9, "cache_read": 0.11 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 400000, + "input": 400000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2 + } }, - "openai/chatgpt-4o-latest": { - "id": "openai/chatgpt-4o-latest", - "name": "ChatGPT-4o-Latest", - "family": "gpt", + "openai/gpt-5-nano": { + "id": "openai/gpt-5-nano", + "name": "GPT 5 Nano", + "family": "gpt-nano", "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2024-08-14", - "last_updated": "2024-08-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "reasoning": true, + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 4.5, "output": 14 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 400000, + "input": 400000, + "output": 128000 + }, + "cost": { + "input": 0.05, + "output": 0.4 + } }, "openai/gpt-4-turbo": { "id": "openai/gpt-4-turbo", - "name": "GPT-4-Turbo", + "name": "GPT-4 Turbo", "family": "gpt", "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2023-09-13", - "last_updated": "2023-09-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2023-11-06", + "last_updated": "2024-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 9, "output": 27 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + }, + "cost": { + "input": 10, + "output": 30 + } }, - "openai/gpt-4o": { - "id": "openai/gpt-4o", - "name": "GPT-4o", + "openai/gpt-5.2": { + "id": "openai/gpt-5.2", + "name": "GPT 5.2", "family": "gpt", "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2024-05-13", - "last_updated": "2024-05-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "limit": { "context": 128000, "output": 8192 } - }, - "openai/gpt-5.3-codex": { - "id": "openai/gpt-5.3-codex", - "name": "GPT-5.3-Codex", - "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2026-02-10", - "last_updated": "2026-02-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "release_date": "2026-01-01", + "last_updated": "2026-01-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.6, "output": 13, "cache_read": 0.16 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 400000, + "input": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14 + } }, "openai/o3-mini-high": { "id": "openai/o3-mini-high", - "name": "o3-mini-high", + "name": "OpenAI o3-mini (High)", "family": "o-mini", - "attachment": true, + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, + "structured_output": true, "release_date": "2025-01-31", "last_updated": "2025-01-31", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.99, "output": 4 }, - "limit": { "context": 200000, "output": 100000 } - }, - "openai/gpt-5-mini": { - "id": "openai/gpt-5-mini", - "name": "GPT-5-mini", - "family": "gpt-mini", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-06-25", - "last_updated": "2025-06-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.22, "output": 1.8, "cache_read": 0.022 }, - "limit": { "context": 400000, "output": 128000 } - }, - "openai/gpt-image-1.5": { - "id": "openai/gpt-image-1.5", - "name": "gpt-image-1.5", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-12-16", - "last_updated": "2025-12-16", - "modalities": { "input": ["text", "image"], "output": ["image"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 0 } + "limit": { + "context": 200000, + "input": 200000, + "output": 100000 + }, + "cost": { + "input": 0.64, + "output": 2.588 + } }, "openai/gpt-4o-mini": { "id": "openai/gpt-4o-mini", - "name": "GPT-4o-mini", + "name": "GPT-4o mini", "family": "gpt-mini", "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": false, + "tool_call": false, + "structured_output": false, "release_date": "2024-07-18", "last_updated": "2024-07-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.14, "output": 0.54, "cache_read": 0.068 }, - "limit": { "context": 124096, "output": 4096 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0.1496, + "output": 0.595 + } }, - "openai/gpt-image-1-mini": { - "id": "openai/gpt-image-1-mini", - "name": "GPT-Image-1-Mini", + "openai/o4-mini-deep-research": { + "id": "openai/o4-mini-deep-research", + "name": "OpenAI o4-mini Deep Research", + "family": "o-mini", + "attachment": false, + "reasoning": true, + "tool_call": false, + "structured_output": false, + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "input": 200000, + "output": 100000 + }, + "cost": { + "input": 9.996, + "output": 19.992 + } + }, + "openai/gpt-5.1-chat": { + "id": "openai/gpt-5.1-chat", + "name": "GPT 5.1 Chat", "family": "gpt", "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-08-26", - "last_updated": "2025-08-26", - "modalities": { "input": ["text", "image"], "output": ["image"] }, + "reasoning": true, + "tool_call": false, + "structured_output": false, + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 0, "output": 0 } + "limit": { + "context": 400000, + "input": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, - "openai/gpt-5.1-codex-max": { - "id": "openai/gpt-5.1-codex-max", - "name": "GPT 5.1 Codex Max", - "attachment": true, + "openai/o4-mini": { + "id": "openai/o4-mini", + "name": "OpenAI o4-mini", + "family": "o-mini", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-12-08", - "last_updated": "2025-12-08", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 9, "cache_read": 0.11 }, - "limit": { "context": 400000, "output": 128000 } - }, - "openai/gpt-4.1": { - "id": "openai/gpt-4.1", - "name": "GPT-4.1", - "family": "gpt", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.8, "output": 7.2, "cache_read": 0.45 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 200000, + "input": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4 + } }, - "openai/gpt-3.5-turbo": { - "id": "openai/gpt-3.5-turbo", - "name": "GPT-3.5-Turbo", - "family": "gpt", + "openai/gpt-5.2-codex": { + "id": "openai/gpt-5.2-codex", + "name": "GPT 5.2 Codex", + "family": "gpt-codex", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2023-09-13", - "last_updated": "2023-09-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "release_date": "2026-01-14", + "last_updated": "2026-01-14", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.45, "output": 1.4 }, - "limit": { "context": 16384, "output": 2048 } + "limit": { + "context": 400000, + "input": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14 + } }, - "openai/gpt-3.5-turbo-instruct": { - "id": "openai/gpt-3.5-turbo-instruct", - "name": "GPT-3.5-Turbo-Instruct", - "family": "gpt", + "openai/gpt-5.1-codex-mini": { + "id": "openai/gpt-5.1-codex-mini", + "name": "GPT 5.1 Codex Mini", + "family": "gpt-codex-mini", "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2023-09-20", - "last_updated": "2023-09-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "reasoning": true, + "tool_call": false, + "structured_output": false, + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.4, "output": 1.8 }, - "limit": { "context": 3500, "output": 1024 } + "limit": { + "context": 400000, + "input": 400000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2 + } }, - "openai/dall-e-3": { - "id": "openai/dall-e-3", - "name": "DALL-E-3", - "family": "dall-e", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2023-11-06", - "last_updated": "2023-11-06", - "modalities": { "input": ["text"], "output": ["image"] }, + "openai/o1-preview": { + "id": "openai/o1-preview", + "name": "OpenAI o1-preview", + "family": "o", + "attachment": false, + "reasoning": true, + "tool_call": false, + "structured_output": false, + "release_date": "2024-09-12", + "last_updated": "2024-09-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 800, "output": 0 } + "limit": { + "context": 128000, + "input": 128000, + "output": 32768 + }, + "cost": { + "input": 14.993999999999998, + "output": 59.993 + } }, - "openai/gpt-4-classic": { - "id": "openai/gpt-4-classic", - "name": "GPT-4-Classic", + "openai/gpt-4o-2024-08-06": { + "id": "openai/gpt-4o-2024-08-06", + "name": "GPT-4o (2024-08-06)", "family": "gpt", "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2024-03-25", - "last_updated": "2024-03-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-08-06", + "last_updated": "2024-08-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 27, "output": 54 }, - "limit": { "context": 8192, "output": 4096 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 2.499, + "output": 9.996 + } }, - "openai/gpt-5.4": { - "id": "openai/gpt-5.4", - "name": "GPT-5.4", + "openai/gpt-5.1": { + "id": "openai/gpt-5.1", + "name": "GPT 5.1", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2026-02-26", - "last_updated": "2026-02-26", - "modalities": { "input": ["text", "image", "pdf"], "output": ["image"] }, + "structured_output": true, + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.2, "output": 14, "cache_read": 0.22 }, - "limit": { "context": 1050000, "input": 922000, "output": 128000 } + "limit": { + "context": 400000, + "input": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, "openai/o1": { "id": "openai/o1", - "name": "o1", + "name": "OpenAI o1", "family": "o", - "attachment": true, + "attachment": false, "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2024-12-18", - "last_updated": "2024-12-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-17", + "last_updated": "2024-12-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 14, "output": 54 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 200000, + "input": 200000, + "output": 100000 + }, + "cost": { + "input": 14.993999999999998, + "output": 59.993 + } }, - "openai/gpt-4o-aug": { - "id": "openai/gpt-4o-aug", - "name": "GPT-4o-Aug", + "openai/gpt-3.5-turbo": { + "id": "openai/gpt-3.5-turbo", + "name": "GPT-3.5 Turbo", "family": "gpt", - "attachment": true, + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2024-11-21", - "last_updated": "2024-11-21", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2022-11-30", + "last_updated": "2024-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.2, "output": 9, "cache_read": 1.1 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 16385, + "input": 16385, + "output": 4096 + }, + "cost": { + "input": 0.5, + "output": 1.5 + } }, - "openai/o3": { - "id": "openai/o3", - "name": "o3", + "openai/o3-deep-research": { + "id": "openai/o3-deep-research", + "name": "OpenAI o3 Deep Research", "family": "o", - "attachment": true, + "attachment": false, "reasoning": true, - "tool_call": true, - "temperature": false, + "tool_call": false, + "structured_output": false, "release_date": "2025-04-16", "last_updated": "2025-04-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.8, "output": 7.2, "cache_read": 0.45 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 200000, + "input": 200000, + "output": 100000 + }, + "cost": { + "input": 9.996, + "output": 19.992 + } }, - "openai/gpt-5-chat": { - "id": "openai/gpt-5-chat", - "name": "GPT-5-Chat", - "family": "gpt-codex", - "attachment": true, - "reasoning": false, + "openai/o3-mini": { + "id": "openai/o3-mini", + "name": "OpenAI o3-mini", + "family": "o-mini", + "attachment": false, + "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-01-31", + "last_updated": "2025-01-31", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 9, "cache_read": 0.11 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 200000, + "input": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4 + } }, - "openai/gpt-4o-mini-search": { - "id": "openai/gpt-4o-mini-search", - "name": "GPT-4o-mini-Search", - "family": "gpt-mini", - "attachment": true, + "openai/gpt-4-turbo-preview": { + "id": "openai/gpt-4-turbo-preview", + "name": "GPT-4 Turbo Preview", + "family": "gpt", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-03-11", - "last_updated": "2025-03-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2023-11-06", + "last_updated": "2024-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.14, "output": 0.54 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + }, + "cost": { + "input": 9.996, + "output": 30.004999999999995 + } }, - "openai/gpt-3.5-turbo-raw": { - "id": "openai/gpt-3.5-turbo-raw", - "name": "GPT-3.5-Turbo-Raw", - "family": "gpt", + "openai/o1-pro": { + "id": "openai/o1-pro", + "name": "OpenAI o1 Pro", + "family": "o-pro", "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2023-09-27", - "last_updated": "2023-09-27", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-01-25", + "last_updated": "2025-01-25", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.45, "output": 1.4 }, - "limit": { "context": 4524, "output": 2048 } + "limit": { + "context": 200000, + "input": 200000, + "output": 100000 + }, + "cost": { + "input": 150, + "output": 600 + } }, - "openai/gpt-5.4-nano": { - "id": "openai/gpt-5.4-nano", - "name": "GPT-5.4-Nano", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2026-03-11", - "last_updated": "2026-03-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "openai/gpt-5-codex": { + "id": "openai/gpt-5-codex", + "name": "GPT-5 Codex", + "family": "gpt-codex", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-09-15", + "last_updated": "2025-09-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.18, "output": 1.1, "cache_read": 0.018 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 256000, + "input": 256000, + "output": 32768 + }, + "cost": { + "input": 9.996, + "output": 19.992 + } }, - "openai/gpt-5.2": { - "id": "openai/gpt-5.2", - "name": "GPT-5.2", + "openai/gpt-5.1-chat-latest": { + "id": "openai/gpt-5.1-chat-latest", + "name": "GPT 5.1 Chat (Latest)", + "family": "gpt", "attachment": true, "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-12-08", - "last_updated": "2025-12-08", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.6, "output": 13, "cache_read": 0.16 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 400000, + "input": 400000, + "output": 16384 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, - "openai/o4-mini-deep-research": { - "id": "openai/o4-mini-deep-research", - "name": "o4-mini-deep-research", - "family": "o-mini", + "openai/gpt-4o-search-preview": { + "id": "openai/gpt-4o-search-preview", + "name": "GPT-4o Search Preview", + "family": "gpt", "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-06-27", - "last_updated": "2025-06-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-05-13", + "last_updated": "2024-05-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.8, "output": 7.2, "cache_read": 0.45 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 1.47, + "output": 5.88 + } }, - "openai/gpt-5.1": { - "id": "openai/gpt-5.1", - "name": "GPT-5.1", - "family": "gpt", + "openai/gpt-4.1-nano": { + "id": "openai/gpt-4.1-nano", + "name": "GPT 4.1 Nano", + "family": "gpt-nano", "attachment": true, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1047576, + "input": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } + }, + "openai/o4-mini-high": { + "id": "openai/o4-mini-high", + "name": "OpenAI o4-mini high", + "family": "o-mini", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-11-12", - "last_updated": "2025-11-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 9, "cache_read": 0.11 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 200000, + "input": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4 + } }, - "openai/gpt-4.1-mini": { - "id": "openai/gpt-4.1-mini", - "name": "GPT-4.1-mini", - "family": "gpt-mini", - "attachment": true, + "openai/o3": { + "id": "openai/o3", + "name": "OpenAI o3", + "family": "o", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-04-15", - "last_updated": "2025-04-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.36, "output": 1.4, "cache_read": 0.09 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 200000, + "input": 200000, + "output": 100000 + }, + "cost": { + "input": 2, + "output": 8 + } }, - "openai/gpt-5-nano": { - "id": "openai/gpt-5-nano", - "name": "GPT-5-nano", - "family": "gpt-nano", - "attachment": true, + "openai/gpt-oss-20b": { + "id": "openai/gpt-oss-20b", + "name": "GPT OSS 20B", + "family": "gpt-oss", + "attachment": false, "reasoning": true, - "tool_call": true, - "temperature": false, + "tool_call": false, + "structured_output": false, "release_date": "2025-08-05", "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.045, "output": 0.36, "cache_read": 0.0045 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0.04, + "output": 0.15 + } }, - "openai/gpt-5.1-instant": { - "id": "openai/gpt-5.1-instant", - "name": "GPT-5.1-Instant", - "family": "gpt", + "openai/gpt-5-pro": { + "id": "openai/gpt-5-pro", + "name": "GPT 5 Pro", + "family": "gpt-pro", "attachment": true, "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-11-12", - "last_updated": "2025-11-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.1, "output": 9, "cache_read": 0.11 }, - "limit": { "context": 128000, "output": 16384 } - }, - "openai/gpt-5.2-instant": { - "id": "openai/gpt-5.2-instant", - "name": "GPT-5.2-Instant", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.6, "output": 13, "cache_read": 0.16 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 400000, + "input": 400000, + "output": 128000 + }, + "cost": { + "input": 15, + "output": 120 + } }, - "openai/gpt-4-classic-0314": { - "id": "openai/gpt-4-classic-0314", - "name": "GPT-4-Classic-0314", + "openai/gpt-5.1-2025-11-13": { + "id": "openai/gpt-5.1-2025-11-13", + "name": "GPT-5.1 (2025-11-13)", "family": "gpt", - "attachment": true, + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2024-08-26", - "last_updated": "2024-08-26", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 27, "output": 54 }, - "limit": { "context": 8192, "output": 4096 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 32768 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, - "openai/gpt-4o-search": { - "id": "openai/gpt-4o-search", - "name": "GPT-4o-Search", + "openai/gpt-4o": { + "id": "openai/gpt-4o", + "name": "GPT-4o", "family": "gpt", "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-03-11", - "last_updated": "2025-03-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-05-13", + "last_updated": "2024-05-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.2, "output": 9 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 2.499, + "output": 9.996 + } }, - "openai/gpt-image-1": { - "id": "openai/gpt-image-1", - "name": "GPT-Image-1", - "family": "gpt", - "attachment": true, - "reasoning": false, + "openai/o3-mini-low": { + "id": "openai/o3-mini-low", + "name": "OpenAI o3-mini (Low)", + "family": "o-mini", + "attachment": false, + "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-03-31", - "last_updated": "2025-03-31", - "modalities": { "input": ["text", "image"], "output": ["image"] }, + "structured_output": true, + "release_date": "2025-01-31", + "last_updated": "2025-01-31", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 0 } + "limit": { + "context": 200000, + "input": 200000, + "output": 100000 + }, + "cost": { + "input": 9.996, + "output": 19.992 + } }, - "openai/o3-pro": { - "id": "openai/o3-pro", - "name": "o3-pro", - "family": "o-pro", + "openai/gpt-5": { + "id": "openai/gpt-5", + "name": "GPT 5", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-06-10", - "last_updated": "2025-06-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 18, "output": 72 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 400000, + "input": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, - "openai/gpt-4.1-nano": { - "id": "openai/gpt-4.1-nano", - "name": "GPT-4.1-nano", - "family": "gpt-nano", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-04-15", - "last_updated": "2025-04-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "openai/gpt-oss-safeguard-20b": { + "id": "openai/gpt-oss-safeguard-20b", + "name": "GPT OSS Safeguard 20B", + "family": "gpt-oss", + "attachment": false, + "reasoning": true, + "tool_call": false, + "structured_output": false, + "release_date": "2025-10-29", + "last_updated": "2025-10-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.09, "output": 0.36, "cache_read": 0.022 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0.075, + "output": 0.3 + } }, - "openai/gpt-5.3-instant": { - "id": "openai/gpt-5.3-instant", - "name": "GPT-5.3-Instant", - "attachment": true, - "reasoning": false, + "openai/o3-pro-2025-06-10": { + "id": "openai/o3-pro-2025-06-10", + "name": "OpenAI o3-pro (2025-06-10)", + "family": "o-pro", + "attachment": false, + "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2026-03-03", - "last_updated": "2026-03-03", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-06-10", + "last_updated": "2025-06-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.6, "output": 13, "cache_read": 0.16 }, - "limit": { "context": 128000, "input": 111616, "output": 16384 } + "limit": { + "context": 200000, + "input": 200000, + "output": 100000 + }, + "cost": { + "input": 9.996, + "output": 19.992 + } }, - "openai/gpt-5-codex": { - "id": "openai/gpt-5-codex", - "name": "GPT-5-Codex", - "family": "gpt-codex", - "attachment": true, + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "name": "GPT OSS 120B", + "family": "gpt-oss", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-09-23", - "last_updated": "2025-09-23", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 9 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0.05, + "output": 0.25 + } }, - "openai/o4-mini": { - "id": "openai/o4-mini", - "name": "o4-mini", - "family": "o-mini", + "openai/gpt-5-chat-latest": { + "id": "openai/gpt-5-chat-latest", + "name": "GPT 5 Chat", + "family": "gpt", "attachment": true, "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.99, "output": 4, "cache_read": 0.25 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 400000, + "input": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, - "openai/o3-deep-research": { - "id": "openai/o3-deep-research", - "name": "o3-deep-research", - "family": "o", + "openai/gpt-4.1": { + "id": "openai/gpt-4.1", + "name": "GPT 4.1", + "family": "gpt", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "temperature": false, - "release_date": "2025-06-27", - "last_updated": "2025-06-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-09-10", + "last_updated": "2025-09-10", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1047576, + "input": 1047576, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 8 + } + }, + "openai/gpt-4.1-mini": { + "id": "openai/gpt-4.1-mini", + "name": "GPT 4.1 Mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 9, "output": 36, "cache_read": 2.2 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 1047576, + "input": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 1.6 + } }, "openai/gpt-5.1-codex": { "id": "openai/gpt-5.1-codex", - "name": "GPT-5.1-Codex", + "name": "GPT 5.1 Codex", "family": "gpt-codex", "attachment": true, "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-11-12", - "last_updated": "2025-11-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 9, "cache_read": 0.11 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 400000, + "input": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, - "openai/sora-2-pro": { - "id": "openai/sora-2-pro", - "name": "Sora-2-Pro", - "family": "sora", + "openai/gpt-4o-2024-11-20": { + "id": "openai/gpt-4o-2024-11-20", + "name": "GPT-4o (2024-11-20)", + "family": "gpt", "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-10-06", - "last_updated": "2025-10-06", - "modalities": { "input": ["text", "image"], "output": ["video"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-11-20", + "last_updated": "2024-11-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 0, "output": 0 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10 + } }, - "openai/gpt-5.2-pro": { - "id": "openai/gpt-5.2-pro", - "name": "GPT-5.2-Pro", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "VongolaChouko/Starcannon-Unleashed-12B-v1.0": { + "id": "VongolaChouko/Starcannon-Unleashed-12B-v1.0", + "name": "Mistral Nemo Starcannon 12b v1", + "family": "mistral-nemo", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-01", + "last_updated": "2024-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 19, "output": 150 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + }, + "cost": { + "input": 0.49299999999999994, + "output": 0.49299999999999994 + } }, - "anthropic/claude-opus-4.6": { - "id": "anthropic/claude-opus-4.6", - "name": "Claude-Opus-4.6", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2026-02-04", - "last_updated": "2026-02-04", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "amazon/nova-lite-v1": { + "id": "amazon/nova-lite-v1", + "name": "Amazon Nova Lite 1.0", + "family": "nova-lite", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-03", + "last_updated": "2024-12-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 4.3, "output": 21, "cache_read": 0.43, "cache_write": 5.3 }, - "limit": { "context": 983040, "output": 128000 } + "limit": { + "context": 300000, + "input": 300000, + "output": 5120 + }, + "cost": { + "input": 0.0595, + "output": 0.238 + } }, - "anthropic/claude-haiku-4.5": { - "id": "anthropic/claude-haiku-4.5", - "name": "Claude-Haiku-4.5", - "family": "claude-haiku", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "amazon/nova-pro-v1": { + "id": "amazon/nova-pro-v1", + "name": "Amazon Nova Pro 1.0", + "family": "nova-pro", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-03", + "last_updated": "2024-12-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.85, "output": 4.3, "cache_read": 0.085, "cache_write": 1.1 }, - "limit": { "context": 192000, "output": 64000 } + "limit": { + "context": 300000, + "input": 300000, + "output": 32000 + }, + "cost": { + "input": 0.7989999999999999, + "output": 3.1959999999999997 + } }, - "anthropic/claude-haiku-3.5": { - "id": "anthropic/claude-haiku-3.5", - "name": "Claude-Haiku-3.5", - "family": "claude-haiku", - "attachment": true, + "amazon/nova-2-lite-v1": { + "id": "amazon/nova-2-lite-v1", + "name": "Amazon Nova 2 Lite", + "family": "nova", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2024-10-01", - "last_updated": "2024-10-01", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-03", + "last_updated": "2024-12-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.68, "output": 3.4, "cache_read": 0.068, "cache_write": 0.85 }, - "limit": { "context": 189096, "output": 8192 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 65535 + }, + "cost": { + "input": 0.5099999999999999, + "output": 4.25 + } }, - "anthropic/claude-haiku-3": { - "id": "anthropic/claude-haiku-3", - "name": "Claude-Haiku-3", - "family": "claude-haiku", - "attachment": true, + "amazon/nova-micro-v1": { + "id": "amazon/nova-micro-v1", + "name": "Amazon Nova Micro 1.0", + "family": "nova-micro", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2024-03-09", - "last_updated": "2024-03-09", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-03", + "last_updated": "2024-12-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.21, "output": 1.1, "cache_read": 0.021, "cache_write": 0.26 }, - "limit": { "context": 189096, "output": 8192 } + "limit": { + "context": 128000, + "input": 128000, + "output": 5120 + }, + "cost": { + "input": 0.0357, + "output": 0.1394 + } }, - "anthropic/claude-sonnet-3.7": { - "id": "anthropic/claude-sonnet-3.7", - "name": "Claude-Sonnet-3.7", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "Sao10K/L3.3-70B-Euryale-v2.3": { + "id": "Sao10K/L3.3-70B-Euryale-v2.3", + "name": "Llama 3.3 70B Euryale", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.6, "output": 13, "cache_read": 0.26, "cache_write": 3.2 }, - "limit": { "context": 196608, "output": 128000 } + "limit": { + "context": 20480, + "input": 20480, + "output": 16384 + }, + "cost": { + "input": 0.49299999999999994, + "output": 0.49299999999999994 + } }, - "anthropic/claude-opus-4.1": { - "id": "anthropic/claude-opus-4.1", - "name": "Claude-Opus-4.1", - "family": "claude-opus", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "Sao10K/L3.1-70B-Euryale-v2.2": { + "id": "Sao10K/L3.1-70B-Euryale-v2.2", + "name": "Llama 3.1 70B Euryale", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 13, "output": 64, "cache_read": 1.3, "cache_write": 16 }, - "limit": { "context": 196608, "output": 32000 } + "limit": { + "context": 20480, + "input": 20480, + "output": 16384 + }, + "cost": { + "input": 0.306, + "output": 0.357 + } }, - "anthropic/claude-sonnet-4.6": { - "id": "anthropic/claude-sonnet-4.6", - "name": "Claude-Sonnet-4.6", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "Sao10K/L3.1-70B-Hanami-x1": { + "id": "Sao10K/L3.1-70B-Hanami-x1", + "name": "Llama 3.1 70B Hanami", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.6, "output": 13, "cache_read": 0.26, "cache_write": 3.2 }, - "limit": { "context": 983040, "output": 128000 } + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + }, + "cost": { + "input": 0.49299999999999994, + "output": 0.49299999999999994 + } }, - "anthropic/claude-sonnet-4": { - "id": "anthropic/claude-sonnet-4", - "name": "Claude-Sonnet-4", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-05-21", - "last_updated": "2025-05-21", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "Sao10K/L3-8B-Stheno-v3.2": { + "id": "Sao10K/L3-8B-Stheno-v3.2", + "name": "Sao10K Stheno 8b", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-11-29", + "last_updated": "2024-11-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.6, "output": 13, "cache_read": 0.26, "cache_write": 3.2 }, - "limit": { "context": 983040, "output": 64000 } + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + }, + "cost": { + "input": 0.2006, + "output": 0.2006 + } }, - "anthropic/claude-sonnet-3.5-june": { - "id": "anthropic/claude-sonnet-3.5-june", - "name": "Claude-Sonnet-3.5-June", - "family": "claude-sonnet", - "attachment": true, + "LatitudeGames/Wayfarer-Large-70B-Llama-3.3": { + "id": "LatitudeGames/Wayfarer-Large-70B-Llama-3.3", + "name": "Llama 3.3 70B Wayfarer", + "family": "llama", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2024-11-18", - "last_updated": "2024-11-18", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-02-20", + "last_updated": "2025-02-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.6, "output": 13, "cache_read": 0.26, "cache_write": 3.2 }, - "limit": { "context": 189096, "output": 8192 } + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + }, + "cost": { + "input": 0.700000007, + "output": 0.700000007 + } }, - "anthropic/claude-opus-4.5": { - "id": "anthropic/claude-opus-4.5", - "name": "Claude-Opus-4.5", - "family": "claude-opus", - "attachment": true, + "z-ai/glm-4.6:thinking": { + "id": "z-ai/glm-4.6:thinking", + "name": "GLM 4.6 Thinking", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-11-21", - "last_updated": "2025-11-21", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 4.3, "output": 21, "cache_read": 0.43, "cache_write": 5.3 }, - "limit": { "context": 196608, "output": 64000 } + "limit": { + "context": 200000, + "input": 200000, + "output": 65535 + }, + "cost": { + "input": 0.4, + "output": 1.5 + } }, - "anthropic/claude-sonnet-3.5": { - "id": "anthropic/claude-sonnet-3.5", - "name": "Claude-Sonnet-3.5", - "family": "claude-sonnet", + "z-ai/glm-4.5v": { + "id": "z-ai/glm-4.5v", + "name": "GLM 4.5V", + "family": "glmv", "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2024-06-05", - "last_updated": "2024-06-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "reasoning": true, + "tool_call": false, + "structured_output": false, + "release_date": "2025-11-22", + "last_updated": "2025-11-22", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.6, "output": 13, "cache_read": 0.26, "cache_write": 3.2 }, - "limit": { "context": 189096, "output": 8192 } + "limit": { + "context": 64000, + "input": 64000, + "output": 96000 + }, + "cost": { + "input": 0.6, + "output": 1.7999999999999998 + } }, - "anthropic/claude-sonnet-4.5": { - "id": "anthropic/claude-sonnet-4.5", - "name": "Claude-Sonnet-4.5", - "family": "claude-sonnet", - "attachment": true, + "z-ai/glm-4.6": { + "id": "z-ai/glm-4.6", + "name": "GLM 4.6", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-09-26", - "last_updated": "2025-09-26", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.6, "output": 13, "cache_read": 0.26, "cache_write": 3.2 }, - "limit": { "context": 983040, "output": 32768 } + "limit": { + "context": 200000, + "input": 200000, + "output": 65535 + }, + "cost": { + "input": 0.4, + "output": 1.5 + } }, - "anthropic/claude-opus-4": { - "id": "anthropic/claude-opus-4", - "name": "Claude-Opus-4", - "family": "claude-opus", + "z-ai/glm-4.5v:thinking": { + "id": "z-ai/glm-4.5v:thinking", + "name": "GLM 4.5V Thinking", + "family": "glmv", "attachment": true, "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-05-21", - "last_updated": "2025-05-21", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-11-22", + "last_updated": "2025-11-22", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 13, "output": 64, "cache_read": 1.3, "cache_write": 16 }, - "limit": { "context": 192512, "output": 28672 } + "limit": { + "context": 64000, + "input": 64000, + "output": 96000 + }, + "cost": { + "input": 0.6, + "output": 1.7999999999999998 + } }, - "xai/grok-4-fast-non-reasoning": { - "id": "xai/grok-4-fast-non-reasoning", - "name": "Grok-4-Fast-Non-Reasoning", - "family": "grok", + "baidu/ernie-4.5-vl-28b-a3b": { + "id": "baidu/ernie-4.5-vl-28b-a3b", + "name": "ERNIE 4.5 VL 28B", + "family": "ernie", "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-09-16", - "last_updated": "2025-09-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 128000 } - }, - "xai/grok-4": { - "id": "xai/grok-4", - "name": "Grok-4", - "family": "grok", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-07-10", - "last_updated": "2025-07-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-06-30", + "last_updated": "2025-06-30", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.75 }, - "limit": { "context": 256000, "output": 128000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.13999999999999999, + "output": 0.5599999999999999 + } }, - "xai/grok-code-fast-1": { - "id": "xai/grok-code-fast-1", - "name": "Grok Code Fast 1", - "family": "grok", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-08-22", - "last_updated": "2025-08-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "baidu/ernie-4.5-300b-a47b": { + "id": "baidu/ernie-4.5-300b-a47b", + "name": "ERNIE 4.5 300B", + "family": "ernie", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-06-30", + "last_updated": "2025-06-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 1.5, "cache_read": 0.02 }, - "limit": { "context": 256000, "output": 128000 } + "limit": { + "context": 131072, + "input": 131072, + "output": 16384 + }, + "cost": { + "input": 0.35, + "output": 1.15 + } }, - "xai/grok-4.1-fast-non-reasoning": { - "id": "xai/grok-4.1-fast-non-reasoning", - "name": "Grok-4.1-Fast-Non-Reasoning", - "family": "grok", - "attachment": true, + "dmind/dmind-1": { + "id": "dmind/dmind-1", + "name": "DMind-1", + "family": "gpt", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-11-19", - "last_updated": "2025-11-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-06-01", + "last_updated": "2025-06-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 2000000, "output": 30000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 0.6 + } }, - "xai/grok-4.1-fast-reasoning": { - "id": "xai/grok-4.1-fast-reasoning", - "name": "Grok-4.1-Fast-Reasoning", - "family": "grok", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-11-19", - "last_updated": "2025-11-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "dmind/dmind-1-mini": { + "id": "dmind/dmind-1-mini", + "name": "DMind-1-Mini", + "family": "gpt", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-06-01", + "last_updated": "2025-06-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 2000000, "output": 30000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 8192 + }, + "cost": { + "input": 0.2, + "output": 0.4 + } }, - "xai/grok-3-mini": { - "id": "xai/grok-3-mini", - "name": "Grok 3 Mini", - "family": "grok", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-04-11", - "last_updated": "2025-04-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "Infermatic/MN-12B-Inferor-v0.0": { + "id": "Infermatic/MN-12B-Inferor-v0.0", + "name": "Mistral Nemo Inferor 12B", + "family": "mistral-nemo", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-01", + "last_updated": "2024-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 0.5, "cache_read": 0.075 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + }, + "cost": { + "input": 0.25499999999999995, + "output": 0.49299999999999994 + } }, - "xai/grok-4.20-multi-agent": { - "id": "xai/grok-4.20-multi-agent", - "name": "Grok-4.20-Multi-Agent", - "attachment": true, + "meituan-longcat/LongCat-Flash-Chat-FP8": { + "id": "meituan-longcat/LongCat-Flash-Chat-FP8", + "name": "LongCat Flash", + "family": "longcat", + "attachment": false, "reasoning": false, "tool_call": true, - "temperature": false, - "release_date": "2026-03-13", - "last_updated": "2026-03-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-08-31", + "last_updated": "2025-08-31", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 6, "cache_read": 0.2 }, - "limit": { "context": 128000, "output": 0 } + "limit": { + "context": 128000, + "input": 128000, + "output": 32768 + }, + "cost": { + "input": 0.15, + "output": 0.7 + } }, - "xai/grok-4-fast-reasoning": { - "id": "xai/grok-4-fast-reasoning", - "name": "Grok-4-Fast-Reasoning", - "family": "grok", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-09-16", - "last_updated": "2025-09-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "meganova-ai/manta-mini-1.0": { + "id": "meganova-ai/manta-mini-1.0", + "name": "Manta Mini 1.0", + "family": "nova", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-20", + "last_updated": "2025-12-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 128000 } + "limit": { + "context": 8192, + "input": 8192, + "output": 8192 + }, + "cost": { + "input": 0.02, + "output": 0.16 + } }, - "xai/grok-3": { - "id": "xai/grok-3", - "name": "Grok 3", - "family": "grok", - "attachment": true, + "meganova-ai/manta-pro-1.0": { + "id": "meganova-ai/manta-pro-1.0", + "name": "Manta Pro 1.0", + "family": "nova", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-04-11", - "last_updated": "2025-04-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-20", + "last_updated": "2025-12-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.75 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + }, + "cost": { + "input": 0.060000000000000005, + "output": 0.5 + } }, - "stabilityai/stablediffusionxl": { - "id": "stabilityai/stablediffusionxl", - "name": "StableDiffusionXL", - "family": "stable-diffusion", - "attachment": true, + "meganova-ai/manta-flash-1.0": { + "id": "meganova-ai/manta-flash-1.0", + "name": "Manta Flash 1.0", + "family": "nova", + "attachment": false, "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-20", + "last_updated": "2025-12-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + }, + "cost": { + "input": 0.02, + "output": 0.16 + } + }, + "minimax/minimax-m2.7": { + "id": "minimax/minimax-m2.7", + "name": "MiniMax M2.7", + "family": "minimax", + "attachment": false, + "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2023-07-09", - "last_updated": "2023-07-09", - "modalities": { "input": ["text", "image"], "output": ["image"] }, + "structured_output": true, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 200, "output": 0 } + "limit": { + "context": 204800, + "input": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "trytako/tako": { - "id": "trytako/tako", - "name": "Tako", - "family": "tako", + "minimax/minimax-01": { + "id": "minimax/minimax-01", + "name": "MiniMax 01", + "family": "minimax", "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2024-08-15", - "last_updated": "2024-08-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-01-15", + "last_updated": "2025-01-15", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 2048, "output": 0 } + "limit": { + "context": 1000192, + "input": 1000192, + "output": 16384 + }, + "cost": { + "input": 0.1394, + "output": 1.1219999999999999 + } }, - "google/gemini-2.5-flash-lite": { - "id": "google/gemini-2.5-flash-lite", - "name": "Gemini-2.5-Flash-Lite", - "family": "gemini-flash-lite", - "attachment": true, + "minimax/minimax-m2.1": { + "id": "minimax/minimax-m2.1", + "name": "MiniMax M2.1", + "family": "minimax", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-06-19", - "last_updated": "2025-06-19", - "modalities": { "input": ["text", "image", "video", "audio"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-12-19", + "last_updated": "2025-12-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.07, "output": 0.28 }, - "limit": { "context": 1024000, "output": 64000 } + "limit": { + "context": 200000, + "input": 200000, + "output": 131072 + }, + "cost": { + "input": 0.33, + "output": 1.32 + } }, - "google/lyria": { - "id": "google/lyria", - "name": "Lyria", - "family": "lyria", - "attachment": true, + "minimax/minimax-m2-her": { + "id": "minimax/minimax-m2-her", + "name": "MiniMax M2-her", + "family": "minimax", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-06-04", - "last_updated": "2025-06-04", - "modalities": { "input": ["text"], "output": ["audio"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2026-01-24", + "last_updated": "2026-01-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 0, "output": 0 } + "limit": { + "context": 65532, + "input": 65532, + "output": 2048 + }, + "cost": { + "input": 0.30200000000000005, + "output": 1.2069999999999999 + } }, - "google/imagen-4-ultra": { - "id": "google/imagen-4-ultra", - "name": "Imagen-4-Ultra", - "family": "imagen", - "attachment": true, - "reasoning": false, + "minimax/minimax-m2.5": { + "id": "minimax/minimax-m2.5", + "name": "MiniMax M2.5", + "family": "minimax", + "attachment": false, + "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-05-24", - "last_updated": "2025-05-24", - "modalities": { "input": ["text"], "output": ["image"] }, + "structured_output": true, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 480, "output": 0 } + "limit": { + "context": 204800, + "input": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "google/nano-banana-pro": { - "id": "google/nano-banana-pro", - "name": "Nano-Banana-Pro", - "family": "nano-banana", - "attachment": true, + "qwen/Qwen3.6-35B-A3B:thinking": { + "id": "qwen/Qwen3.6-35B-A3B:thinking", + "name": "Qwen3.6 35B A3B Thinking", + "family": "qwen3.6", + "attachment": false, + "reasoning": true, + "tool_call": false, + "structured_output": false, + "release_date": "2026-04-19", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 16384 + }, + "cost": { + "input": 0.29, + "output": 1.74 + } + }, + "qwen/qwen3.5-397b-a17b": { + "id": "qwen/qwen3.5-397b-a17b", + "name": "Qwen3.5 397B A17B", + "family": "qwen", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-11-19", - "last_updated": "2025-11-19", - "modalities": { "input": ["text", "image"], "output": ["image"] }, - "open_weights": false, - "cost": { "input": 2, "output": 12, "cache_read": 0.2 }, - "limit": { "context": 65536, "output": 0 } + "tool_call": false, + "structured_output": false, + "release_date": "2026-02-16", + "last_updated": "2026-02-16", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 258048, + "input": 258048, + "output": 65536 + }, + "cost": { + "input": 0.6, + "output": 3.6 + } }, - "google/imagen-3-fast": { - "id": "google/imagen-3-fast", - "name": "Imagen-3-Fast", - "family": "imagen", - "attachment": true, + "qwen/Qwen3.6-35B-A3B": { + "id": "qwen/Qwen3.6-35B-A3B", + "name": "Qwen3.6 35B A3B", + "family": "qwen3.6", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2024-10-17", - "last_updated": "2024-10-17", - "modalities": { "input": ["text"], "output": ["image"] }, - "open_weights": false, - "limit": { "context": 480, "output": 0 } + "tool_call": false, + "structured_output": false, + "release_date": "2026-04-17", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 16384 + }, + "cost": { + "input": 0.29, + "output": 1.74 + } }, - "google/imagen-3": { - "id": "google/imagen-3", - "name": "Imagen-3", - "family": "imagen", - "attachment": true, + "unsloth/gemma-3-1b-it": { + "id": "unsloth/gemma-3-1b-it", + "name": "Gemma 3 1B IT", + "family": "unsloth", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2024-10-15", - "last_updated": "2024-10-15", - "modalities": { "input": ["text"], "output": ["image"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-03-10", + "last_updated": "2025-03-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 480, "output": 0 } + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "cost": { + "input": 0.1003, + "output": 0.1003 + } }, - "google/gemini-2.0-flash-lite": { - "id": "google/gemini-2.0-flash-lite", - "name": "Gemini-2.0-Flash-Lite", - "family": "gemini-flash-lite", + "unsloth/gemma-3-12b-it": { + "id": "unsloth/gemma-3-12b-it", + "name": "Gemma 3 12B IT", + "family": "unsloth", "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-02-05", - "last_updated": "2025-02-05", - "modalities": { "input": ["text", "image", "video", "audio"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-03-10", + "last_updated": "2025-03-10", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.052, "output": 0.21 }, - "limit": { "context": 990000, "output": 8192 } + "limit": { + "context": 128000, + "input": 128000, + "output": 131072 + }, + "cost": { + "input": 0.272, + "output": 0.272 + } }, - "google/veo-3.1-fast": { - "id": "google/veo-3.1-fast", - "name": "Veo-3.1-Fast", - "family": "veo", + "unsloth/gemma-3-4b-it": { + "id": "unsloth/gemma-3-4b-it", + "name": "Gemma 3 4B IT", + "family": "unsloth", "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image"], "output": ["video"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-03-10", + "last_updated": "2025-03-10", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 480, "output": 0 } + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "cost": { + "input": 0.2006, + "output": 0.2006 + } }, - "google/veo-3-fast": { - "id": "google/veo-3-fast", - "name": "Veo-3-Fast", - "family": "veo", + "unsloth/gemma-3-27b-it": { + "id": "unsloth/gemma-3-27b-it", + "name": "Gemma 3 27B IT", + "family": "unsloth", "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-10-13", - "last_updated": "2025-10-13", - "modalities": { "input": ["text"], "output": ["video"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-03-10", + "last_updated": "2025-03-10", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 480, "output": 0 } + "limit": { + "context": 128000, + "input": 128000, + "output": 96000 + }, + "cost": { + "input": 0.2992, + "output": 0.2992 + } }, - "google/imagen-4-fast": { - "id": "google/imagen-4-fast", - "name": "Imagen-4-Fast", - "family": "imagen", - "attachment": true, + "THUDM/GLM-Z1-9B-0414": { + "id": "THUDM/GLM-Z1-9B-0414", + "name": "GLM Z1 9B 0414", + "family": "glm-z", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-06-25", - "last_updated": "2025-06-25", - "modalities": { "input": ["text"], "output": ["image"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 480, "output": 0 } + "limit": { + "context": 32000, + "input": 32000, + "output": 8000 + }, + "cost": { + "input": 0.2, + "output": 0.2 + } }, - "google/veo-3.1": { - "id": "google/veo-3.1", - "name": "Veo-3.1", - "family": "veo", - "attachment": true, + "THUDM/GLM-4-9B-0414": { + "id": "THUDM/GLM-4-9B-0414", + "name": "GLM 4 9B 0414", + "family": "glm", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text"], "output": ["video"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 480, "output": 0 } + "limit": { + "context": 32000, + "input": 32000, + "output": 8000 + }, + "cost": { + "input": 0.2, + "output": 0.2 + } }, - "google/imagen-4": { - "id": "google/imagen-4", - "name": "Imagen-4", - "family": "imagen", - "attachment": true, + "THUDM/GLM-Z1-Rumination-32B-0414": { + "id": "THUDM/GLM-Z1-Rumination-32B-0414", + "name": "GLM Z1 Rumination 32B 0414", + "family": "glm-z", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text"], "output": ["image"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-04-15", + "last_updated": "2025-04-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 480, "output": 0 } + "limit": { + "context": 32000, + "input": 32000, + "output": 65536 + }, + "cost": { + "input": 0.2, + "output": 0.2 + } }, - "google/veo-3": { - "id": "google/veo-3", - "name": "Veo-3", - "family": "veo", - "attachment": true, + "THUDM/GLM-4-32B-0414": { + "id": "THUDM/GLM-4-32B-0414", + "name": "GLM 4 32B 0414", + "family": "glm", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-05-21", - "last_updated": "2025-05-21", - "modalities": { "input": ["text"], "output": ["video"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 480, "output": 0 } + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + }, + "cost": { + "input": 0.2, + "output": 0.2 + } }, - "google/gemini-2.5-pro": { - "id": "google/gemini-2.5-pro", - "name": "Gemini-2.5-Pro", - "family": "gemini-pro", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-02-05", - "last_updated": "2025-02-05", - "modalities": { "input": ["text", "image", "video", "audio"], "output": ["text"] }, + "THUDM/GLM-Z1-32B-0414": { + "id": "THUDM/GLM-Z1-32B-0414", + "name": "GLM Z1 32B 0414", + "family": "glm-z", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-04-15", + "last_updated": "2025-04-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.87, "output": 7, "cache_read": 0.087 }, - "limit": { "context": 1065535, "output": 65535 } + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + }, + "cost": { + "input": 0.2, + "output": 0.2 + } }, - "google/gemini-2.5-flash": { - "id": "google/gemini-2.5-flash", - "name": "Gemini-2.5-Flash", + "google/gemini-3-flash-preview": { + "id": "google/gemini-3-flash-preview", + "name": "Gemini 3 Flash (Preview)", "family": "gemini-flash", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-04-26", - "last_updated": "2025-04-26", - "modalities": { "input": ["text", "image", "video", "audio"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.21, "output": 1.8, "cache_read": 0.021 }, - "limit": { "context": 1065535, "output": 65535 } + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 3 + } }, - "google/gemini-2.0-flash": { - "id": "google/gemini-2.0-flash", - "name": "Gemini-2.0-Flash", + "google/gemini-flash-1.5": { + "id": "google/gemini-flash-1.5", + "name": "Gemini 1.5 Flash", "family": "gemini-flash", - "attachment": true, + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text", "image", "video", "audio"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-05-14", + "last_updated": "2024-05-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.42 }, - "limit": { "context": 990000, "output": 8192 } + "limit": { + "context": 2000000, + "input": 2000000, + "output": 8192 + }, + "cost": { + "input": 0.0748, + "output": 0.306 + } }, - "google/gemini-3-pro": { - "id": "google/gemini-3-pro", - "name": "Gemini-3-Pro", - "family": "gemini-pro", + "google/gemini-3-flash-preview-thinking": { + "id": "google/gemini-3-flash-preview-thinking", + "name": "Gemini 3 Flash Thinking", + "family": "gemini-flash", "attachment": true, "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-10-22", - "last_updated": "2025-10-22", - "modalities": { "input": ["text", "image", "video", "audio"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.6, "output": 9.6, "cache_read": 0.16 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 3 + } }, - "google/gemini-deep-research": { - "id": "google/gemini-deep-research", - "name": "gemini-deep-research", + "moonshotai/kimi-k2.5": { + "id": "moonshotai/kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "temperature": false, - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "structured_output": false, + "release_date": "2026-01-26", + "last_updated": "2026-01-26", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.6, "output": 9.6 }, - "limit": { "context": 1048576, "output": 0 } + "limit": { + "context": 256000, + "input": 256000, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 1.9 + } }, - "google/veo-2": { - "id": "google/veo-2", - "name": "Veo-2", - "family": "veo", - "attachment": true, + "moonshotai/kimi-k2-instruct": { + "id": "moonshotai/kimi-k2-instruct", + "name": "Kimi K2 Instruct", + "family": "kimi", + "attachment": false, "reasoning": false, "tool_call": true, - "temperature": false, - "release_date": "2024-12-02", - "last_updated": "2024-12-02", - "modalities": { "input": ["text"], "output": ["video"] }, + "structured_output": true, + "release_date": "2025-07-01", + "last_updated": "2025-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 480, "output": 0 } + "limit": { + "context": 256000, + "input": 256000, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 2 + } }, - "google/gemini-3.1-pro": { - "id": "google/gemini-3.1-pro", - "name": "Gemini-3.1-Pro", - "attachment": true, + "moonshotai/kimi-k2-thinking-original": { + "id": "moonshotai/kimi-k2-thinking-original", + "name": "Kimi K2 Thinking Original", + "family": "kimi-thinking", + "attachment": false, "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2026-02-19", - "last_updated": "2026-02-19", - "modalities": { "input": ["text", "image", "video", "audio"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-11-06", + "last_updated": "2025-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 12, "cache_read": 0.2 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 256000, + "input": 256000, + "output": 16384 + }, + "cost": { + "input": 0.6, + "output": 2.5 + } }, - "google/nano-banana": { - "id": "google/nano-banana", - "name": "Nano-Banana", - "family": "nano-banana", - "attachment": true, + "moonshotai/kimi-k2-instruct-0711": { + "id": "moonshotai/kimi-k2-instruct-0711", + "name": "Kimi K2 0711", + "family": "kimi", + "attachment": false, "reasoning": false, "tool_call": true, - "temperature": false, - "release_date": "2025-08-21", - "last_updated": "2025-08-21", - "modalities": { "input": ["text", "image"], "output": ["text", "image"] }, + "structured_output": true, + "release_date": "2025-07-11", + "last_updated": "2025-07-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.21, "output": 1.8, "cache_read": 0.021 }, - "limit": { "context": 65536, "output": 0 } + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 2 + } }, - "google/gemini-3-flash": { - "id": "google/gemini-3-flash", - "name": "Gemini-3-Flash", + "moonshotai/Kimi-Dev-72B": { + "id": "moonshotai/Kimi-Dev-72B", + "name": "Kimi Dev 72B", + "family": "kimi", "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-10-07", - "last_updated": "2025-10-07", - "modalities": { "input": ["text", "image", "video", "audio"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-04-15", + "last_updated": "2025-04-15", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 2.4, "cache_read": 0.04 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 128000, + "input": 128000, + "output": 131072 + }, + "cost": { + "input": 0.4, + "output": 0.4 + } }, - "google/gemini-3.1-flash-lite": { - "id": "google/gemini-3.1-flash-lite", - "name": "Gemini-3.1-Flash-Lite", - "attachment": true, + "moonshotai/kimi-k2-thinking-turbo-original": { + "id": "moonshotai/kimi-k2-thinking-turbo-original", + "name": "Kimi K2 Thinking Turbo Original", + "family": "kimi-thinking", + "attachment": false, "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2026-02-18", - "last_updated": "2026-02-18", - "modalities": { "input": ["text", "image", "video", "audio"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-11-06", + "last_updated": "2025-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 1.5 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 256000, + "input": 256000, + "output": 16384 + }, + "cost": { + "input": 1.15, + "output": 8 + } }, - "poetools/claude-code": { - "id": "poetools/claude-code", - "name": "claude-code", + "moonshotai/kimi-k2.6": { + "id": "moonshotai/kimi-k2.6", + "name": "Kimi K2.6", + "family": "kimi-k2.6", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "temperature": false, - "release_date": "2025-11-27", - "last_updated": "2025-11-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "limit": { "context": 0, "output": 0 } + "structured_output": false, + "release_date": "2026-04-16", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 65536 + }, + "cost": { + "input": 0.53, + "output": 2.73 + } }, - "novita/glm-4.7-n": { - "id": "novita/glm-4.7-n", - "name": "glm-4.7-n", + "moonshotai/kimi-k2.6:thinking": { + "id": "moonshotai/kimi-k2.6:thinking", + "name": "Kimi K2.6 Thinking", + "family": "kimi-thinking", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "limit": { "context": 205000, "output": 131072 } + "structured_output": false, + "release_date": "2026-04-16", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 65536 + }, + "cost": { + "input": 0.53, + "output": 2.73 + } }, - "novita/kimi-k2-thinking": { - "id": "novita/kimi-k2-thinking", - "name": "kimi-k2-thinking", + "moonshotai/Kimi-K2-Instruct-0905": { + "id": "moonshotai/Kimi-K2-Instruct-0905", + "name": "Kimi K2 0905", "family": "kimi", - "attachment": true, - "reasoning": true, + "attachment": false, + "reasoning": false, "tool_call": true, - "temperature": false, - "release_date": "2025-11-07", - "last_updated": "2025-11-07", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 256000, "output": 0 } + "limit": { + "context": 256000, + "input": 256000, + "output": 262144 + }, + "cost": { + "input": 0.4, + "output": 2 + } }, - "novita/kimi-k2.5": { - "id": "novita/kimi-k2.5", - "name": "kimi-k2.5", - "attachment": true, - "reasoning": true, + "moonshotai/kimi-k2-thinking": { + "id": "moonshotai/kimi-k2-thinking", + "name": "Kimi K2 Thinking", + "family": "kimi-thinking", + "attachment": false, + "reasoning": false, "tool_call": true, - "temperature": false, - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-11-06", + "last_updated": "2025-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 256000, "output": 262144 } + "limit": { + "context": 256000, + "input": 256000, + "output": 262144 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "novita/glm-4.7-flash": { - "id": "novita/glm-4.7-flash", - "name": "glm-4.7-flash", + "moonshotai/kimi-k2.5:thinking": { + "id": "moonshotai/kimi-k2.5:thinking", + "name": "Kimi K2.5 Thinking", + "family": "kimi-thinking", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2026-01-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "release_date": "2026-01-26", + "last_updated": "2026-01-26", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 200000, "output": 65500 } + "limit": { + "context": 256000, + "input": 256000, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 1.9 + } }, - "novita/minimax-m2.1": { - "id": "novita/minimax-m2.1", - "name": "minimax-m2.1", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-12-26", - "last_updated": "2025-12-26", - "modalities": { "input": ["text"], "output": ["text"] }, + "Tongyi-Zhiwen/QwenLong-L1-32B": { + "id": "Tongyi-Zhiwen/QwenLong-L1-32B", + "name": "QwenLong L1 32B", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-01-25", + "last_updated": "2025-01-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 205000, "output": 131072 } + "limit": { + "context": 128000, + "input": 128000, + "output": 40960 + }, + "cost": { + "input": 0.13999999999999999, + "output": 0.6 + } }, - "novita/glm-4.6": { - "id": "novita/glm-4.6", - "name": "GLM-4.6", - "family": "glm", - "attachment": true, + "nothingiisreal/L3.1-70B-Celeste-V0.1-BF16": { + "id": "nothingiisreal/L3.1-70B-Celeste-V0.1-BF16", + "name": "Llama 3.1 70B Celeste v0.1", + "family": "llama", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 0, "output": 0 } + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + }, + "cost": { + "input": 0.49299999999999994, + "output": 0.49299999999999994 + } }, - "novita/glm-4.6v": { - "id": "novita/glm-4.6v", - "name": "glm-4.6v", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-12-09", - "last_updated": "2025-12-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "aion-labs/aion-1.0": { + "id": "aion-labs/aion-1.0", + "name": "Aion 1.0", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-02-01", + "last_updated": "2025-02-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 131000, "output": 32768 } + "limit": { + "context": 65536, + "input": 65536, + "output": 8192 + }, + "cost": { + "input": 3.995, + "output": 7.99 + } }, - "novita/glm-4.7": { - "id": "novita/glm-4.7", - "name": "glm-4.7", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "aion-labs/aion-rp-llama-3.1-8b": { + "id": "aion-labs/aion-rp-llama-3.1-8b", + "name": "Llama 3.1 8b (uncensored)", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 205000, "output": 131072 } - }, - "novita/deepseek-v3.2": { - "id": "novita/deepseek-v3.2", - "name": "DeepSeek-V3.2", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.27, "output": 0.4, "cache_read": 0.13 }, - "limit": { "context": 128000, "output": 0 } + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + }, + "cost": { + "input": 0.2006, + "output": 0.2006 + } }, - "topazlabs-co/topazlabs": { - "id": "topazlabs-co/topazlabs", - "name": "TopazLabs", - "family": "topazlabs", - "attachment": true, + "aion-labs/aion-1.0-mini": { + "id": "aion-labs/aion-1.0-mini", + "name": "Aion 1.0 mini (DeepSeek)", + "family": "deepseek", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2024-12-03", - "last_updated": "2024-12-03", - "modalities": { "input": ["text"], "output": ["image"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-02-20", + "last_updated": "2025-02-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 204, "output": 0 } + "limit": { + "context": 131072, + "input": 131072, + "output": 8192 + }, + "cost": { + "input": 0.7989999999999999, + "output": 1.394 + } }, - "elevenlabs/elevenlabs-v3": { - "id": "elevenlabs/elevenlabs-v3", - "name": "ElevenLabs-v3", - "family": "elevenlabs", - "attachment": true, + "Alibaba-NLP/Tongyi-DeepResearch-30B-A3B": { + "id": "Alibaba-NLP/Tongyi-DeepResearch-30B-A3B", + "name": "Tongyi DeepResearch 30B A3B", + "family": "yi", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-06-05", - "last_updated": "2025-06-05", - "modalities": { "input": ["text"], "output": ["audio"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-26", + "last_updated": "2025-08-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 0 } + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + }, + "cost": { + "input": 0.08, + "output": 0.24000000000000002 + } }, - "elevenlabs/elevenlabs-music": { - "id": "elevenlabs/elevenlabs-music", - "name": "ElevenLabs-Music", - "family": "elevenlabs", - "attachment": true, + "MiniMaxAI/MiniMax-M1-80k": { + "id": "MiniMaxAI/MiniMax-M1-80k", + "name": "MiniMax M1 80K", + "family": "minimax", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-08-29", - "last_updated": "2025-08-29", - "modalities": { "input": ["text"], "output": ["audio"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-06-16", + "last_updated": "2025-06-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 2000, "output": 0 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 131072 + }, + "cost": { + "input": 0.6052, + "output": 2.4225000000000003 + } }, - "elevenlabs/elevenlabs-v2.5-turbo": { - "id": "elevenlabs/elevenlabs-v2.5-turbo", - "name": "ElevenLabs-v2.5-Turbo", - "family": "elevenlabs", + "anthropic/claude-opus-4.6:thinking:low": { + "id": "anthropic/claude-opus-4.6:thinking:low", + "name": "Claude 4.6 Opus Thinking Low", + "family": "claude-opus", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2024-10-28", - "last_updated": "2024-10-28", - "modalities": { "input": ["text"], "output": ["audio"] }, + "structured_output": true, + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 128000, "output": 0 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 128000 + }, + "cost": { + "input": 4.998, + "output": 25.007 + } }, - "lumalabs/ray2": { - "id": "lumalabs/ray2", - "name": "Ray2", - "family": "ray", + "anthropic/claude-opus-4.6": { + "id": "anthropic/claude-opus-4.6", + "name": "Claude 4.6 Opus", + "family": "claude-opus", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-02-20", - "last_updated": "2025-02-20", - "modalities": { "input": ["text", "image"], "output": ["video"] }, + "structured_output": true, + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 5000, "output": 0 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 128000 + }, + "cost": { + "input": 4.998, + "output": 25.007 + } }, - "cerebras/gpt-oss-120b-cs": { - "id": "cerebras/gpt-oss-120b-cs", - "name": "gpt-oss-120b-cs", + "anthropic/claude-sonnet-4.6:thinking": { + "id": "anthropic/claude-sonnet-4.6:thinking", + "name": "Claude Sonnet 4.6 Thinking", + "family": "claude-sonnet", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-08-06", - "last_updated": "2025-08-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "limit": { "context": 0, "output": 0 } - }, - "cerebras/llama-3.3-70b-cs": { - "id": "cerebras/llama-3.3-70b-cs", - "name": "llama-3.3-70b-cs", + "structured_output": true, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-02-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 128000 + }, + "cost": { + "input": 2.992, + "output": 14.993999999999998 + } + }, + "anthropic/claude-opus-4.6:thinking:max": { + "id": "anthropic/claude-opus-4.6:thinking:max", + "name": "Claude 4.6 Opus Thinking Max", + "family": "claude-opus", "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2025-05-13", - "last_updated": "2025-05-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 0, "output": 0 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 128000 + }, + "cost": { + "input": 4.998, + "output": 25.007 + } }, - "cerebras/qwen3-235b-2507-cs": { - "id": "cerebras/qwen3-235b-2507-cs", - "name": "qwen3-235b-2507-cs", + "anthropic/claude-opus-4.6:thinking:medium": { + "id": "anthropic/claude-opus-4.6:thinking:medium", + "name": "Claude 4.6 Opus Thinking Medium", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-08-06", - "last_updated": "2025-08-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 0, "output": 0 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 128000 + }, + "cost": { + "input": 4.998, + "output": 25.007 + } }, - "cerebras/llama-3.1-8b-cs": { - "id": "cerebras/llama-3.1-8b-cs", - "name": "llama-3.1-8b-cs", + "anthropic/claude-sonnet-4.6": { + "id": "anthropic/claude-sonnet-4.6", + "name": "Claude Sonnet 4.6", + "family": "claude-sonnet", "attachment": true, "reasoning": false, "tool_call": true, - "temperature": false, - "release_date": "2025-05-13", - "last_updated": "2025-05-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-02-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 0, "output": 0 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 128000 + }, + "cost": { + "input": 2.992, + "output": 14.993999999999998 + } }, - "cerebras/qwen3-32b-cs": { - "id": "cerebras/qwen3-32b-cs", - "name": "qwen3-32b-cs", + "anthropic/claude-opus-4.6:thinking": { + "id": "anthropic/claude-opus-4.6:thinking", + "name": "Claude 4.6 Opus Thinking", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-05-15", - "last_updated": "2025-05-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 0, "output": 0 } + "limit": { + "context": 1000000, + "input": 1000000, + "output": 128000 + }, + "cost": { + "input": 4.998, + "output": 25.007 + } }, - "ideogramai/ideogram-v2": { - "id": "ideogramai/ideogram-v2", - "name": "Ideogram-v2", - "family": "ideogram", - "attachment": true, + "abacusai/Dracarys-72B-Instruct": { + "id": "abacusai/Dracarys-72B-Instruct", + "name": "Llama 3.1 70B Dracarys 2", + "family": "llama", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2024-08-21", - "last_updated": "2024-08-21", - "modalities": { "input": ["text", "image"], "output": ["image"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-02", + "last_updated": "2025-08-02", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 150, "output": 0 } + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + }, + "cost": { + "input": 0.49299999999999994, + "output": 0.49299999999999994 + } }, - "ideogramai/ideogram-v2a": { - "id": "ideogramai/ideogram-v2a", - "name": "Ideogram-v2a", - "family": "ideogram", - "attachment": true, + "EVA-UNIT-01/EVA-LLaMA-3.33-70B-v0.0": { + "id": "EVA-UNIT-01/EVA-LLaMA-3.33-70B-v0.0", + "name": "EVA Llama 3.33 70B", + "family": "llama", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-02-27", - "last_updated": "2025-02-27", - "modalities": { "input": ["text"], "output": ["image"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-26", + "last_updated": "2025-07-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 150, "output": 0 } + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + }, + "cost": { + "input": 2.006, + "output": 2.006 + } }, - "ideogramai/ideogram-v2a-turbo": { - "id": "ideogramai/ideogram-v2a-turbo", - "name": "Ideogram-v2a-Turbo", - "family": "ideogram", - "attachment": true, + "EVA-UNIT-01/EVA-Qwen2.5-72B-v0.2": { + "id": "EVA-UNIT-01/EVA-Qwen2.5-72B-v0.2", + "name": "EVA-Qwen2.5-72B-v0.2", + "family": "qwen", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-02-27", - "last_updated": "2025-02-27", - "modalities": { "input": ["text"], "output": ["image"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 150, "output": 0 } + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + }, + "cost": { + "input": 0.7989999999999999, + "output": 0.7989999999999999 + } }, - "ideogramai/ideogram": { - "id": "ideogramai/ideogram", - "name": "Ideogram", - "family": "ideogram", - "attachment": true, + "EVA-UNIT-01/EVA-LLaMA-3.33-70B-v0.1": { + "id": "EVA-UNIT-01/EVA-LLaMA-3.33-70B-v0.1", + "name": "EVA-LLaMA-3.33-70B-v0.1", + "family": "llama", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2024-04-03", - "last_updated": "2024-04-03", - "modalities": { "input": ["text", "image"], "output": ["image"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 150, "output": 0 } - } - } - }, - "v0": { - "id": "v0", - "env": ["V0_API_KEY"], - "npm": "@ai-sdk/vercel", - "name": "v0", - "doc": "https://sdk.vercel.ai/providers/ai-sdk-providers/vercel", - "models": { - "v0-1.5-lg": { - "id": "v0-1.5-lg", - "name": "v0-1.5-lg", - "family": "v0", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-06-09", - "last_updated": "2025-06-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + }, + "cost": { + "input": 2.006, + "output": 2.006 + } + }, + "EVA-UNIT-01/EVA-Qwen2.5-32B-v0.2": { + "id": "EVA-UNIT-01/EVA-Qwen2.5-32B-v0.2", + "name": "EVA-Qwen2.5-32B-v0.2", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-26", + "last_updated": "2025-07-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75 }, - "limit": { "context": 512000, "output": 32000 } + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + }, + "cost": { + "input": 0.7989999999999999, + "output": 0.7989999999999999 + } }, - "v0-1.0-md": { - "id": "v0-1.0-md", - "name": "v0-1.0-md", - "family": "v0", - "attachment": true, + "huihui-ai/DeepSeek-R1-Distill-Qwen-32B-abliterated": { + "id": "huihui-ai/DeepSeek-R1-Distill-Qwen-32B-abliterated", + "name": "DeepSeek R1 Qwen Abliterated", + "family": "qwen", + "attachment": false, "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 128000, "output": 32000 } + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + }, + "cost": { + "input": 1.4, + "output": 1.4 + } }, - "v0-1.5-md": { - "id": "v0-1.5-md", - "name": "v0-1.5-md", - "family": "v0", - "attachment": true, + "huihui-ai/DeepSeek-R1-Distill-Llama-70B-abliterated": { + "id": "huihui-ai/DeepSeek-R1-Distill-Llama-70B-abliterated", + "name": "DeepSeek R1 Llama 70B Abliterated", + "family": "deepseek", + "attachment": false, "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-06-09", - "last_updated": "2025-06-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "structured_output": false, + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + }, + "cost": { + "input": 0.7, + "output": 0.7 + } + }, + "huihui-ai/Llama-3.3-70B-Instruct-abliterated": { + "id": "huihui-ai/Llama-3.3-70B-Instruct-abliterated", + "name": "Llama 3.3 70B Instruct abliterated", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-08-08", + "last_updated": "2025-08-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + }, + "cost": { + "input": 0.7, + "output": 0.7 + } + }, + "huihui-ai/Qwen2.5-32B-Instruct-abliterated": { + "id": "huihui-ai/Qwen2.5-32B-Instruct-abliterated", + "name": "Qwen 2.5 32B Abliterated", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-01-06", + "last_updated": "2025-01-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "input": 32768, + "output": 8192 + }, + "cost": { + "input": 0.7, + "output": 0.7 + } + }, + "huihui-ai/Llama-3.1-Nemotron-70B-Instruct-HF-abliterated": { + "id": "huihui-ai/Llama-3.1-Nemotron-70B-Instruct-HF-abliterated", + "name": "Nemotron 3.1 70B abliterated", + "family": "nemotron", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + }, + "cost": { + "input": 0.7, + "output": 0.7 + } + }, + "xiaomi/mimo-v2-flash-thinking-original": { + "id": "xiaomi/mimo-v2-flash-thinking-original", + "name": "MiMo V2 Flash (Thinking) Original", + "family": "mimo", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "input": 256000, + "output": 32768 + }, + "cost": { + "input": 0.102, + "output": 0.306 + } + }, + "xiaomi/mimo-v2-flash-thinking": { + "id": "xiaomi/mimo-v2-flash-thinking", + "name": "MiMo V2 Flash (Thinking)", + "family": "mimo", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "input": 256000, + "output": 32768 + }, + "cost": { + "input": 0.102, + "output": 0.306 + } + }, + "xiaomi/mimo-v2-flash": { + "id": "xiaomi/mimo-v2-flash", + "name": "MiMo V2 Flash", + "family": "mimo", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "input": 256000, + "output": 32768 + }, + "cost": { + "input": 0.102, + "output": 0.306 + } + }, + "xiaomi/mimo-v2-flash-original": { + "id": "xiaomi/mimo-v2-flash-original", + "name": "MiMo V2 Flash Original", + "family": "mimo", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "input": 256000, + "output": 32768 + }, + "cost": { + "input": 0.102, + "output": 0.306 + } + }, + "tngtech/DeepSeek-TNG-R1T2-Chimera": { + "id": "tngtech/DeepSeek-TNG-R1T2-Chimera", + "name": "DeepSeek TNG R1T2 Chimera", + "family": "tngtech", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "cost": { + "input": 0.31, + "output": 0.31 + } + }, + "tngtech/tng-r1t-chimera": { + "id": "tngtech/tng-r1t-chimera", + "name": "TNG R1T Chimera", + "family": "tngtech", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-11-26", + "last_updated": "2025-11-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } + }, + "inflatebot/MN-12B-Mag-Mell-R1": { + "id": "inflatebot/MN-12B-Mag-Mell-R1", + "name": "Mag Mell R1", + "family": "mistral-nemo", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2024-07-01", + "last_updated": "2024-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + }, + "cost": { + "input": 0.49299999999999994, + "output": 0.49299999999999994 + } + }, + "failspy/Meta-Llama-3-70B-Instruct-abliterated-v3.5": { + "id": "failspy/Meta-Llama-3-70B-Instruct-abliterated-v3.5", + "name": "Llama 3 70B abliterated", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "release_date": "2025-07-26", + "last_updated": "2025-07-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 128000, "output": 32000 } + "limit": { + "context": 8192, + "input": 8192, + "output": 8192 + }, + "cost": { + "input": 0.7, + "output": 0.7 + } } } }, - "opencode": { - "id": "opencode", - "env": ["OPENCODE_API_KEY"], + "abacus": { + "id": "abacus", + "env": ["ABACUS_API_KEY"], "npm": "@ai-sdk/openai-compatible", - "api": "https://opencode.ai/zen/v1", - "name": "OpenCode Zen", - "doc": "https://opencode.ai/docs/zen", + "api": "https://routellm.abacus.ai/v1", + "name": "Abacus", + "doc": "https://abacus.ai/help/api", "models": { - "gpt-5.2-codex": { - "id": "gpt-5.2-codex", - "name": "GPT-5.2 Codex", - "family": "gpt-codex", + "gpt-5.1-codex-max": { + "id": "gpt-5.1-codex-max", + "name": "GPT-5.1 Codex Max", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-01-14", - "last_updated": "2026-01-14", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai" } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, - "mimo-v2-flash-free": { - "id": "mimo-v2-flash-free", - "name": "MiMo V2 Flash Free", - "family": "mimo-flash-free", + "claude-opus-4-5-20251101": { + "id": "claude-opus-4-5-20251101", + "name": "Claude Opus 4.5", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-11-01", + "last_updated": "2025-11-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 5, + "output": 25 + } + }, + "kimi-k2.5": { + "id": "kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "structured_output": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-12-16", - "last_updated": "2025-12-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0 }, - "limit": { "context": 262144, "output": 65536 }, - "status": "deprecated" + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0.6, + "output": 3 + } }, - "gpt-5.1-codex-mini": { - "id": "gpt-5.1-codex-mini", - "name": "GPT-5.1 Codex Mini", - "family": "gpt-codex", + "gemini-3.1-flash-lite-preview": { + "id": "gemini-3.1-flash-lite-preview", + "name": "Gemini 3.1 Flash Lite Preview", + "family": "gemini-flash", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "release_date": "2026-03-01", + "last_updated": "2026-03-01", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.025 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai" } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.25, + "output": 1.5, + "cache_read": 0.025, + "cache_write": 1 + } }, - "gpt-5.4-pro": { - "id": "gpt-5.4-pro", - "name": "GPT-5.4 Pro", - "family": "gpt-pro", + "claude-sonnet-4-6": { + "id": "claude-sonnet-4-6", + "name": "Claude Sonnet 4.6", + "family": "claude-sonnet", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": false, - "temperature": false, + "temperature": true, "knowledge": "2025-08-31", - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-02-17", + "last_updated": "2026-02-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 30, "output": 180, "cache_read": 30 }, - "limit": { "context": 1050000, "input": 922000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai" } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "big-pickle": { - "id": "big-pickle", - "name": "Big Pickle", - "family": "big-pickle", - "attachment": false, + "gemini-3.1-pro-preview": { + "id": "gemini-3.1-pro-preview", + "name": "Gemini 3.1 Pro Preview", + "family": "gemini-pro", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, "knowledge": "2025-01", - "release_date": "2025-10-17", - "last_updated": "2025-10-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-19", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 200000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/anthropic" } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12 + } }, - "gpt-5.4-mini": { - "id": "gpt-5.4-mini", - "name": "GPT-5.4 Mini", - "family": "gpt-mini", + "gpt-5.3-chat-latest": { + "id": "gpt-5.3-chat-latest", + "name": "GPT-5.3 Chat Latest", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-17", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "release_date": "2026-03-01", + "last_updated": "2026-03-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.75, "output": 4.5, "cache_read": 0.075 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai" } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14 + } }, - "kimi-k2-thinking": { - "id": "kimi-k2-thinking", - "name": "Kimi K2 Thinking", - "family": "kimi-thinking", - "attachment": false, + "gemini-3-flash-preview": { + "id": "gemini-3-flash-preview", + "name": "Gemini 3 Flash Preview", + "family": "gemini-flash", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.4, "output": 2.5, "cache_read": 0.4 }, - "limit": { "context": 262144, "output": 262144 }, - "status": "deprecated" + "knowledge": "2025-01", + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 3 + } }, - "minimax-m2.1-free": { - "id": "minimax-m2.1-free", - "name": "MiniMax M2.1 Free", - "family": "minimax-free", + "llama-3.3-70b-versatile": { + "id": "llama-3.3-70b-versatile", + "name": "Llama 3.3 70B Versatile", + "family": "llama", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0 }, - "limit": { "context": 204800, "output": 131072 }, - "status": "deprecated", - "provider": { "npm": "@ai-sdk/anthropic" } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.59, + "output": 0.79 + } }, - "gpt-5": { - "id": "gpt-5", - "name": "GPT-5", - "family": "gpt", + "gpt-5-mini": { + "id": "gpt-5-mini", + "name": "GPT-5 Mini", + "family": "gpt-mini", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": false, - "knowledge": "2024-09-30", + "knowledge": "2024-05-30", "release_date": "2025-08-07", "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.07, "output": 8.5, "cache_read": 0.107 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai" } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2 + } }, - "claude-opus-4-5": { - "id": "claude-opus-4-5", - "name": "Claude Opus 4.5", - "family": "claude-opus", + "gpt-5-nano": { + "id": "gpt-5-nano", + "name": "GPT-5 Nano", + "family": "gpt-nano", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-11-24", - "last_updated": "2025-11-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 200000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/anthropic" } - }, - "kimi-k2.5": { - "id": "kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi", - "attachment": true, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 3, "cache_read": 0.08 }, - "limit": { "context": 262144, "output": 65536 } - }, - "mimo-v2-omni-free": { - "id": "mimo-v2-omni-free", - "name": "MiMo V2 Omni Free", - "family": "mimo-omni-free", - "attachment": true, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2024-12", - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text", "image", "audio", "pdf"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0 }, - "limit": { "context": 262144, "output": 64000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.05, + "output": 0.4 + } }, "gpt-5.3-codex": { "id": "gpt-5.3-codex", "name": "GPT-5.3 Codex", - "family": "gpt-codex", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, "knowledge": "2025-08-31", - "release_date": "2026-02-24", - "last_updated": "2026-02-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai" } - }, - "qwen3.6-plus-free": { - "id": "qwen3.6-plus-free", - "name": "Qwen3.6 Plus Free", - "family": "qwen-free", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2024-12", - "release_date": "2026-03-30", - "last_updated": "2026-03-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0 }, - "limit": { "context": 1048576, "output": 64000 } - }, - "gpt-5.3-codex-spark": { - "id": "gpt-5.3-codex-spark", - "name": "GPT-5.3 Codex Spark", - "family": "gpt-codex-spark", - "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 128000, "input": 128000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai" } - }, - "kimi-k2": { - "id": "kimi-k2", - "name": "Kimi K2", - "family": "kimi", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.4, "output": 2.5, "cache_read": 0.4 }, - "limit": { "context": 262144, "output": 262144 }, - "status": "deprecated" - }, - "claude-opus-4-6": { - "id": "claude-opus-4-6", - "name": "Claude Opus 4.6", - "family": "claude-opus", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-08-31", "release_date": "2026-02-05", "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 1000000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/anthropic" } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14 + } }, - "gpt-5.1-codex-max": { - "id": "gpt-5.1-codex-max", - "name": "GPT-5.1 Codex Max", - "family": "gpt-codex", + "claude-sonnet-4-5-20250929": { + "id": "claude-sonnet-4-5-20250929", + "name": "Claude Sonnet 4.5", + "family": "claude-sonnet", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai" } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "claude-sonnet-4-6": { - "id": "claude-sonnet-4-6", - "name": "Claude Sonnet 4.6", - "family": "claude-sonnet", + "gemini-2.5-pro": { + "id": "gemini-2.5-pro", + "name": "Gemini 2.5 Pro", + "family": "gemini-pro", "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": true, "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2026-02-17", - "last_updated": "2026-02-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-03-25", + "last_updated": "2025-03-25", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 1000000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/anthropic" } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, - "grok-code": { - "id": "grok-code", - "name": "Grok Code Fast 1", + "grok-4-1-fast-non-reasoning": { + "id": "grok-4-1-fast-non-reasoning", + "name": "Grok 4.1 Fast (Non-Reasoning)", "family": "grok", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2025-08-20", - "last_updated": "2025-08-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-11-17", + "last_updated": "2025-11-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 256000, "output": 256000 }, - "status": "deprecated" + "limit": { + "context": 2000000, + "output": 16384 + }, + "cost": { + "input": 0.2, + "output": 0.5 + } }, - "gpt-5.4": { - "id": "gpt-5.4", - "name": "GPT-5.4", + "gpt-5.2": { + "id": "gpt-5.2", + "name": "GPT-5.2", "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": false, "knowledge": "2025-08-31", - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 15, "cache_read": 0.25 }, - "limit": { "context": 1050000, "input": 922000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai" } - }, - "qwen3-coder": { - "id": "qwen3-coder", - "name": "Qwen3 Coder", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.45, "output": 1.8 }, - "limit": { "context": 262144, "output": 65536 }, - "status": "deprecated" + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14 + } }, - "minimax-m2.1": { - "id": "minimax-m2.1", - "name": "MiniMax M2.1", - "family": "minimax", - "attachment": false, + "o3-pro": { + "id": "o3-pro", + "name": "o3-pro", + "family": "o-pro", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.1 }, - "limit": { "context": 204800, "output": 131072 }, - "status": "deprecated" + "temperature": false, + "knowledge": "2024-05", + "release_date": "2025-06-10", + "last_updated": "2025-06-10", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 20, + "output": 40 + } }, - "kimi-k2.5-free": { - "id": "kimi-k2.5-free", - "name": "Kimi K2.5 Free", - "family": "kimi-free", + "gpt-4o-mini": { + "id": "gpt-4o-mini", + "name": "GPT-4o Mini", + "family": "gpt", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2024-10", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0 }, - "limit": { "context": 262144, "output": 262144 }, - "status": "deprecated" + "knowledge": "2024-04", + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "glm-5-free": { - "id": "glm-5-free", - "name": "GLM-5 Free", - "family": "glm-free", + "qwen3-max": { + "id": "qwen3-max", + "name": "Qwen3 Max", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0 }, - "limit": { "context": 204800, "output": 131072 }, - "status": "deprecated" + "release_date": "2025-05-28", + "last_updated": "2025-05-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 1.2, + "output": 6 + } }, - "claude-opus-4-1": { - "id": "claude-opus-4-1", - "name": "Claude Opus 4.1", - "family": "claude-opus", + "o4-mini": { + "id": "o4-mini", + "name": "o4-mini", + "family": "o-mini", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 }, - "provider": { "npm": "@ai-sdk/anthropic" } - }, - "glm-4.6": { - "id": "glm-4.6", - "name": "GLM-4.6", - "family": "glm", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.2, "cache_read": 0.1 }, - "limit": { "context": 204800, "output": 131072 }, - "status": "deprecated" + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4 + } }, - "gpt-5.4-nano": { - "id": "gpt-5.4-nano", - "name": "GPT-5.4 Nano", - "family": "gpt-nano", + "gpt-5.2-codex": { + "id": "gpt-5.2-codex", + "name": "GPT-5.2 Codex", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, "knowledge": "2025-08-31", - "release_date": "2026-03-17", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 1.25, "cache_read": 0.02 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai" } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14 + } }, - "claude-sonnet-4": { - "id": "claude-sonnet-4", - "name": "Claude Sonnet 4", - "family": "claude-sonnet", + "gemini-2.5-flash": { + "id": "gemini-2.5-flash", + "name": "Gemini 2.5 Flash", + "family": "gemini-flash", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75, - "context_over_200k": { "input": 6, "output": 22.5, "cache_read": 0.6, "cache_write": 7.5 } + "limit": { + "context": 1048576, + "output": 65536 }, - "limit": { "context": 1000000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/anthropic" } + "cost": { + "input": 0.3, + "output": 2.5 + } }, - "gpt-5.2": { - "id": "gpt-5.2", - "name": "GPT-5.2", + "gpt-5.2-chat-latest": { + "id": "gpt-5.2-chat-latest", + "name": "GPT-5.2 Chat Latest", "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-09-30", + "release_date": "2026-01-01", + "last_updated": "2026-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai" } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14 + } }, - "gpt-5.1": { - "id": "gpt-5.1", - "name": "GPT-5.1", + "gpt-5.3-codex-xhigh": { + "id": "gpt-5.3-codex-xhigh", + "name": "GPT-5.3 Codex XHigh", "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.07, "output": 8.5, "cache_read": 0.107 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai" } - }, - "gemini-3-pro": { - "id": "gemini-3-pro", - "name": "Gemini 3 Pro", - "family": "gemini-pro", + "knowledge": "2025-08-31", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14 + } + }, + "grok-code-fast-1": { + "id": "grok-code-fast-1", + "name": "Grok Code Fast 1", + "family": "grok", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, + "release_date": "2025-09-01", + "last_updated": "2025-09-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, + "limit": { + "context": 256000, + "output": 16384 + }, "cost": { - "input": 2, - "output": 12, - "cache_read": 0.2, - "context_over_200k": { "input": 4, "output": 18, "cache_read": 0.4 } + "input": 0.2, + "output": 1.5 + } + }, + "gpt-5.1": { + "id": "gpt-5.1", + "name": "GPT-5.1", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] }, - "limit": { "context": 1048576, "output": 65536 }, - "status": "deprecated", - "provider": { "npm": "@ai-sdk/google" } + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, - "glm-5": { - "id": "glm-5", - "name": "GLM-5", - "family": "glm", + "o3-mini": { + "id": "o3-mini", + "name": "o3-mini", + "family": "o-mini", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2024-12-20", + "last_updated": "2025-01-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4 + } + }, + "grok-4-0709": { + "id": "grok-4-0709", + "name": "Grok 4", + "family": "grok", + "attachment": true, + "reasoning": true, + "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1, "output": 3.2, "cache_read": 0.2 }, - "limit": { "context": 204800, "output": 131072 } + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 16384 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "gpt-5-nano": { - "id": "gpt-5-nano", - "name": "GPT-5 Nano", - "family": "gpt-nano", + "route-llm": { + "id": "route-llm", + "name": "Route LLM", + "family": "gpt", "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2024-01-01", + "last_updated": "2024-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 3, + "output": 15 + } + }, + "qwen-2.5-coder-32b": { + "id": "qwen-2.5-coder-32b", + "name": "Qwen 2.5 Coder 32B", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-11-11", + "last_updated": "2024-11-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.79, + "output": 0.79 + } + }, + "gpt-5-codex": { + "id": "gpt-5-codex", + "name": "GPT-5 Codex", + "family": "gpt", + "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-09-30", + "release_date": "2025-09-15", + "last_updated": "2025-09-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai" } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, - "nemotron-3-super-free": { - "id": "nemotron-3-super-free", - "name": "Nemotron 3 Super Free", - "family": "nemotron-free", - "attachment": false, + "claude-opus-4-1-20250805": { + "id": "claude-opus-4-1-20250805", + "name": "Claude Opus 4.1", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2026-02", - "release_date": "2026-03-11", - "last_updated": "2026-03-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0 }, - "limit": { "context": 1000000, "output": 128000 } + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75 + } }, - "claude-3-5-haiku": { - "id": "claude-3-5-haiku", - "name": "Claude Haiku 3.5", + "gpt-5.4": { + "id": "gpt-5.4", + "name": "GPT-5.4", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 2.5, + "output": 15 + } + }, + "gpt-5.1-chat-latest": { + "id": "gpt-5.1-chat-latest", + "name": "GPT-5.1 Chat Latest", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10 + } + }, + "claude-haiku-4-5-20251001": { + "id": "claude-haiku-4-5-20251001", + "name": "Claude Haiku 4.5", "family": "claude-haiku", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-07-31", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.8, "output": 4, "cache_read": 0.08, "cache_write": 1 }, - "limit": { "context": 200000, "output": 8192 }, - "provider": { "npm": "@ai-sdk/anthropic" } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5 + } }, - "minimax-m2.5-free": { - "id": "minimax-m2.5-free", - "name": "MiniMax M2.5 Free", - "family": "minimax-free", - "attachment": false, + "claude-sonnet-4-20250514": { + "id": "claude-sonnet-4-20250514", + "name": "Claude Sonnet 4", + "family": "claude-sonnet", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0 }, - "limit": { "context": 204800, "output": 131072 }, - "provider": { "npm": "@ai-sdk/anthropic" } + "release_date": "2025-05-14", + "last_updated": "2025-05-14", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "trinity-large-preview-free": { - "id": "trinity-large-preview-free", - "name": "Trinity Large Preview", - "family": "trinity", + "kimi-k2-turbo-preview": { + "id": "kimi-k2-turbo-preview", + "name": "Kimi K2 Turbo Preview", + "family": "kimi", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2026-01-28", - "last_updated": "2026-01-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 131072 }, - "status": "deprecated" + "release_date": "2025-07-08", + "last_updated": "2025-07-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 8192 + }, + "cost": { + "input": 0.15, + "output": 8 + } }, - "glm-4.7": { - "id": "glm-4.7", - "name": "GLM-4.7", - "family": "glm", - "attachment": false, + "claude-opus-4-6": { + "id": "claude-opus-4-6", + "name": "Claude Opus 4.6", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.2, "cache_read": 0.1 }, - "limit": { "context": 204800, "output": 131072 }, - "status": "deprecated" + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25 + } }, - "gemini-3.1-pro": { - "id": "gemini-3.1-pro", - "name": "Gemini 3.1 Pro Preview", - "family": "gemini-pro", + "gpt-4.1-nano": { + "id": "gpt-4.1-nano", + "name": "GPT-4.1 Nano", + "family": "gpt", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-02-19", - "last_updated": "2026-02-19", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { - "input": 2, - "output": 12, - "cache_read": 0.2, - "context_over_200k": { "input": 4, "output": 18, "cache_read": 0.4 } + "limit": { + "context": 1047576, + "output": 32768 }, - "limit": { "context": 1048576, "output": 65536 }, - "provider": { "npm": "@ai-sdk/google" } + "cost": { + "input": 0.1, + "output": 0.4 + } }, - "claude-sonnet-4-5": { - "id": "claude-sonnet-4-5", - "name": "Claude Sonnet 4.5", + "claude-3-7-sonnet-20250219": { + "id": "claude-3-7-sonnet-20250219", + "name": "Claude Sonnet 3.7", "family": "claude-sonnet", "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": true, "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2024-10-31", + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, "cost": { "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75, - "context_over_200k": { "input": 6, "output": 22.5, "cache_read": 0.6, "cache_write": 7.5 } - }, - "limit": { "context": 1000000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/anthropic" } + "output": 15 + } }, - "glm-4.7-free": { - "id": "glm-4.7-free", - "name": "GLM-4.7 Free", - "family": "glm-free", - "attachment": false, + "o3": { + "id": "o3", + "name": "o3", + "family": "o", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0 }, - "limit": { "context": 204800, "output": 131072 }, - "status": "deprecated" + "temperature": false, + "knowledge": "2024-05", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 2, + "output": 8 + } }, - "gpt-5-codex": { - "id": "gpt-5-codex", - "name": "GPT-5 Codex", - "family": "gpt-codex", + "gpt-5": { + "id": "gpt-5", + "name": "GPT-5", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": false, "knowledge": "2024-09-30", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.07, "output": 8.5, "cache_read": 0.107 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai" } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, - "claude-haiku-4-5": { - "id": "claude-haiku-4-5", - "name": "Claude Haiku 4.5", - "family": "claude-haiku", + "claude-opus-4-20250514": { + "id": "claude-opus-4-20250514", + "name": "Claude Opus 4", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-02-28", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-05-14", + "last_updated": "2025-05-14", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75 + } + }, + "gpt-4.1": { + "id": "gpt-4.1", + "name": "GPT-4.1", + "family": "gpt", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 8 + } + }, + "gpt-4.1-mini": { + "id": "gpt-4.1-mini", + "name": "GPT-4.1 Mini", + "family": "gpt", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/anthropic" } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 1.6 + } }, "gpt-5.1-codex": { "id": "gpt-5.1-codex", "name": "GPT-5.1 Codex", - "family": "gpt-codex", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, @@ -12157,4330 +18142,6128 @@ "knowledge": "2024-09-30", "release_date": "2025-11-13", "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.07, "output": 8.5, "cache_read": 0.107 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai" } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, - "minimax-m2.5": { - "id": "minimax-m2.5", - "name": "MiniMax M2.5", - "family": "minimax", - "attachment": false, - "reasoning": true, + "gpt-4o-2024-11-20": { + "id": "gpt-4o-2024-11-20", + "name": "GPT-4o (2024-11-20)", + "family": "gpt", + "attachment": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.06 }, - "limit": { "context": 204800, "output": 131072 } + "knowledge": "2024-10", + "release_date": "2024-11-20", + "last_updated": "2024-11-20", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10 + } }, - "gemini-3-flash": { - "id": "gemini-3-flash", - "name": "Gemini 3 Flash", - "family": "gemini-flash", + "grok-4-fast-non-reasoning": { + "id": "grok-4-fast-non-reasoning", + "name": "Grok 4 Fast (Non-Reasoning)", + "family": "grok", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 3, "cache_read": 0.05 }, - "limit": { "context": 1048576, "output": 65536 }, - "provider": { "npm": "@ai-sdk/google" } + "limit": { + "context": 2000000, + "output": 16384 + }, + "cost": { + "input": 0.2, + "output": 0.5 + } }, - "mimo-v2-pro-free": { - "id": "mimo-v2-pro-free", - "name": "MiMo V2 Pro Free", - "family": "mimo-pro-free", - "attachment": true, + "deepseek/deepseek-v3.1": { + "id": "deepseek/deepseek-v3.1", + "name": "DeepSeek V3.1", + "family": "deepseek", + "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2024-12", - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0 }, - "limit": { "context": 1048576, "output": 64000 } - } - } - }, - "berget": { - "id": "berget", - "env": ["BERGET_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.berget.ai/v1", - "name": "Berget.AI", - "doc": "https://api.berget.ai", - "models": { - "openai/gpt-oss-120b": { - "id": "openai/gpt-oss-120b", - "name": "GPT-OSS-120B", - "family": "gpt-oss", + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.55, + "output": 1.66 + } + }, + "Qwen/QwQ-32B": { + "id": "Qwen/QwQ-32B", + "name": "QwQ 32B", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-08", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-11-28", + "last_updated": "2024-11-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 0.9 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 0.4 + } }, - "zai-org/GLM-4.7": { - "id": "zai-org/GLM-4.7", - "name": "GLM 4.7", - "family": "glm", + "Qwen/Qwen3-235B-A22B-Instruct-2507": { + "id": "Qwen/Qwen3-235B-A22B-Instruct-2507", + "name": "Qwen3 235B A22B Instruct", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-12", - "release_date": "2026-01-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-07-01", + "last_updated": "2025-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.7, "output": 2.3 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 262144, + "output": 8192 + }, + "cost": { + "input": 0.13, + "output": 0.6 + } }, - "meta-llama/Llama-3.3-70B-Instruct": { - "id": "meta-llama/Llama-3.3-70B-Instruct", - "name": "Llama 3.3 70B Instruct", - "family": "llama", + "Qwen/Qwen3-32B": { + "id": "Qwen/Qwen3-32B", + "name": "Qwen3 32B", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2025-04-27", - "last_updated": "2025-04-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.9, "output": 0.9 }, - "limit": { "context": 128000, "output": 8192 } - }, - "intfloat/multilingual-e5-large-instruct": { - "id": "intfloat/multilingual-e5-large-instruct", - "name": "Multilingual-E5-large-instruct", - "family": "text-embedding", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "knowledge": "2025-04", - "release_date": "2025-04-27", - "last_updated": "2025-04-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-04-29", + "last_updated": "2025-04-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.02, "output": 0 }, - "limit": { "context": 512, "output": 1024 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.09, + "output": 0.29 + } }, - "intfloat/multilingual-e5-large": { - "id": "intfloat/multilingual-e5-large", - "name": "Multilingual-E5-large", - "family": "text-embedding", + "Qwen/qwen3-coder-480b-a35b-instruct": { + "id": "Qwen/qwen3-coder-480b-a35b-instruct", + "name": "Qwen3 Coder 480B A35B Instruct", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "knowledge": "2025-09", - "release_date": "2025-09-11", - "last_updated": "2025-09-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-07-22", + "last_updated": "2025-07-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.02, "output": 0 }, - "limit": { "context": 512, "output": 1024 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.29, + "output": 1.2 + } }, - "KBLab/kb-whisper-large": { - "id": "KBLab/kb-whisper-large", - "name": "KB-Whisper-Large", - "family": "whisper", + "Qwen/Qwen2.5-72B-Instruct": { + "id": "Qwen/Qwen2.5-72B-Instruct", + "name": "Qwen 2.5 72B Instruct", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, - "temperature": false, - "knowledge": "2025-04", - "release_date": "2025-04-27", - "last_updated": "2025-04-27", - "modalities": { "input": ["audio"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "release_date": "2024-09-19", + "last_updated": "2024-09-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 3, "output": 3 }, - "limit": { "context": 480000, "output": 4800 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.11, + "output": 0.38 + } }, - "BAAI/bge-reranker-v2-m3": { - "id": "BAAI/bge-reranker-v2-m3", - "name": "bge-reranker-v2-m3", - "family": "bge", + "zai-org/glm-4.7": { + "id": "zai-org/glm-4.7", + "name": "GLM-4.7", + "family": "glm", "attachment": false, "reasoning": false, - "tool_call": false, - "temperature": false, - "knowledge": "2025-04", - "release_date": "2025-04-23", - "last_updated": "2025-04-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.1 }, - "limit": { "context": 512, "output": 512 } - }, - "mistralai/Mistral-Small-3.2-24B-Instruct-2506": { - "id": "mistralai/Mistral-Small-3.2-24B-Instruct-2506", - "name": "Mistral Small 3.2 24B Instruct 2506", - "family": "mistral-small", - "attachment": false, - "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-10-01", - "last_updated": "2025-10-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-06-01", + "last_updated": "2025-06-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 0.3 }, - "limit": { "context": 32000, "output": 8192 } - } - } - }, - "lucidquery": { - "id": "lucidquery", - "env": ["LUCIDQUERY_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://lucidquery.com/api/v1", - "name": "LucidQuery AI", - "doc": "https://lucidquery.com/api/docs", - "models": { - "lucidquery-nexus-coder": { - "id": "lucidquery-nexus-coder", - "name": "LucidQuery Nexus Coder", - "family": "lucid", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2025-08-01", - "release_date": "2025-09-01", - "last_updated": "2025-09-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 5 }, - "limit": { "context": 250000, "output": 60000 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.6, + "output": 2.2 + } }, - "lucidnova-rf1-100b": { - "id": "lucidnova-rf1-100b", - "name": "LucidNova RF1 100B", - "family": "nova", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2025-09-16", - "release_date": "2024-12-28", - "last_updated": "2025-09-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 5 }, - "limit": { "context": 120000, "output": 8000 } - } - } - }, - "zhipuai": { - "id": "zhipuai", - "env": ["ZHIPU_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://open.bigmodel.cn/api/paas/v4", - "name": "Zhipu AI", - "doc": "https://docs.z.ai/guides/overview/pricing", - "models": { - "glm-5": { - "id": "glm-5", + "zai-org/glm-5": { + "id": "zai-org/glm-5", "name": "GLM-5", "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, "release_date": "2026-02-11", "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1, "output": 3.2, "cache_read": 0.2, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3.2 + } }, - "glm-4.7": { - "id": "glm-4.7", - "name": "GLM-4.7", + "zai-org/glm-4.5": { + "id": "zai-org/glm-4.5", + "name": "GLM-4.5", "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.2, "cache_read": 0.11, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.6, + "output": 2.2 + } }, - "glm-4.5v": { - "id": "glm-4.5v", - "name": "GLM-4.5V", + "zai-org/glm-4.6": { + "id": "zai-org/glm-4.6", + "name": "GLM-4.6", "family": "glm", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-08-11", - "last_updated": "2025-08-11", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 1.8 }, - "limit": { "context": 64000, "output": 16384 } - }, - "glm-4.5-air": { - "id": "glm-4.5-air", - "name": "GLM-4.5-Air", - "family": "glm-air", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-03-01", + "last_updated": "2025-03-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 1.1, "cache_read": 0.03, "cache_write": 0 }, - "limit": { "context": 131072, "output": 98304 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.6, + "output": 2.2 + } }, - "glm-4.5-flash": { - "id": "glm-4.5-flash", - "name": "GLM-4.5-Flash", - "family": "glm-flash", + "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo": { + "id": "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo", + "name": "Llama 3.1 405B Instruct Turbo", + "family": "llama", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 131072, "output": 98304 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 3.5, + "output": 3.5 + } }, - "glm-4.6v": { - "id": "glm-4.6v", - "name": "GLM-4.6V", - "family": "glm", + "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8": { + "id": "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8", + "name": "Llama 4 Maverick 17B 128E Instruct FP8", + "family": "llama", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-08", - "last_updated": "2025-12-08", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2024-08", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 0.9 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 1000000, + "output": 32768 + }, + "cost": { + "input": 0.14, + "output": 0.59 + } }, - "glm-4.6": { - "id": "glm-4.6", - "name": "GLM-4.6", - "family": "glm", + "meta-llama/Meta-Llama-3.1-8B-Instruct": { + "id": "meta-llama/Meta-Llama-3.1-8B-Instruct", + "name": "Llama 3.1 8B Instruct", + "family": "llama", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.2, "cache_read": 0.11, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.02, + "output": 0.05 + } }, - "glm-4.7-flashx": { - "id": "glm-4.7-flashx", - "name": "GLM-4.7-FlashX", - "family": "glm-flash", + "deepseek-ai/DeepSeek-R1": { + "id": "deepseek-ai/DeepSeek-R1", + "name": "DeepSeek R1", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-01-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.07, "output": 0.4, "cache_read": 0.01, "cache_write": 0 }, - "limit": { "context": 200000, "output": 131072 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 7 + } }, - "glm-4.5": { - "id": "glm-4.5", - "name": "GLM-4.5", - "family": "glm", + "deepseek-ai/DeepSeek-V3.2": { + "id": "deepseek-ai/DeepSeek-V3.2", + "name": "DeepSeek V3.2", + "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-06-15", + "last_updated": "2025-06-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.2, "cache_read": 0.11, "cache_write": 0 }, - "limit": { "context": 131072, "output": 98304 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.27, + "output": 0.4 + } }, - "glm-4.7-flash": { - "id": "glm-4.7-flash", - "name": "GLM-4.7-Flash", - "family": "glm-flash", + "deepseek-ai/DeepSeek-V3.1-Terminus": { + "id": "deepseek-ai/DeepSeek-V3.1-Terminus", + "name": "DeepSeek V3.1 Terminus", + "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-01-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-06-01", + "last_updated": "2025-06-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 200000, "output": 131072 } - } - } - }, - "nvidia": { - "id": "nvidia", - "env": ["NVIDIA_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://integrate.api.nvidia.com/v1", - "name": "Nvidia", - "doc": "https://docs.api.nvidia.com/nim/", - "models": { + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.27, + "output": 1 + } + }, "openai/gpt-oss-120b": { "id": "openai/gpt-oss-120b", - "name": "GPT-OSS-120B", + "name": "GPT-OSS 120B", "family": "gpt-oss", "attachment": true, "reasoning": true, - "tool_call": false, - "temperature": true, - "knowledge": "2025-08", - "release_date": "2025-08-04", - "last_updated": "2025-08-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 8192 } - }, - "openai/whisper-large-v3": { - "id": "openai/whisper-large-v3", - "name": "Whisper Large v3", - "family": "whisper", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "knowledge": "2023-09", - "release_date": "2023-09-01", - "last_updated": "2025-09-05", - "modalities": { "input": ["audio"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 0, "output": 4096 } - }, - "microsoft/phi-3-small-8k-instruct": { - "id": "microsoft/phi-3-small-8k-instruct", - "name": "Phi 3 Small 8k Instruct", - "attachment": true, - "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-05-07", - "last_updated": "2024-05-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 8000, "output": 4096 } - }, - "microsoft/phi-3-vision-128k-instruct": { - "id": "microsoft/phi-3-vision-128k-instruct", - "name": "Phi 3 Vision 128k Instruct", + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.08, + "output": 0.44 + } + } + } + }, + "perplexity-agent": { + "id": "perplexity-agent", + "env": ["PERPLEXITY_API_KEY"], + "npm": "@ai-sdk/openai", + "api": "https://api.perplexity.ai/v1", + "name": "Perplexity Agent", + "doc": "https://docs.perplexity.ai/docs/agent-api/models", + "models": { + "perplexity/sonar": { + "id": "perplexity/sonar", + "name": "Sonar", + "family": "sonar", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-05-19", - "last_updated": "2024-05-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "knowledge": "2025-09-01", + "release_date": "2024-01-01", + "last_updated": "2025-09-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.25, + "output": 2.5, + "cache_read": 0.0625 + } }, - "microsoft/phi-3-medium-4k-instruct": { - "id": "microsoft/phi-3-medium-4k-instruct", - "name": "Phi 3 Medium 4k Instruct", + "xai/grok-4-1-fast-non-reasoning": { + "id": "xai/grok-4-1-fast-non-reasoning", + "name": "Grok 4.1 Fast (Non-Reasoning)", + "family": "grok", "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-05-07", - "last_updated": "2024-05-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 4000, "output": 4096 } + "knowledge": "2025-07", + "release_date": "2025-11-19", + "last_updated": "2025-11-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "microsoft/phi-3.5-vision-instruct": { - "id": "microsoft/phi-3.5-vision-instruct", - "name": "Phi 3.5 Vision Instruct", + "nvidia/nemotron-3-super-120b-a12b": { + "id": "nvidia/nemotron-3-super-120b-a12b", + "name": "Nemotron 3 Super 120B", + "family": "nemotron", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-08-16", - "last_updated": "2024-08-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2026-02", + "release_date": "2026-03-11", + "last_updated": "2026-03-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 1000000, + "output": 32000 + }, + "cost": { + "input": 0.25, + "output": 2.5 + } }, - "microsoft/phi-3-medium-128k-instruct": { - "id": "microsoft/phi-3-medium-128k-instruct", - "name": "Phi 3 Medium 128k Instruct", + "openai/gpt-5.5": { + "id": "openai/gpt-5.5", + "name": "GPT-5.5", + "family": "gpt", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-05-07", - "last_updated": "2024-05-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "temperature": false, + "knowledge": "2025-12-01", + "release_date": "2026-04-23", + "last_updated": "2026-04-23", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5 + } }, - "microsoft/phi-3.5-moe-instruct": { - "id": "microsoft/phi-3.5-moe-instruct", - "name": "Phi 3.5 Moe Instruct", - "attachment": false, - "reasoning": false, + "openai/gpt-5-mini": { + "id": "openai/gpt-5-mini", + "name": "GPT-5 Mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2024-08-17", - "last_updated": "2024-08-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.025 + } }, - "microsoft/phi-3-small-128k-instruct": { - "id": "microsoft/phi-3-small-128k-instruct", - "name": "Phi 3 Small 128k Instruct", + "openai/gpt-5.2": { + "id": "openai/gpt-5.2", + "name": "GPT-5.2", + "family": "gpt", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-05-07", - "last_updated": "2024-05-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "microsoft/phi-4-mini-instruct": { - "id": "microsoft/phi-4-mini-instruct", - "name": "Phi-4-Mini", - "family": "phi", + "openai/gpt-5.1": { + "id": "openai/gpt-5.1", + "name": "GPT-5.1", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-12", - "release_date": "2024-12-01", - "last_updated": "2025-09-05", - "modalities": { "input": ["text", "image", "audio"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "nvidia/llama3-chatqa-1.5-70b": { - "id": "nvidia/llama3-chatqa-1.5-70b", - "name": "Llama3 Chatqa 1.5 70b", - "attachment": false, - "reasoning": false, + "openai/gpt-5.4": { + "id": "openai/gpt-5.4", + "name": "GPT-5.4", + "family": "gpt", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2024-04-28", - "last_updated": "2024-04-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 2.5, + "output": 15, + "cache_read": 0.25 + } }, - "nvidia/llama-3.1-nemotron-70b-instruct": { - "id": "nvidia/llama-3.1-nemotron-70b-instruct", - "name": "Llama 3.1 Nemotron 70b Instruct", - "attachment": false, - "reasoning": false, + "google/gemini-3.1-pro-preview": { + "id": "google/gemini-3.1-pro-preview", + "name": "Gemini 3.1 Pro Preview", + "family": "gemini-pro", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2024-10-12", - "last_updated": "2024-10-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } - }, - "nvidia/llama-3.3-nemotron-super-49b-v1.5": { - "id": "nvidia/llama-3.3-nemotron-super-49b-v1.5", - "name": "Llama 3.3 Nemotron Super 49b V1.5", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, "temperature": true, - "release_date": "2025-03-16", - "last_updated": "2025-03-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-02-19", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2, + "tiers": [ + { + "input": 4, + "output": 18, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 18, + "cache_read": 0.4 + } + } }, - "nvidia/nemotron-4-340b-instruct": { - "id": "nvidia/nemotron-4-340b-instruct", - "name": "Nemotron 4 340b Instruct", - "attachment": false, - "reasoning": false, + "google/gemini-3-flash-preview": { + "id": "google/gemini-3-flash-preview", + "name": "Gemini 3 Flash Preview", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-06-13", - "last_updated": "2024-06-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.05, + "tiers": [ + { + "input": 0.5, + "output": 3, + "cache_read": 0.05, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 0.5, + "output": 3, + "cache_read": 0.05 + } + } }, - "nvidia/cosmos-nemotron-34b": { - "id": "nvidia/cosmos-nemotron-34b", - "name": "Cosmos Nemotron 34B", - "family": "nemotron", - "attachment": false, + "google/gemini-2.5-pro": { + "id": "google/gemini-2.5-pro", + "name": "Gemini 2.5 Pro", + "family": "gemini-pro", + "attachment": true, "reasoning": true, - "tool_call": false, + "tool_call": true, "temperature": true, - "knowledge": "2024-01", - "release_date": "2024-01-01", - "last_updated": "2025-09-05", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125, + "tiers": [ + { + "input": 2.5, + "output": 15, + "cache_read": 0.25, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 2.5, + "output": 15, + "cache_read": 0.25 + } + } }, - "nvidia/nemotron-3-super-120b-a12b": { - "id": "nvidia/nemotron-3-super-120b-a12b", - "name": "Nemotron 3 Super", - "family": "nemotron", - "attachment": false, + "google/gemini-2.5-flash": { + "id": "google/gemini-2.5-flash", + "name": "Gemini 2.5 Flash", + "family": "gemini-flash", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2026-03-11", - "last_updated": "2026-03-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 262144, "output": 262144 } + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.03 + } }, - "nvidia/nvidia-nemotron-nano-9b-v2": { - "id": "nvidia/nvidia-nemotron-nano-9b-v2", - "name": "nvidia-nemotron-nano-9b-v2", - "family": "nemotron", - "attachment": false, + "anthropic/claude-haiku-4-5": { + "id": "anthropic/claude-haiku-4-5", + "name": "Claude Haiku 4.5", + "family": "claude-haiku", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-09", - "release_date": "2025-08-18", - "last_updated": "2025-08-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 131072 } + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1 + } }, - "nvidia/llama-3.1-nemotron-51b-instruct": { - "id": "nvidia/llama-3.1-nemotron-51b-instruct", - "name": "Llama 3.1 Nemotron 51b Instruct", - "attachment": false, - "reasoning": false, + "anthropic/claude-sonnet-4-6": { + "id": "anthropic/claude-sonnet-4-6", + "name": "Claude Sonnet 4.6", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-09-22", - "last_updated": "2024-09-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-02-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3 + } }, - "nvidia/nemoretriever-ocr-v1": { - "id": "nvidia/nemoretriever-ocr-v1", - "name": "NeMo Retriever OCR v1", - "family": "nemoretriever", - "attachment": false, - "reasoning": false, - "tool_call": false, + "anthropic/claude-opus-4-7": { + "id": "anthropic/claude-opus-4-7", + "name": "Claude Opus 4.7", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, "temperature": false, - "knowledge": "2024-01", - "release_date": "2024-01-01", - "last_updated": "2025-09-05", - "modalities": { "input": ["image"], "output": ["text"] }, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 0, "output": 4096 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5 + } }, - "nvidia/llama-embed-nemotron-8b": { - "id": "nvidia/llama-embed-nemotron-8b", - "name": "Llama Embed Nemotron 8B", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "knowledge": "2025-03", - "release_date": "2025-03-18", - "last_updated": "2025-03-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "anthropic/claude-opus-4-5": { + "id": "anthropic/claude-opus-4-5", + "name": "Claude Opus 4.5", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-11-24", + "last_updated": "2025-11-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 32768, "output": 2048 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5 + } }, - "nvidia/llama-3.3-nemotron-super-49b-v1": { - "id": "nvidia/llama-3.3-nemotron-super-49b-v1", - "name": "Llama 3.3 Nemotron Super 49b V1", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, + "anthropic/claude-opus-4-6": { + "id": "anthropic/claude-opus-4-6", + "name": "Claude Opus 4.6", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-03-16", - "last_updated": "2025-03-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5 + } }, - "nvidia/parakeet-tdt-0.6b-v2": { - "id": "nvidia/parakeet-tdt-0.6b-v2", - "name": "Parakeet TDT 0.6B v2", - "family": "parakeet", + "anthropic/claude-sonnet-4-5": { + "id": "anthropic/claude-sonnet-4-5", + "name": "Claude Sonnet 4.5", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3 + } + } + } + }, + "siliconflow-cn": { + "id": "siliconflow-cn", + "env": ["SILICONFLOW_CN_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.siliconflow.cn/v1", + "name": "SiliconFlow (China)", + "doc": "https://cloud.siliconflow.com/models", + "models": { + "Kwaipilot/KAT-Dev": { + "id": "Kwaipilot/KAT-Dev", + "name": "Kwaipilot/KAT-Dev", + "family": "kat-coder", "attachment": false, "reasoning": false, - "tool_call": false, - "temperature": false, - "knowledge": "2024-01", - "release_date": "2024-01-01", - "last_updated": "2025-09-05", - "modalities": { "input": ["audio"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-09-27", + "last_updated": "2026-01-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 0, "output": 4096 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.2, + "output": 0.6 + } }, - "nvidia/nemotron-3-nano-30b-a3b": { - "id": "nvidia/nemotron-3-nano-30b-a3b", - "name": "nemotron-3-nano-30b-a3b", - "family": "nemotron", + "Qwen/Qwen3.5-397B-A17B": { + "id": "Qwen/Qwen3.5-397B-A17B", + "name": "Qwen/Qwen3.5-397B-A17B", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-09", - "release_date": "2024-12", - "last_updated": "2024-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2026-02-16", + "last_updated": "2026-02-16", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.29, + "output": 1.74 + } }, - "nvidia/llama-3.1-nemotron-ultra-253b-v1": { - "id": "nvidia/llama-3.1-nemotron-ultra-253b-v1", - "name": "Llama-3.1-Nemotron-Ultra-253B-v1", - "family": "llama", + "Qwen/Qwen3.5-35B-A3B": { + "id": "Qwen/Qwen3.5-35B-A3B", + "name": "Qwen/Qwen3.5-35B-A3B", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2024-07-01", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 8192 } + "knowledge": "2025-04", + "release_date": "2026-02-25", + "last_updated": "2026-02-25", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.23, + "output": 1.86 + } }, - "z-ai/glm5": { - "id": "z-ai/glm5", - "name": "GLM5", - "family": "glm", + "Qwen/Qwen3.5-122B-A10B": { + "id": "Qwen/Qwen3.5-122B-A10B", + "name": "Qwen/Qwen3.5-122B-A10B", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2026-02-26", + "last_updated": "2026-02-26", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 202752, "output": 131000 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.29, + "output": 2.32 + } }, - "z-ai/glm4.7": { - "id": "z-ai/glm4.7", - "name": "GLM-4.7", - "family": "glm", + "Qwen/Qwen3.5-9B": { + "id": "Qwen/Qwen3.5-9B", + "name": "Qwen/Qwen3.5-9B", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-03", + "last_updated": "2026-03-03", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.22, + "output": 1.74 + } }, - "stepfun-ai/step-3.5-flash": { - "id": "stepfun-ai/step-3.5-flash", - "name": "Step 3.5 Flash", + "Qwen/Qwen3.5-27B": { + "id": "Qwen/Qwen3.5-27B", + "name": "Qwen/Qwen3.5-27B", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-02-02", - "last_updated": "2026-02-02", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 16384 } - }, - "google/gemma-3-1b-it": { - "id": "google/gemma-3-1b-it", - "name": "Gemma 3 1b It", - "attachment": true, - "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-03-10", - "last_updated": "2025-03-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2026-02-25", + "last_updated": "2026-02-25", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.26, + "output": 2.09 + } }, - "google/gemma-3n-e2b-it": { - "id": "google/gemma-3n-e2b-it", - "name": "Gemma 3n E2b It", - "attachment": true, - "reasoning": false, + "Qwen/Qwen3.5-4B": { + "id": "Qwen/Qwen3.5-4B", + "name": "Qwen/Qwen3.5-4B", + "family": "qwen", + "attachment": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2024-06", - "release_date": "2025-06-12", - "last_updated": "2025-06-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2026-03-03", + "last_updated": "2026-03-03", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "google/gemma-3-27b-it": { - "id": "google/gemma-3-27b-it", - "name": "Gemma-3-27B-IT", - "family": "gemma", - "attachment": true, + "Qwen/Qwen3.6-35B-A3B": { + "id": "Qwen/Qwen3.6-35B-A3B", + "name": "Qwen/Qwen3.6-35B-A3B", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2024-12-01", - "last_updated": "2025-09-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 8192 } + "knowledge": "2025-04", + "release_date": "2026-04-17", + "last_updated": "2026-04-17", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.23, + "output": 1.86 + } }, - "google/gemma-3n-e4b-it": { - "id": "google/gemma-3n-e4b-it", - "name": "Gemma 3n E4b It", - "attachment": true, + "Qwen/Qwen2.5-72B-Instruct": { + "id": "Qwen/Qwen2.5-72B-Instruct", + "name": "Qwen/Qwen2.5-72B-Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2024-06", - "release_date": "2025-06-03", - "last_updated": "2025-06-03", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } - }, - "google/codegemma-1.1-7b": { - "id": "google/codegemma-1.1-7b", - "name": "Codegemma 1.1 7b", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2024-04-30", - "last_updated": "2024-04-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } - }, - "google/codegemma-7b": { - "id": "google/codegemma-7b", - "name": "Codegemma 7b", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2024-03-21", - "last_updated": "2024-03-21", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2024-09-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 33000, + "output": 4000 + }, + "cost": { + "input": 0.59, + "output": 0.59 + } }, - "google/gemma-2-2b-it": { - "id": "google/gemma-2-2b-it", - "name": "Gemma 2 2b It", + "Qwen/Qwen3-Coder-480B-A35B-Instruct": { + "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct", + "name": "Qwen/Qwen3-Coder-480B-A35B-Instruct", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2024-07-16", - "last_updated": "2024-07-16", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2025-07-31", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.25, + "output": 1 + } }, - "google/gemma-2-27b-it": { - "id": "google/gemma-2-27b-it", - "name": "Gemma 2 27b It", - "attachment": false, + "Qwen/Qwen3-VL-8B-Instruct": { + "id": "Qwen/Qwen3-VL-8B-Instruct", + "name": "Qwen/Qwen3-VL-8B-Instruct", + "family": "qwen", + "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2024-06-24", - "last_updated": "2024-06-24", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2025-10-15", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.18, + "output": 0.68 + } }, - "google/gemma-3-12b-it": { - "id": "google/gemma-3-12b-it", - "name": "Gemma 3 12b It", - "attachment": false, + "Qwen/Qwen3-VL-32B-Instruct": { + "id": "Qwen/Qwen3-VL-32B-Instruct", + "name": "Qwen/Qwen3-VL-32B-Instruct", + "family": "qwen", + "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-03-01", - "last_updated": "2025-03-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2025-10-21", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.2, + "output": 0.6 + } }, - "minimaxai/minimax-m2.1": { - "id": "minimaxai/minimax-m2.1", - "name": "MiniMax-M2.1", - "family": "minimax", - "attachment": false, + "Qwen/Qwen3-VL-30B-A3B-Thinking": { + "id": "Qwen/Qwen3-VL-30B-A3B-Thinking", + "name": "Qwen/Qwen3-VL-30B-A3B-Thinking", + "family": "qwen", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 204800, "output": 131072 } + "release_date": "2025-10-11", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.29, + "output": 1 + } }, - "minimaxai/minimax-m2.5": { - "id": "minimaxai/minimax-m2.5", - "name": "MiniMax-M2.5", - "family": "minimax", + "Qwen/Qwen2.5-14B-Instruct": { + "id": "Qwen/Qwen2.5-14B-Instruct", + "name": "Qwen/Qwen2.5-14B-Instruct", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-08", - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 204800, "output": 131072 } + "release_date": "2024-09-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 33000, + "output": 4000 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "deepseek-ai/deepseek-v3.1-terminus": { - "id": "deepseek-ai/deepseek-v3.1-terminus", - "name": "DeepSeek V3.1 Terminus", - "family": "deepseek", - "attachment": false, - "reasoning": true, + "Qwen/Qwen3-VL-235B-A22B-Instruct": { + "id": "Qwen/Qwen3-VL-235B-A22B-Instruct", + "name": "Qwen/Qwen3-VL-235B-A22B-Instruct", + "family": "qwen", + "attachment": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-22", - "last_updated": "2025-09-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-10-04", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.3, + "output": 1.5 + } }, - "deepseek-ai/deepseek-r1": { - "id": "deepseek-ai/deepseek-r1", - "name": "Deepseek R1", + "Qwen/Qwen3-Next-80B-A3B-Thinking": { + "id": "Qwen/Qwen3-Next-80B-A3B-Thinking", + "name": "Qwen/Qwen3-Next-80B-A3B-Thinking", + "family": "qwen", "attachment": false, "reasoning": true, - "tool_call": false, - "structured_output": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2025-09-25", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.14, + "output": 0.57 + } }, - "deepseek-ai/deepseek-coder-6.7b-instruct": { - "id": "deepseek-ai/deepseek-coder-6.7b-instruct", - "name": "Deepseek Coder 6.7b Instruct", - "attachment": false, + "Qwen/Qwen2.5-VL-32B-Instruct": { + "id": "Qwen/Qwen2.5-VL-32B-Instruct", + "name": "Qwen/Qwen2.5-VL-32B-Instruct", + "family": "qwen", + "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2023-10-29", - "last_updated": "2023-10-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2025-03-24", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.27, + "output": 0.27 + } }, - "deepseek-ai/deepseek-v3.1": { - "id": "deepseek-ai/deepseek-v3.1", - "name": "DeepSeek V3.1", - "family": "deepseek", - "attachment": false, + "Qwen/Qwen3-Omni-30B-A3B-Thinking": { + "id": "Qwen/Qwen3-Omni-30B-A3B-Thinking", + "name": "Qwen/Qwen3-Omni-30B-A3B-Thinking", + "family": "qwen", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-08-20", - "last_updated": "2025-08-26", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-10-04", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 66000, + "output": 66000 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } }, - "deepseek-ai/deepseek-r1-0528": { - "id": "deepseek-ai/deepseek-r1-0528", - "name": "Deepseek R1 0528", + "Qwen/Qwen3-235B-A22B-Thinking-2507": { + "id": "Qwen/Qwen3-235B-A22B-Thinking-2507", + "name": "Qwen/Qwen3-235B-A22B-Thinking-2507", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-05-28", - "last_updated": "2025-05-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2025-07-28", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.13, + "output": 0.6 + } }, - "deepseek-ai/deepseek-v3.2": { - "id": "deepseek-ai/deepseek-v3.2", - "name": "DeepSeek V3.2", - "family": "deepseek", + "Qwen/Qwen2.5-32B-Instruct": { + "id": "Qwen/Qwen2.5-32B-Instruct", + "name": "Qwen/Qwen2.5-32B-Instruct", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-09-19", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 163840, "output": 65536 } - }, - "qwen/qwq-32b": { - "id": "qwen/qwq-32b", - "name": "Qwq 32b", - "attachment": false, - "reasoning": true, - "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2025-03-05", - "last_updated": "2025-03-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 33000, + "output": 4000 + }, + "cost": { + "input": 0.18, + "output": 0.18 + } }, - "qwen/qwen2.5-coder-7b-instruct": { - "id": "qwen/qwen2.5-coder-7b-instruct", - "name": "Qwen2.5 Coder 7b Instruct", + "Qwen/Qwen2.5-72B-Instruct-128K": { + "id": "Qwen/Qwen2.5-72B-Instruct-128K", + "name": "Qwen/Qwen2.5-72B-Instruct-128K", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2024-09-17", - "last_updated": "2024-09-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2024-09-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 4000 + }, + "cost": { + "input": 0.59, + "output": 0.59 + } }, - "qwen/qwen2.5-coder-32b-instruct": { - "id": "qwen/qwen2.5-coder-32b-instruct", - "name": "Qwen2.5 Coder 32b Instruct", + "Qwen/Qwen3-14B": { + "id": "Qwen/Qwen3-14B", + "name": "Qwen/Qwen3-14B", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2024-11-06", - "last_updated": "2024-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2025-04-30", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.07, + "output": 0.28 + } }, - "qwen/qwen3-coder-480b-a35b-instruct": { - "id": "qwen/qwen3-coder-480b-a35b-instruct", - "name": "Qwen3 Coder 480B A35B Instruct", + "Qwen/Qwen3-Omni-30B-A3B-Instruct": { + "id": "Qwen/Qwen3-Omni-30B-A3B-Instruct", + "name": "Qwen/Qwen3-Omni-30B-A3B-Instruct", "family": "qwen", - "attachment": false, + "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-10-04", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 66536 } + "limit": { + "context": 66000, + "output": 66000 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } }, - "qwen/qwen3-next-80b-a3b-thinking": { - "id": "qwen/qwen3-next-80b-a3b-thinking", - "name": "Qwen3-Next-80B-A3B-Thinking", + "Qwen/Qwen3-Coder-30B-A3B-Instruct": { + "id": "Qwen/Qwen3-Coder-30B-A3B-Instruct", + "name": "Qwen/Qwen3-Coder-30B-A3B-Instruct", "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2024-12-01", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 16384 } + "release_date": "2025-08-01", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.07, + "output": 0.28 + } }, - "qwen/qwen3-next-80b-a3b-instruct": { - "id": "qwen/qwen3-next-80b-a3b-instruct", - "name": "Qwen3-Next-80B-A3B-Instruct", + "Qwen/Qwen3-32B": { + "id": "Qwen/Qwen3-32B", + "name": "Qwen/Qwen3-32B", "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2024-12-01", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-04-30", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 16384 } + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.14, + "output": 0.57 + } }, - "qwen/qwen3-235b-a22b": { - "id": "qwen/qwen3-235b-a22b", - "name": "Qwen3-235B-A22B", + "Qwen/Qwen3-235B-A22B-Instruct-2507": { + "id": "Qwen/Qwen3-235B-A22B-Instruct-2507", + "name": "Qwen/Qwen3-235B-A22B-Instruct-2507", "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2024-12-01", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-07-23", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.09, + "output": 0.6 + } }, - "qwen/qwen3.5-397b-a17b": { - "id": "qwen/qwen3.5-397b-a17b", - "name": "Qwen3.5-397B-A17B", + "Qwen/Qwen3-30B-A3B-Instruct-2507": { + "id": "Qwen/Qwen3-30B-A3B-Instruct-2507", + "name": "Qwen/Qwen3-30B-A3B-Instruct-2507", "family": "qwen", - "attachment": true, - "reasoning": true, + "attachment": false, + "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2026-01", - "release_date": "2026-02-16", - "last_updated": "2026-02-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 8192 } + "release_date": "2025-07-30", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.09, + "output": 0.3 + } }, - "black-forest-labs/flux.1-dev": { - "id": "black-forest-labs/flux.1-dev", - "name": "FLUX.1-dev", - "family": "flux", + "Qwen/Qwen3-8B": { + "id": "Qwen/Qwen3-8B", + "name": "Qwen/Qwen3-8B", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-08", - "release_date": "2024-08-01", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["image"] }, + "release_date": "2025-04-30", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 4096, "output": 0 } + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.06, + "output": 0.06 + } }, - "moonshotai/kimi-k2-thinking": { - "id": "moonshotai/kimi-k2-thinking", - "name": "Kimi K2 Thinking", - "family": "kimi-thinking", + "Qwen/Qwen3-Next-80B-A3B-Instruct": { + "id": "Qwen/Qwen3-Next-80B-A3B-Instruct", + "name": "Qwen/Qwen3-Next-80B-A3B-Instruct", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": true, "structured_output": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-11", - "last_updated": "2025-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 262144, "output": 262144 } + "release_date": "2025-09-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.14, + "output": 1.4 + } }, - "moonshotai/kimi-k2.5": { - "id": "moonshotai/kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi", + "Qwen/Qwen3-VL-8B-Thinking": { + "id": "Qwen/Qwen3-VL-8B-Thinking", + "name": "Qwen/Qwen3-VL-8B-Thinking", + "family": "qwen", "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "structured_output": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 262144 } + "release_date": "2025-10-15", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.18, + "output": 2 + } }, - "moonshotai/kimi-k2-instruct-0905": { - "id": "moonshotai/kimi-k2-instruct-0905", - "name": "Kimi K2 0905", - "family": "kimi", - "attachment": false, + "Qwen/Qwen3-Omni-30B-A3B-Captioner": { + "id": "Qwen/Qwen3-Omni-30B-A3B-Captioner", + "name": "Qwen/Qwen3-Omni-30B-A3B-Captioner", + "family": "qwen", + "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 262144 } + "release_date": "2025-10-04", + "last_updated": "2025-11-25", + "modalities": { + "input": ["audio"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 66000, + "output": 66000 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } }, - "moonshotai/kimi-k2-instruct": { - "id": "moonshotai/kimi-k2-instruct", - "name": "Kimi K2 Instruct", - "family": "kimi", + "Qwen/QwQ-32B": { + "id": "Qwen/QwQ-32B", + "name": "Qwen/QwQ-32B", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-01", - "release_date": "2025-01-01", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-03-06", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.15, + "output": 0.58 + } }, - "meta/codellama-70b": { - "id": "meta/codellama-70b", - "name": "Codellama 70b", - "attachment": false, + "Qwen/Qwen3-VL-30B-A3B-Instruct": { + "id": "Qwen/Qwen3-VL-30B-A3B-Instruct", + "name": "Qwen/Qwen3-VL-30B-A3B-Instruct", + "family": "qwen", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-01-29", - "last_updated": "2024-01-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2025-10-05", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.29, + "output": 1 + } }, - "meta/llama-3.3-70b-instruct": { - "id": "meta/llama-3.3-70b-instruct", - "name": "Llama 3.3 70b Instruct", + "Qwen/Qwen2.5-Coder-32B-Instruct": { + "id": "Qwen/Qwen2.5-Coder-32B-Instruct", + "name": "Qwen/Qwen2.5-Coder-32B-Instruct", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2024-11-26", - "last_updated": "2024-11-26", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2024-11-11", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 33000, + "output": 4000 + }, + "cost": { + "input": 0.18, + "output": 0.18 + } }, - "meta/llama-3.2-1b-instruct": { - "id": "meta/llama-3.2-1b-instruct", - "name": "Llama 3.2 1b Instruct", + "Qwen/Qwen2.5-7B-Instruct": { + "id": "Qwen/Qwen2.5-7B-Instruct", + "name": "Qwen/Qwen2.5-7B-Instruct", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2023-12", "release_date": "2024-09-18", - "last_updated": "2024-09-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 33000, + "output": 4000 + }, + "cost": { + "input": 0.05, + "output": 0.05 + } }, - "meta/llama-3.1-405b-instruct": { - "id": "meta/llama-3.1-405b-instruct", - "name": "Llama 3.1 405b Instruct", - "attachment": false, - "reasoning": false, + "Qwen/Qwen3-VL-235B-A22B-Thinking": { + "id": "Qwen/Qwen3-VL-235B-A22B-Thinking", + "name": "Qwen/Qwen3-VL-235B-A22B-Thinking", + "family": "qwen", + "attachment": true, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2024-07-16", - "last_updated": "2024-07-16", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2025-10-04", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.45, + "output": 3.5 + } }, - "meta/llama3-8b-instruct": { - "id": "meta/llama3-8b-instruct", - "name": "Llama3 8b Instruct", + "Qwen/Qwen3-30B-A3B-Thinking-2507": { + "id": "Qwen/Qwen3-30B-A3B-Thinking-2507", + "name": "Qwen/Qwen3-30B-A3B-Thinking-2507", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2024-04-17", - "last_updated": "2024-04-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2025-07-31", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 131000 + }, + "cost": { + "input": 0.09, + "output": 0.3 + } }, - "meta/llama-4-scout-17b-16e-instruct": { - "id": "meta/llama-4-scout-17b-16e-instruct", - "name": "Llama 4 Scout 17b 16e Instruct", + "Qwen/Qwen3-VL-32B-Thinking": { + "id": "Qwen/Qwen3-VL-32B-Thinking", + "name": "Qwen/Qwen3-VL-32B-Thinking", + "family": "qwen", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2024-02", - "release_date": "2025-04-02", - "last_updated": "2025-04-02", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2025-10-21", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.2, + "output": 1.5 + } }, - "meta/llama-3.2-11b-vision-instruct": { - "id": "meta/llama-3.2-11b-vision-instruct", - "name": "Llama 3.2 11b Vision Instruct", + "Qwen/Qwen2.5-VL-72B-Instruct": { + "id": "Qwen/Qwen2.5-VL-72B-Instruct", + "name": "Qwen/Qwen2.5-VL-72B-Instruct", + "family": "qwen", "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-09-18", - "last_updated": "2024-09-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2025-01-28", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 4000 + }, + "cost": { + "input": 0.59, + "output": 0.59 + } }, - "meta/llama3-70b-instruct": { - "id": "meta/llama3-70b-instruct", - "name": "Llama3 70b Instruct", + "stepfun-ai/Step-3.5-Flash": { + "id": "stepfun-ai/Step-3.5-Flash", + "name": "stepfun-ai/Step-3.5-Flash", + "family": "step", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2024-04-17", - "last_updated": "2024-04-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } }, - "meta/llama-4-maverick-17b-128e-instruct": { - "id": "meta/llama-4-maverick-17b-128e-instruct", - "name": "Llama 4 Maverick 17b 128e Instruct", + "zai-org/GLM-4.5V": { + "id": "zai-org/GLM-4.5V", + "name": "zai-org/GLM-4.5V", + "family": "glm", "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2024-02", - "release_date": "2025-04-01", - "last_updated": "2025-04-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2025-08-13", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 66000, + "output": 66000 + }, + "cost": { + "input": 0.14, + "output": 0.86 + } }, - "meta/llama-3.1-70b-instruct": { - "id": "meta/llama-3.1-70b-instruct", - "name": "Llama 3.1 70b Instruct", + "zai-org/GLM-4.6": { + "id": "zai-org/GLM-4.6", + "name": "zai-org/GLM-4.6", + "family": "glm", "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2024-07-16", - "last_updated": "2024-07-16", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2025-10-04", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 205000, + "output": 205000 + }, + "cost": { + "input": 0.5, + "output": 1.9 + } }, - "mistralai/devstral-2-123b-instruct-2512": { - "id": "mistralai/devstral-2-123b-instruct-2512", - "name": "Devstral-2-123B-Instruct-2512", - "family": "devstral", + "zai-org/GLM-4.6V": { + "id": "zai-org/GLM-4.6V", + "name": "zai-org/GLM-4.6V", + "family": "glm", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-12", - "release_date": "2025-12-08", - "last_updated": "2025-12-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 262144 } + "release_date": "2025-12-07", + "last_updated": "2025-12-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.3, + "output": 0.9 + } }, - "mistralai/mistral-large-3-675b-instruct-2512": { - "id": "mistralai/mistral-large-3-675b-instruct-2512", - "name": "Mistral Large 3 675B Instruct 2512", - "family": "mistral-large", - "attachment": true, + "zai-org/GLM-4.5-Air": { + "id": "zai-org/GLM-4.5-Air", + "name": "zai-org/GLM-4.5-Air", + "family": "glm-air", + "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-12-02", - "last_updated": "2025-12-02", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 262144 } + "release_date": "2025-07-28", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.14, + "output": 0.86 + } }, - "mistralai/mistral-large-2-instruct": { - "id": "mistralai/mistral-large-2-instruct", - "name": "Mistral Large 2 Instruct", + "inclusionAI/Ling-flash-2.0": { + "id": "inclusionAI/Ling-flash-2.0", + "name": "inclusionAI/Ling-flash-2.0", + "family": "ling", "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2024-07-24", - "last_updated": "2024-07-24", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2025-09-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.14, + "output": 0.57 + } }, - "mistralai/ministral-14b-instruct-2512": { - "id": "mistralai/ministral-14b-instruct-2512", - "name": "Ministral 3 14B Instruct 2512", - "family": "ministral", - "attachment": true, + "inclusionAI/Ling-mini-2.0": { + "id": "inclusionAI/Ling-mini-2.0", + "name": "inclusionAI/Ling-mini-2.0", + "family": "ling", + "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-12", - "release_date": "2025-12-01", - "last_updated": "2025-12-08", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 262144 } + "release_date": "2025-09-10", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.07, + "output": 0.28 + } }, - "mistralai/mamba-codestral-7b-v0.1": { - "id": "mistralai/mamba-codestral-7b-v0.1", - "name": "Mamba Codestral 7b V0.1", + "inclusionAI/Ring-flash-2.0": { + "id": "inclusionAI/Ring-flash-2.0", + "name": "inclusionAI/Ring-flash-2.0", + "family": "ring", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-07-16", - "last_updated": "2024-07-16", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2025-09-29", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.14, + "output": 0.57 + } }, - "mistralai/codestral-22b-instruct-v0.1": { - "id": "mistralai/codestral-22b-instruct-v0.1", - "name": "Codestral 22b Instruct V0.1", + "ascend-tribe/pangu-pro-moe": { + "id": "ascend-tribe/pangu-pro-moe", + "name": "ascend-tribe/pangu-pro-moe", + "family": "pangu", "attachment": false, - "reasoning": false, - "tool_call": true, + "reasoning": true, + "tool_call": false, "structured_output": true, "temperature": true, - "release_date": "2024-05-29", - "last_updated": "2024-05-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2025-07-02", + "last_updated": "2026-01-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.2, + "output": 0.6 + } }, - "mistralai/mistral-small-3.1-24b-instruct-2503": { - "id": "mistralai/mistral-small-3.1-24b-instruct-2503", - "name": "Mistral Small 3.1 24b Instruct 2503", + "tencent/Hunyuan-MT-7B": { + "id": "tencent/Hunyuan-MT-7B", + "name": "tencent/Hunyuan-MT-7B", + "family": "hunyuan", "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-03-11", - "last_updated": "2025-03-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } - } - } - }, - "nebius": { - "id": "nebius", - "env": ["NEBIUS_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.tokenfactory.nebius.com/v1", - "name": "Nebius Token Factory", - "doc": "https://docs.tokenfactory.nebius.com/", - "models": { - "openai/gpt-oss-120b": { - "id": "openai/gpt-oss-120b", - "name": "gpt-oss-120b", + "release_date": "2025-09-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 33000, + "output": 33000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "tencent/Hunyuan-A13B-Instruct": { + "id": "tencent/Hunyuan-A13B-Instruct", + "name": "tencent/Hunyuan-A13B-Instruct", + "family": "hunyuan", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "knowledge": "2025-09", - "release_date": "2026-01-10", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.6, "reasoning": 0.6, "cache_read": 0.015, "cache_write": 0.18 }, - "limit": { "context": 128000, "input": 124000, "output": 8192 } + "release_date": "2025-06-30", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.14, + "output": 0.57 + } }, - "openai/gpt-oss-20b": { - "id": "openai/gpt-oss-20b", - "name": "gpt-oss-20b", + "Pro/zai-org/GLM-4.7": { + "id": "Pro/zai-org/GLM-4.7", + "name": "Pro/zai-org/GLM-4.7", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "knowledge": "2025-09", - "release_date": "2026-01-10", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.05, "output": 0.2, "cache_read": 0.005, "cache_write": 0.06 }, - "limit": { "context": 128000, "input": 124000, "output": 4096 } + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 205000, + "output": 205000 + }, + "cost": { + "input": 0.6, + "output": 2.2 + } }, - "NousResearch/Hermes-4-405B": { - "id": "NousResearch/Hermes-4-405B", - "name": "Hermes-4-405B", + "Pro/zai-org/GLM-5.1": { + "id": "Pro/zai-org/GLM-5.1", + "name": "Pro/zai-org/GLM-5.1", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "knowledge": "2025-11", - "release_date": "2026-01-30", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-08", + "last_updated": "2026-04-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1, "output": 3, "reasoning": 3, "cache_read": 0.1, "cache_write": 1.25 }, - "limit": { "context": 128000, "input": 120000, "output": 8192 } + "limit": { + "context": 205000, + "output": 205000 + }, + "cost": { + "input": 1.4, + "output": 4.4, + "cache_write": 0 + } }, - "NousResearch/Hermes-4-70B": { - "id": "NousResearch/Hermes-4-70B", - "name": "Hermes-4-70B", + "Pro/zai-org/GLM-5": { + "id": "Pro/zai-org/GLM-5", + "name": "Pro/zai-org/GLM-5", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "knowledge": "2025-11", - "release_date": "2026-01-30", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.13, "output": 0.4, "reasoning": 0.4, "cache_read": 0.013, "cache_write": 0.16 }, - "limit": { "context": 128000, "input": 120000, "output": 8192 } + "limit": { + "context": 205000, + "output": 205000 + }, + "cost": { + "input": 1, + "output": 3.2 + } }, - "zai-org/GLM-4.5-Air": { - "id": "zai-org/GLM-4.5-Air", - "name": "GLM-4.5-Air", + "Pro/deepseek-ai/DeepSeek-V3": { + "id": "Pro/deepseek-ai/DeepSeek-V3", + "name": "Pro/deepseek-ai/DeepSeek-V3", + "family": "deepseek", "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-11-15", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-12-26", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 1.2, "cache_read": 0.02, "cache_write": 0.25 }, - "limit": { "context": 128000, "input": 124000, "output": 4096 } + "limit": { + "context": 164000, + "output": 164000 + }, + "cost": { + "input": 0.25, + "output": 1 + } }, - "zai-org/GLM-4.5": { - "id": "zai-org/GLM-4.5", - "name": "GLM-4.5", + "Pro/deepseek-ai/DeepSeek-R1": { + "id": "Pro/deepseek-ai/DeepSeek-R1", + "name": "Pro/deepseek-ai/DeepSeek-R1", + "family": "deepseek-thinking", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-11-15", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-05-28", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 2.2, "cache_read": 0.06, "cache_write": 0.75 }, - "limit": { "context": 128000, "input": 124000, "output": 4096 } + "limit": { + "context": 164000, + "output": 164000 + }, + "cost": { + "input": 0.5, + "output": 2.18 + } }, - "zai-org/GLM-5": { - "id": "zai-org/GLM-5", - "name": "GLM-5", + "Pro/deepseek-ai/DeepSeek-V3.2": { + "id": "Pro/deepseek-ai/DeepSeek-V3.2", + "name": "Pro/deepseek-ai/DeepSeek-V3.2", + "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "knowledge": "2026-01", - "release_date": "2026-03-01", - "last_updated": "2026-03-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-03", + "last_updated": "2025-12-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 3.2, "cache_read": 0.1, "cache_write": 1 }, - "limit": { "context": 200000, "input": 200000, "output": 16384 } + "limit": { + "context": 164000, + "output": 164000 + }, + "cost": { + "input": 0.27, + "output": 0.42 + } }, - "zai-org/GLM-4.7-FP8": { - "id": "zai-org/GLM-4.7-FP8", - "name": "GLM-4.7 (FP8)", + "Pro/deepseek-ai/DeepSeek-V3.1-Terminus": { + "id": "Pro/deepseek-ai/DeepSeek-V3.1-Terminus", + "name": "Pro/deepseek-ai/DeepSeek-V3.1-Terminus", + "family": "deepseek", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-12", - "release_date": "2026-01-15", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-09-29", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 2, "cache_read": 0.04, "cache_write": 0.5 }, - "limit": { "context": 128000, "input": 124000, "output": 4096 } + "limit": { + "context": 164000, + "output": 164000 + }, + "cost": { + "input": 0.27, + "output": 1 + } }, - "nvidia/Nemotron-Nano-V2-12b": { - "id": "nvidia/Nemotron-Nano-V2-12b", - "name": "Nemotron-Nano-V2-12b", + "Pro/moonshotai/Kimi-K2-Thinking": { + "id": "Pro/moonshotai/Kimi-K2-Thinking", + "name": "Pro/moonshotai/Kimi-K2-Thinking", + "family": "kimi-thinking", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-03-15", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.07, "output": 0.2, "cache_read": 0.007, "cache_write": 0.08 }, - "limit": { "context": 32000, "input": 30000, "output": 4096 } + "release_date": "2025-11-07", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.55, + "output": 2.5 + } }, - "nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B": { - "id": "nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B", - "name": "Nemotron-3-Nano-30B-A3B", + "Pro/moonshotai/Kimi-K2.6": { + "id": "Pro/moonshotai/Kimi-K2.6", + "name": "Pro/moonshotai/Kimi-K2.6", + "family": "kimi", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "knowledge": "2025-05", - "release_date": "2025-08-10", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.06, "output": 0.24, "cache_read": 0.006, "cache_write": 0.075 }, - "limit": { "context": 32000, "input": 30000, "output": 4096 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.95, + "output": 4, + "cache_read": 0.16 + } }, - "nvidia/nemotron-3-super-120b-a12b": { - "id": "nvidia/nemotron-3-super-120b-a12b", - "name": "Nemotron-3-Super-120B-A12B", + "Pro/moonshotai/Kimi-K2-Instruct-0905": { + "id": "Pro/moonshotai/Kimi-K2-Instruct-0905", + "name": "Pro/moonshotai/Kimi-K2-Instruct-0905", + "family": "kimi", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2026-02", - "release_date": "2026-03-11", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 0.9 }, - "limit": { "context": 256000, "input": 256000, "output": 32768 } + "release_date": "2025-09-08", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.4, + "output": 2 + } }, - "nvidia/Llama-3_1-Nemotron-Ultra-253B-v1": { - "id": "nvidia/Llama-3_1-Nemotron-Ultra-253B-v1", - "name": "Llama-3.1-Nemotron-Ultra-253B-v1", + "Pro/moonshotai/Kimi-K2.5": { + "id": "Pro/moonshotai/Kimi-K2.5", + "name": "Pro/moonshotai/Kimi-K2.5", + "family": "kimi", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-01-15", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 1.8, "cache_read": 0.06, "cache_write": 0.75 }, - "limit": { "context": 128000, "input": 120000, "output": 4096 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.45, + "output": 2.25 + } }, - "google/gemma-3-27b-it": { - "id": "google/gemma-3-27b-it", - "name": "Gemma-3-27b-it", - "attachment": true, + "Pro/MiniMaxAI/MiniMax-M2.5": { + "id": "Pro/MiniMaxAI/MiniMax-M2.5", + "name": "Pro/MiniMaxAI/MiniMax-M2.5", + "family": "minimax", + "attachment": false, "reasoning": false, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "knowledge": "2025-10", - "release_date": "2026-01-20", - "last_updated": "2026-02-04", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.3, "cache_read": 0.01, "cache_write": 0.125 }, - "limit": { "context": 110000, "input": 100000, "output": 8192 } + "release_date": "2026-02-13", + "last_updated": "2026-02-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 192000, + "output": 131000 + }, + "cost": { + "input": 0.3, + "output": 1.22 + } }, - "google/gemma-3-27b-it-fast": { - "id": "google/gemma-3-27b-it-fast", - "name": "Gemma-3-27b-it (Fast)", - "attachment": true, + "Pro/MiniMaxAI/MiniMax-M2.1": { + "id": "Pro/MiniMaxAI/MiniMax-M2.1", + "name": "Pro/MiniMaxAI/MiniMax-M2.1", + "family": "minimax", + "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-10", - "release_date": "2026-01-20", - "last_updated": "2026-02-04", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.6, "cache_read": 0.02, "cache_write": 0.25 }, - "limit": { "context": 110000, "input": 100000, "output": 8192 } + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 197000, + "output": 131000 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "google/gemma-2-2b-it": { - "id": "google/gemma-2-2b-it", - "name": "Gemma-2-2b-it", - "attachment": false, + "PaddlePaddle/PaddleOCR-VL": { + "id": "PaddlePaddle/PaddleOCR-VL", + "name": "PaddlePaddle/PaddleOCR-VL", + "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": false, "temperature": true, - "knowledge": "2024-06", - "release_date": "2024-07-31", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-10-16", + "last_updated": "2025-10-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.02, "output": 0.06, "cache_read": 0.002, "cache_write": 0.025 }, - "limit": { "context": 8192, "input": 8000, "output": 4096 } + "limit": { + "context": 16384, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "google/gemma-2-9b-it-fast": { - "id": "google/gemma-2-9b-it-fast", - "name": "Gemma-2-9b-it (Fast)", - "attachment": false, + "PaddlePaddle/PaddleOCR-VL-1.5": { + "id": "PaddlePaddle/PaddleOCR-VL-1.5", + "name": "PaddlePaddle/PaddleOCR-VL-1.5", + "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": false, - "temperature": true, - "knowledge": "2024-06", - "release_date": "2024-06-27", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.03, "output": 0.09, "cache_read": 0.003, "cache_write": 0.0375 }, - "limit": { "context": 8192, "input": 8000, "output": 4096 } - }, - "PrimeIntellect/INTELLECT-3": { - "id": "PrimeIntellect/INTELLECT-3", - "name": "INTELLECT-3", - "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-10", - "release_date": "2026-01-25", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01-29", + "last_updated": "2026-01-29", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 1.1, "cache_read": 0.02, "cache_write": 0.25 }, - "limit": { "context": 128000, "input": 120000, "output": 8192 } + "limit": { + "context": 16384, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "meta-llama/Llama-Guard-3-8B": { - "id": "meta-llama/Llama-Guard-3-8B", - "name": "Llama-Guard-3-8B", - "attachment": false, + "deepseek-ai/DeepSeek-OCR": { + "id": "deepseek-ai/DeepSeek-OCR", + "name": "deepseek-ai/DeepSeek-OCR", + "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": true, - "temperature": false, - "knowledge": "2024-04", - "release_date": "2024-04-18", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "release_date": "2025-10-20", + "last_updated": "2025-10-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.02, "output": 0.06, "cache_read": 0.002, "cache_write": 0.025 }, - "limit": { "context": 8192, "input": 8000, "output": 1024 } + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "meta-llama/Llama-3.3-70B-Instruct": { - "id": "meta-llama/Llama-3.3-70B-Instruct", - "name": "Llama-3.3-70B-Instruct", + "deepseek-ai/DeepSeek-V3.1-Terminus": { + "id": "deepseek-ai/DeepSeek-V3.1-Terminus", + "name": "deepseek-ai/DeepSeek-V3.1-Terminus", + "family": "deepseek", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-08", - "release_date": "2025-12-05", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.13, "output": 0.4, "cache_read": 0.013, "cache_write": 0.16 }, - "limit": { "context": 128000, "input": 120000, "output": 8192 } + "release_date": "2025-09-29", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 164000, + "output": 164000 + }, + "cost": { + "input": 0.27, + "output": 1 + } }, - "meta-llama/Meta-Llama-3.1-8B-Instruct": { - "id": "meta-llama/Meta-Llama-3.1-8B-Instruct", - "name": "Meta-Llama-3.1-8B-Instruct", + "deepseek-ai/DeepSeek-V3.2": { + "id": "deepseek-ai/DeepSeek-V3.2", + "name": "deepseek-ai/DeepSeek-V3.2", + "family": "deepseek", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2024-07-23", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.02, "output": 0.06, "cache_read": 0.002, "cache_write": 0.025 }, - "limit": { "context": 128000, "input": 120000, "output": 4096 } + "release_date": "2025-12-03", + "last_updated": "2025-12-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 164000, + "output": 164000 + }, + "cost": { + "input": 0.27, + "output": 0.42 + } }, - "meta-llama/Meta-Llama-3.1-8B-Instruct-fast": { - "id": "meta-llama/Meta-Llama-3.1-8B-Instruct-fast", - "name": "Meta-Llama-3.1-8B-Instruct (Fast)", + "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B": { + "id": "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B", + "name": "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2024-07-23", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.03, "output": 0.09, "cache_read": 0.003, "cache_write": 0.03 }, - "limit": { "context": 128000, "input": 120000, "output": 4096 } + "release_date": "2025-01-20", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "meta-llama/Llama-3.3-70B-Instruct-fast": { - "id": "meta-llama/Llama-3.3-70B-Instruct-fast", - "name": "Llama-3.3-70B-Instruct (Fast)", + "deepseek-ai/DeepSeek-R1": { + "id": "deepseek-ai/DeepSeek-R1", + "name": "deepseek-ai/DeepSeek-R1", + "family": "deepseek-thinking", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-08", - "release_date": "2025-12-05", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.25, "output": 0.75, "cache_read": 0.025, "cache_write": 0.31 }, - "limit": { "context": 128000, "input": 120000, "output": 8192 } + "release_date": "2025-05-28", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 164000, + "output": 164000 + }, + "cost": { + "input": 0.5, + "output": 2.18 + } }, - "MiniMaxAI/MiniMax-M2.1": { - "id": "MiniMaxAI/MiniMax-M2.1", - "name": "MiniMax-M2.1", + "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B": { + "id": "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", + "name": "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "knowledge": "2025-10", - "release_date": "2026-02-01", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "reasoning": 1.2, "cache_read": 0.03, "cache_write": 0.375 }, - "limit": { "context": 128000, "input": 120000, "output": 8192 } + "release_date": "2025-01-20", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.18, + "output": 0.18 + } }, - "deepseek-ai/DeepSeek-V3-0324": { - "id": "deepseek-ai/DeepSeek-V3-0324", - "name": "DeepSeek-V3-0324", + "deepseek-ai/DeepSeek-V3": { + "id": "deepseek-ai/DeepSeek-V3", + "name": "deepseek-ai/DeepSeek-V3", + "family": "deepseek", "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-03-24", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.5, "output": 1.5, "cache_read": 0.05, "cache_write": 0.1875 }, - "limit": { "context": 128000, "input": 120000, "output": 8192 } + "release_date": "2024-12-26", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 164000, + "output": 164000 + }, + "cost": { + "input": 0.25, + "output": 1 + } }, - "deepseek-ai/DeepSeek-R1-0528-fast": { - "id": "deepseek-ai/DeepSeek-R1-0528-fast", - "name": "DeepSeek R1 0528 Fast", + "deepseek-ai/deepseek-vl2": { + "id": "deepseek-ai/deepseek-vl2", + "name": "deepseek-ai/deepseek-vl2", "family": "deepseek", - "attachment": false, - "reasoning": true, + "attachment": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-01-01", - "last_updated": "2025-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2, "output": 6 }, - "limit": { "context": 131072, "output": 8192 } + "release_date": "2024-12-13", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 4000, + "output": 4000 + }, + "cost": { + "input": 0.15, + "output": 0.15 + } }, - "deepseek-ai/DeepSeek-V3-0324-fast": { - "id": "deepseek-ai/DeepSeek-V3-0324-fast", - "name": "DeepSeek-V3-0324 (Fast)", + "baidu/ERNIE-4.5-300B-A47B": { + "id": "baidu/ERNIE-4.5-300B-A47B", + "name": "baidu/ERNIE-4.5-300B-A47B", + "family": "ernie", "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-03-24", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.75, "output": 2.25, "cache_read": 0.075, "cache_write": 0.28125 }, - "limit": { "context": 128000, "input": 120000, "output": 8192 } + "release_date": "2025-07-02", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.28, + "output": 1.1 + } }, - "deepseek-ai/DeepSeek-R1-0528": { - "id": "deepseek-ai/DeepSeek-R1-0528", - "name": "DeepSeek-R1-0528", + "THUDM/GLM-Z1-32B-0414": { + "id": "THUDM/GLM-Z1-32B-0414", + "name": "THUDM/GLM-Z1-32B-0414", + "family": "glm-z", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "knowledge": "2025-11", - "release_date": "2026-01-15", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.8, "output": 2.4, "reasoning": 2.4, "cache_read": 0.08, "cache_write": 1 }, - "limit": { "context": 128000, "input": 120000, "output": 32768 } + "release_date": "2025-04-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.14, + "output": 0.57 + } }, - "deepseek-ai/DeepSeek-V3.2": { - "id": "deepseek-ai/DeepSeek-V3.2", - "name": "DeepSeek-V3.2", + "THUDM/GLM-4-32B-0414": { + "id": "THUDM/GLM-4-32B-0414", + "name": "THUDM/GLM-4-32B-0414", + "family": "glm", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "knowledge": "2025-11", - "release_date": "2026-01-20", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 0.45, "reasoning": 0.45, "cache_read": 0.03, "cache_write": 0.375 }, - "limit": { "context": 163000, "input": 160000, "output": 16384 } - }, - "intfloat/e5-mistral-7b-instruct": { - "id": "intfloat/e5-mistral-7b-instruct", - "name": "e5-mistral-7b-instruct", - "family": "text-embedding", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "temperature": false, - "knowledge": "2023-12", - "release_date": "2024-01-01", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.01, "output": 0 }, - "limit": { "context": 32768, "input": 32768, "output": 0 } - }, - "black-forest-labs/flux-dev": { - "id": "black-forest-labs/flux-dev", - "name": "FLUX.1-dev", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "temperature": false, - "knowledge": "2024-07", - "release_date": "2024-08-01", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["image"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 77, "input": 77, "output": 0 } + "release_date": "2025-04-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 33000, + "output": 33000 + }, + "cost": { + "input": 0.27, + "output": 0.27 + } }, - "black-forest-labs/flux-schnell": { - "id": "black-forest-labs/flux-schnell", - "name": "FLUX.1-schnell", + "THUDM/GLM-4-9B-0414": { + "id": "THUDM/GLM-4-9B-0414", + "name": "THUDM/GLM-4-9B-0414", + "family": "glm", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "temperature": false, - "knowledge": "2024-07", - "release_date": "2024-08-01", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["image"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 77, "input": 77, "output": 0 } - }, - "Qwen/Qwen3-Next-80B-A3B-Thinking": { - "id": "Qwen/Qwen3-Next-80B-A3B-Thinking", - "name": "Qwen3-Next-80B-A3B-Thinking", - "attachment": false, - "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "knowledge": "2025-12", - "release_date": "2026-01-28", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 1.2, "reasoning": 1.2, "cache_read": 0.015, "cache_write": 0.18 }, - "limit": { "context": 128000, "input": 120000, "output": 16384 } + "release_date": "2025-04-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 33000, + "output": 33000 + }, + "cost": { + "input": 0.086, + "output": 0.086 + } }, - "Qwen/Qwen3-30B-A3B-Thinking-2507": { - "id": "Qwen/Qwen3-30B-A3B-Thinking-2507", - "name": "Qwen3-30B-A3B-Thinking-2507", + "THUDM/GLM-Z1-9B-0414": { + "id": "THUDM/GLM-Z1-9B-0414", + "name": "THUDM/GLM-Z1-9B-0414", + "family": "glm-z", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "knowledge": "2025-12", - "release_date": "2026-01-28", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.3, "reasoning": 0.3, "cache_read": 0.01, "cache_write": 0.125 }, - "limit": { "context": 128000, "input": 120000, "output": 16384 } + "release_date": "2025-04-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.086, + "output": 0.086 + } }, - "Qwen/Qwen3-32B": { - "id": "Qwen/Qwen3-32B", - "name": "Qwen3-32B", + "moonshotai/Kimi-K2-Instruct-0905": { + "id": "moonshotai/Kimi-K2-Instruct-0905", + "name": "moonshotai/Kimi-K2-Instruct-0905", + "family": "kimi", "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-12", - "release_date": "2026-01-28", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.3, "cache_read": 0.01, "cache_write": 0.125 }, - "limit": { "context": 128000, "input": 120000, "output": 8192 } + "release_date": "2025-09-08", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.4, + "output": 2 + } }, - "Qwen/Qwen3-235B-A22B-Thinking-2507": { - "id": "Qwen/Qwen3-235B-A22B-Thinking-2507", - "name": "Qwen3 235B A22B Thinking 2507", - "family": "qwen", + "moonshotai/Kimi-K2-Thinking": { + "id": "moonshotai/Kimi-K2-Thinking", + "name": "moonshotai/Kimi-K2-Thinking", + "family": "kimi-thinking", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-07-25", - "last_updated": "2025-10-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-11-07", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 262144, "output": 8192 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.55, + "output": 2.5 + } }, - "Qwen/Qwen2.5-Coder-7B-fast": { - "id": "Qwen/Qwen2.5-Coder-7B-fast", - "name": "Qwen2.5-Coder-7B (Fast)", + "ByteDance-Seed/Seed-OSS-36B-Instruct": { + "id": "ByteDance-Seed/Seed-OSS-36B-Instruct", + "name": "ByteDance-Seed/Seed-OSS-36B-Instruct", + "family": "seed", "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2024-09", - "release_date": "2024-09-19", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.03, "output": 0.09, "cache_read": 0.003, "cache_write": 0.03 }, - "limit": { "context": 128000, "input": 120000, "output": 8192 } - }, - "Qwen/Qwen2.5-VL-72B-Instruct": { - "id": "Qwen/Qwen2.5-VL-72B-Instruct", - "name": "Qwen2.5-VL-72B-Instruct", - "attachment": true, + "release_date": "2025-09-04", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.21, + "output": 0.57 + } + } + } + }, + "submodel": { + "id": "submodel", + "env": ["SUBMODEL_INSTAGEN_ACCESS_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://llm.submodel.ai/v1", + "name": "submodel", + "doc": "https://submodel.gitbook.io", + "models": { + "Qwen/Qwen3-235B-A22B-Instruct-2507": { + "id": "Qwen/Qwen3-235B-A22B-Instruct-2507", + "name": "Qwen3 235B A22B Instruct 2507", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-01-20", - "last_updated": "2026-02-04", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-23", + "last_updated": "2025-08-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.25, "output": 0.75, "cache_read": 0.025, "cache_write": 0.31 }, - "limit": { "context": 128000, "input": 120000, "output": 8192 } + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0.2, + "output": 0.3 + } }, - "Qwen/Qwen3-32B-fast": { - "id": "Qwen/Qwen3-32B-fast", - "name": "Qwen3-32B (Fast)", + "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8": { + "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8", + "name": "Qwen3 Coder 480B A35B Instruct", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-12", - "release_date": "2026-01-28", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.6, "cache_read": 0.02, "cache_write": 0.25 }, - "limit": { "context": 128000, "input": 120000, "output": 8192 } + "release_date": "2025-08-23", + "last_updated": "2025-08-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.2, + "output": 0.8 + } }, - "Qwen/Qwen3-Coder-30B-A3B-Instruct": { - "id": "Qwen/Qwen3-Coder-30B-A3B-Instruct", - "name": "Qwen3-Coder-30B-A3B-Instruct", + "Qwen/Qwen3-235B-A22B-Thinking-2507": { + "id": "Qwen/Qwen3-235B-A22B-Thinking-2507", + "name": "Qwen3 235B A22B Thinking 2507", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-12", - "release_date": "2026-01-28", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-08-23", + "last_updated": "2025-08-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.3, "cache_read": 0.01, "cache_write": 0.125 }, - "limit": { "context": 128000, "input": 120000, "output": 8192 } + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0.2, + "output": 0.6 + } }, - "Qwen/Qwen3-Coder-480B-A35B-Instruct": { - "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct", - "name": "Qwen3 Coder 480B A35B Instruct", - "family": "qwen", + "zai-org/GLM-4.5-Air": { + "id": "zai-org/GLM-4.5-Air", + "name": "GLM 4.5 Air", + "family": "glm-air", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-10-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 1.8 }, - "limit": { "context": 262144, "output": 66536 } + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.1, + "output": 0.5 + } }, - "Qwen/Qwen3-30B-A3B-Instruct-2507": { - "id": "Qwen/Qwen3-30B-A3B-Instruct-2507", - "name": "Qwen3-30B-A3B-Instruct-2507", + "zai-org/GLM-4.5-FP8": { + "id": "zai-org/GLM-4.5-FP8", + "name": "GLM 4.5 FP8", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-12", - "release_date": "2026-01-28", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.3, "cache_read": 0.01, "cache_write": 0.125 }, - "limit": { "context": 128000, "input": 120000, "output": 8192 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.2, + "output": 0.8 + } }, - "Qwen/Qwen3-235B-A22B-Instruct-2507": { - "id": "Qwen/Qwen3-235B-A22B-Instruct-2507", - "name": "Qwen3 235B A22B Instruct 2507", - "family": "qwen", + "deepseek-ai/DeepSeek-V3.1": { + "id": "deepseek-ai/DeepSeek-V3.1", + "name": "DeepSeek V3.1", + "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-07-25", - "last_updated": "2025-10-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-08-23", + "last_updated": "2025-08-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.6 }, - "limit": { "context": 262144, "output": 8192 } - }, - "Qwen/Qwen3-Embedding-8B": { - "id": "Qwen/Qwen3-Embedding-8B", - "name": "Qwen3-Embedding-8B", - "family": "text-embedding", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "temperature": false, - "knowledge": "2025-10", - "release_date": "2026-01-10", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.01, "output": 0 }, - "limit": { "context": 32768, "input": 32768, "output": 0 } - }, - "BAAI/bge-en-icl": { - "id": "BAAI/bge-en-icl", - "name": "BGE-ICL", - "family": "text-embedding", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "temperature": false, - "knowledge": "2024-06", - "release_date": "2024-07-30", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.01, "output": 0 }, - "limit": { "context": 32768, "input": 32768, "output": 0 } + "limit": { + "context": 75000, + "output": 163840 + }, + "cost": { + "input": 0.2, + "output": 0.8 + } }, - "BAAI/bge-multilingual-gemma2": { - "id": "BAAI/bge-multilingual-gemma2", - "name": "bge-multilingual-gemma2", - "family": "text-embedding", + "deepseek-ai/DeepSeek-V3-0324": { + "id": "deepseek-ai/DeepSeek-V3-0324", + "name": "DeepSeek V3 0324", + "family": "deepseek", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "temperature": false, - "knowledge": "2024-06", - "release_date": "2024-07-30", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.01, "output": 0 }, - "limit": { "context": 8192, "input": 8192, "output": 0 } - }, - "moonshotai/Kimi-K2-Instruct": { - "id": "moonshotai/Kimi-K2-Instruct", - "name": "Kimi-K2-Instruct", - "attachment": true, - "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-10", - "release_date": "2026-01-05", - "last_updated": "2026-02-04", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-23", + "last_updated": "2025-08-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 2.4, "cache_read": 0.05, "cache_write": 0.625 }, - "limit": { "context": 200000, "input": 190000, "output": 8192 } - }, - "moonshotai/Kimi-K2.5": { - "id": "moonshotai/Kimi-K2.5", - "name": "Kimi-K2.5", - "family": "kimi", - "attachment": true, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, - "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-12-15", - "last_updated": "2026-02-04", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.5, "output": 2.5, "reasoning": 2.5, "cache_read": 0.05, "cache_write": 0.625 }, - "limit": { "context": 256000, "input": 256000, "output": 8192 } + "limit": { + "context": 75000, + "output": 163840 + }, + "cost": { + "input": 0.2, + "output": 0.8 + } }, - "moonshotai/Kimi-K2.5-fast": { - "id": "moonshotai/Kimi-K2.5-fast", - "name": "Kimi-K2.5-fast", - "family": "kimi", - "attachment": true, + "deepseek-ai/DeepSeek-R1-0528": { + "id": "deepseek-ai/DeepSeek-R1-0528", + "name": "DeepSeek R1 0528", + "family": "deepseek-thinking", + "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-12-15", - "last_updated": "2026-02-04", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.5, "output": 2.5, "cache_read": 0.05, "cache_write": 0.625 }, - "limit": { "context": 256000, "input": 256000, "output": 8192 } + "release_date": "2025-08-23", + "last_updated": "2025-08-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 75000, + "output": 163840 + }, + "cost": { + "input": 0.5, + "output": 2.15 + } }, - "moonshotai/Kimi-K2-Thinking": { - "id": "moonshotai/Kimi-K2-Thinking", - "name": "Kimi-K2-Thinking", - "attachment": true, + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "name": "GPT OSS 120B", + "family": "gpt-oss", + "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "knowledge": "2025-10", - "release_date": "2026-01-05", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-08-23", + "last_updated": "2025-08-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.5, "reasoning": 2.5, "cache_read": 0.06, "cache_write": 0.75 }, - "limit": { "context": 128000, "input": 120000, "output": 16384 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.5 + } } } }, - "togetherai": { - "id": "togetherai", - "env": ["TOGETHER_API_KEY"], - "npm": "@ai-sdk/togetherai", - "name": "Together AI", - "doc": "https://docs.together.ai/docs/serverless-models", + "minimax-coding-plan": { + "id": "minimax-coding-plan", + "env": ["MINIMAX_API_KEY"], + "npm": "@ai-sdk/anthropic", + "api": "https://api.minimax.io/anthropic/v1", + "name": "MiniMax Coding Plan (minimax.io)", + "doc": "https://platform.minimax.io/docs/coding-plan/intro", "models": { - "openai/gpt-oss-120b": { - "id": "openai/gpt-oss-120b", - "name": "GPT OSS 120B", - "family": "gpt-oss", + "MiniMax-M2": { + "id": "MiniMax-M2", + "name": "MiniMax-M2", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-08", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-10-27", + "last_updated": "2025-10-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 196608, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "zai-org/GLM-4.7": { - "id": "zai-org/GLM-4.7", - "name": "GLM-4.7", - "family": "glm", + "MiniMax-M2.5": { + "id": "MiniMax-M2.5", + "name": "MiniMax-M2.5", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-07-25", - "last_updated": "2025-07-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.45, "output": 2 }, - "limit": { "context": 200000, "output": 200000 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "zai-org/GLM-5": { - "id": "zai-org/GLM-5", - "name": "GLM-5", - "family": "glm", + "MiniMax-M2.7": { + "id": "MiniMax-M2.7", + "name": "MiniMax-M2.7", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-11", - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1, "output": 3.2 }, - "limit": { "context": 202752, "output": 131072 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "zai-org/GLM-4.6": { - "id": "zai-org/GLM-4.6", - "name": "GLM 4.6", - "family": "glm", + "MiniMax-M2.7-highspeed": { + "id": "MiniMax-M2.7-highspeed", + "name": "MiniMax-M2.7-highspeed", + "family": "minimax", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.2 }, - "limit": { "context": 200000, "output": 200000 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "meta-llama/Llama-3.3-70B-Instruct-Turbo": { - "id": "meta-llama/Llama-3.3-70B-Instruct-Turbo", - "name": "Llama 3.3 70B", - "family": "llama", + "MiniMax-M2.1": { + "id": "MiniMax-M2.1", + "name": "MiniMax-M2.1", + "family": "minimax", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.88, "output": 0.88 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "MiniMaxAI/MiniMax-M2.5": { - "id": "MiniMaxAI/MiniMax-M2.5", - "name": "MiniMax-M2.5", + "MiniMax-M2.5-highspeed": { + "id": "MiniMax-M2.5-highspeed", + "name": "MiniMax-M2.5-highspeed", "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-13", + "last_updated": "2026-02-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.06 }, - "limit": { "context": 204800, "output": 131072 } - }, - "essentialai/Rnj-1-Instruct": { - "id": "essentialai/Rnj-1-Instruct", - "name": "Rnj-1 Instruct", - "family": "rnj", - "attachment": false, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } + } + } + }, + "perplexity": { + "id": "perplexity", + "env": ["PERPLEXITY_API_KEY"], + "npm": "@ai-sdk/perplexity", + "name": "Perplexity", + "doc": "https://docs.perplexity.ai", + "models": { + "sonar-pro": { + "id": "sonar-pro", + "name": "Sonar Pro", + "family": "sonar-pro", + "attachment": true, "reasoning": false, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-12-05", - "last_updated": "2025-12-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.15 }, - "limit": { "context": 32768, "output": 32768 } + "knowledge": "2025-09-01", + "release_date": "2024-01-01", + "last_updated": "2025-09-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "deepseek-ai/DeepSeek-R1": { - "id": "deepseek-ai/DeepSeek-R1", - "name": "DeepSeek R1", - "family": "deepseek-thinking", + "sonar-deep-research": { + "id": "sonar-deep-research", + "name": "Perplexity Sonar Deep Research", "attachment": false, "reasoning": true, "tool_call": false, - "temperature": true, - "knowledge": "2024-07", - "release_date": "2024-12-26", - "last_updated": "2025-03-24", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 3, "output": 7 }, - "limit": { "context": 163839, "output": 163839 } + "temperature": false, + "knowledge": "2025-01", + "release_date": "2025-02-01", + "last_updated": "2025-09-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 8, + "reasoning": 3 + } }, - "deepseek-ai/DeepSeek-V3-1": { - "id": "deepseek-ai/DeepSeek-V3-1", - "name": "DeepSeek V3.1", - "family": "deepseek", + "sonar": { + "id": "sonar", + "name": "Sonar", + "family": "sonar", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2025-08", - "release_date": "2025-08-21", - "last_updated": "2025-08-21", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 1.7 }, - "limit": { "context": 131072, "output": 131072 } + "knowledge": "2025-09-01", + "release_date": "2024-01-01", + "last_updated": "2025-09-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 1, + "output": 1 + } }, - "deepseek-ai/DeepSeek-V3": { - "id": "deepseek-ai/DeepSeek-V3", - "name": "DeepSeek V3", - "family": "deepseek", - "attachment": false, + "sonar-reasoning-pro": { + "id": "sonar-reasoning-pro", + "name": "Sonar Reasoning Pro", + "family": "sonar-reasoning", + "attachment": true, "reasoning": true, + "tool_call": false, + "temperature": true, + "knowledge": "2025-09-01", + "release_date": "2024-01-01", + "last_updated": "2025-09-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 2, + "output": 8 + } + } + } + }, + "deepseek": { + "id": "deepseek", + "env": ["DEEPSEEK_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.deepseek.com", + "name": "DeepSeek", + "doc": "https://api-docs.deepseek.com/quick_start/pricing", + "models": { + "deepseek-chat": { + "id": "deepseek-chat", + "name": "DeepSeek Chat", + "family": "deepseek", + "attachment": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-01-20", - "last_updated": "2025-05-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-09", + "release_date": "2025-12-01", + "last_updated": "2026-02-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1.25, "output": 1.25 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 0.14, + "output": 0.28, + "cache_read": 0.028 + } }, - "Qwen/Qwen3-Coder-Next-FP8": { - "id": "Qwen/Qwen3-Coder-Next-FP8", - "name": "Qwen3 Coder Next FP8", - "family": "qwen", - "attachment": false, + "deepseek-reasoner": { + "id": "deepseek-reasoner", + "name": "DeepSeek Reasoner", + "family": "deepseek-thinking", + "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2026-02-03", - "release_date": "2026-02-03", - "last_updated": "2026-02-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-09", + "release_date": "2025-12-01", + "last_updated": "2026-02-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.5, "output": 1.2 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 0.14, + "output": 0.28, + "cache_read": 0.028 + } }, - "Qwen/Qwen3-235B-A22B-Instruct-2507-tput": { - "id": "Qwen/Qwen3-235B-A22B-Instruct-2507-tput", - "name": "Qwen3 235B A22B Instruct 2507 FP8", - "family": "qwen", + "deepseek-v4-flash": { + "id": "deepseek-v4-flash", + "name": "DeepSeek V4 Flash", + "family": "deepseek-flash", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-07-25", - "last_updated": "2025-07-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 0.6 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 0.14, + "output": 0.28, + "cache_read": 0.028 + } }, - "Qwen/Qwen3.5-397B-A17B": { - "id": "Qwen/Qwen3.5-397B-A17B", - "name": "Qwen3.5 397B A17B", - "family": "qwen", + "deepseek-v4-pro": { + "id": "deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2026-02-16", - "last_updated": "2026-02-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 3.6 }, - "limit": { "context": 262144, "output": 130000 } - }, - "Qwen/Qwen3-Next-80B-A3B-Instruct": { - "id": "Qwen/Qwen3-Next-80B-A3B-Instruct", - "name": "Qwen3-Next-80B-A3B-Instruct", - "family": "qwen", - "attachment": false, + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 1.74, + "output": 3.48, + "cache_read": 0.145 + } + } + } + }, + "llama": { + "id": "llama", + "env": ["LLAMA_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.llama.com/compat/v1/", + "name": "Llama", + "doc": "https://llama.developer.meta.com/docs/models", + "models": { + "llama-3.3-70b-instruct": { + "id": "llama-3.3-70b-instruct", + "name": "Llama-3.3-70B-Instruct", + "family": "llama", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-07-25", - "last_updated": "2025-07-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 1.5 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8": { - "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8", - "name": "Qwen3 Coder 480B A35B Instruct", - "family": "qwen", - "attachment": false, + "cerebras-llama-4-maverick-17b-128e-instruct": { + "id": "cerebras-llama-4-maverick-17b-128e-instruct", + "name": "Cerebras-Llama-4-Maverick-17B-128E-Instruct", + "family": "llama", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 2, "output": 2 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "moonshotai/Kimi-K2-Instruct": { - "id": "moonshotai/Kimi-K2-Instruct", - "name": "Kimi K2 Instruct", - "family": "kimi", - "attachment": false, + "llama-3.3-8b-instruct": { + "id": "llama-3.3-8b-instruct", + "name": "Llama-3.3-8B-Instruct", + "family": "llama", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-07-14", - "last_updated": "2025-07-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1, "output": 3 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "moonshotai/Kimi-K2.5": { - "id": "moonshotai/Kimi-K2.5", - "name": "Kimi K2.5", - "family": "kimi", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": true, - "temperature": true, - "knowledge": "2026-01", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.5, "output": 2.8 }, - "limit": { "context": 262144, "output": 262144 } - } - } - }, - "firmware": { - "id": "firmware", - "env": ["FIRMWARE_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://app.frogbot.ai/api/v1", - "name": "Firmware", - "doc": "https://docs.frogbot.ai", - "models": { - "claude-opus-4-5": { - "id": "claude-opus-4-5", - "name": "Claude Opus 4.5", - "family": "claude-opus", + "cerebras-llama-4-scout-17b-16e-instruct": { + "id": "cerebras-llama-4-scout-17b-16e-instruct", + "name": "Cerebras-Llama-4-Scout-17B-16E-Instruct", + "family": "llama", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-11-24", - "last_updated": "2025-11-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 200000, "output": 64000 } + "knowledge": "2025-01", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "kimi-k2.5": { - "id": "kimi-k2.5", - "name": "Kimi-K2.5", - "attachment": false, - "reasoning": true, + "groq-llama-4-maverick-17b-128e-instruct": { + "id": "groq-llama-4-maverick-17b-128e-instruct", + "name": "Groq-Llama-4-Maverick-17B-128E-Instruct", + "family": "llama", + "attachment": true, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "1970-01-01", - "last_updated": "1970-01-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.6, "output": 3, "cache_read": 0.1 }, - "limit": { "context": 256000, "output": 128000 } + "knowledge": "2025-01", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gemini-3-pro-preview": { - "id": "gemini-3-pro-preview", - "name": "Gemini 3 Pro Preview", - "family": "gemini-pro", + "llama-4-scout-17b-16e-instruct-fp8": { + "id": "llama-4-scout-17b-16e-instruct-fp8", + "name": "Llama-4-Scout-17B-16E-Instruct-FP8", + "family": "llama", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 12, "cache_read": 0.2 }, - "limit": { "context": 1000000, "output": 64000 } + "knowledge": "2024-08", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gpt-4o": { - "id": "gpt-4o", - "name": "GPT-4o", - "family": "gpt", + "llama-4-maverick-17b-128e-instruct-fp8": { + "id": "llama-4-maverick-17b-128e-instruct-fp8", + "name": "Llama-4-Maverick-17B-128E-Instruct-FP8", + "family": "llama", "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2023-09", - "release_date": "2024-05-13", - "last_updated": "2024-08-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 10, "cache_read": 1.25 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2024-08", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } + } + } + }, + "openrouter": { + "id": "openrouter", + "env": ["OPENROUTER_API_KEY"], + "npm": "@openrouter/ai-sdk-provider", + "api": "https://openrouter.ai/api/v1", + "name": "OpenRouter", + "doc": "https://openrouter.ai/models", + "models": { + "liquid/lfm-2.5-1.2b-instruct:free": { + "id": "liquid/lfm-2.5-1.2b-instruct:free", + "name": "LFM2.5-1.2B-Instruct (free)", + "family": "liquid", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "knowledge": "2025-06", + "release_date": "2026-01-20", + "last_updated": "2026-01-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "grok-code-fast-1": { - "id": "grok-code-fast-1", - "name": "Grok 4.1 Fast (Reasoning)", - "family": "grok", + "liquid/lfm-2.5-1.2b-thinking:free": { + "id": "liquid/lfm-2.5-1.2b-thinking:free", + "name": "LFM2.5-1.2B-Thinking (free)", + "family": "liquid", "attachment": false, "reasoning": true, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2023-10", - "release_date": "2025-08-28", - "last_updated": "2025-08-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 1.5, "cache_read": 0.02 }, - "limit": { "context": 256000, "output": 128000 } + "knowledge": "2025-06", + "release_date": "2026-01-20", + "last_updated": "2026-01-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "zai-glm-5": { - "id": "zai-glm-5", - "name": "GLM-5", - "family": "glm", - "attachment": true, - "reasoning": false, + "deepseek/deepseek-chat-v3.1": { + "id": "deepseek/deepseek-chat-v3.1", + "name": "DeepSeek-V3.1", + "family": "deepseek", + "attachment": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-01-20", - "last_updated": "2025-02-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1, "output": 3.2, "cache_read": 0.2 }, - "limit": { "context": 198000, "output": 8192 } + "knowledge": "2025-07", + "release_date": "2025-08-21", + "last_updated": "2025-08-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 163840 + }, + "cost": { + "input": 0.2, + "output": 0.8 + } }, - "gpt-5-mini": { - "id": "gpt-5-mini", - "name": "GPT-5 Mini", - "family": "gpt-mini", - "attachment": true, + "deepseek/deepseek-r1-distill-llama-70b": { + "id": "deepseek/deepseek-r1-distill-llama-70b", + "name": "DeepSeek R1 Distill Llama 70B", + "family": "deepseek-thinking", + "attachment": false, "reasoning": true, - "tool_call": true, + "tool_call": false, "structured_output": true, - "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.03 }, - "limit": { "context": 400000, "output": 128000 } + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-01-23", + "last_updated": "2025-01-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "claude-opus-4-6": { - "id": "claude-opus-4-6", - "name": "Claude Opus 4.6", - "family": "claude-opus", - "attachment": true, + "deepseek/deepseek-r1": { + "id": "deepseek/deepseek-r1", + "name": "DeepSeek: R1", + "family": "deepseek-thinking", + "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-05-31", - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 200000, "output": 128000 } + "knowledge": "2024-07", + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 64000, + "output": 16000 + }, + "cost": { + "input": 0.7, + "output": 2.5 + } }, - "deepseek-v3-2": { - "id": "deepseek-v3-2", - "name": "DeepSeek v3.2", + "deepseek/deepseek-v3.2-speciale": { + "id": "deepseek/deepseek-v3.2-speciale", + "name": "DeepSeek V3.2 Speciale", "family": "deepseek", - "attachment": true, - "reasoning": false, + "attachment": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, "knowledge": "2024-07", - "release_date": "2024-12-26", - "last_updated": "2025-09-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.58, "output": 1.68, "cache_read": 0.28 }, - "limit": { "context": 128000, "output": 8192 } + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 65536 + }, + "cost": { + "input": 0.27, + "output": 0.41 + } }, - "claude-sonnet-4-6": { - "id": "claude-sonnet-4-6", - "name": "Claude Sonnet 4.6", - "family": "claude-sonnet", - "attachment": true, + "deepseek/deepseek-v3.2": { + "id": "deepseek/deepseek-v3.2", + "name": "DeepSeek V3.2", + "family": "deepseek", + "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2026-02-17", - "release_date": "2026-02-17", - "last_updated": "2026-02-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "knowledge": "2024-07", + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 65536 + }, + "cost": { + "input": 0.28, + "output": 0.4 + } }, - "gpt-oss-120b": { - "id": "gpt-oss-120b", - "name": "GPT OSS 120B", - "family": "gpt-oss", + "deepseek/deepseek-v3.1-terminus:exacto": { + "id": "deepseek/deepseek-v3.1-terminus:exacto", + "name": "DeepSeek V3.1 Terminus (exacto)", + "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "1970-01-01", - "last_updated": "1970-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-09-22", + "last_updated": "2025-09-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.27, + "output": 1 + } }, - "minimax-m2-5": { - "id": "minimax-m2-5", - "name": "MiniMax-M2.5", - "family": "minimax", - "attachment": true, + "deepseek/deepseek-chat-v3-0324": { + "id": "deepseek/deepseek-chat-v3-0324", + "name": "DeepSeek V3 0324", + "family": "deepseek", + "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, + "structured_output": true, "temperature": true, - "knowledge": "2024-09", - "release_date": "2025-01-15", - "last_updated": "2025-02-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.03 }, - "limit": { "context": 192000, "output": 8192 } + "knowledge": "2024-10", + "release_date": "2025-03-24", + "last_updated": "2025-03-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 16384, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "grok-4-1-fast-non-reasoning": { - "id": "grok-4-1-fast-non-reasoning", - "name": "Grok 4.1 Fast (Non-Reasoning)", - "family": "grok", - "attachment": true, - "reasoning": false, + "deepseek/deepseek-v3.1-terminus": { + "id": "deepseek/deepseek-v3.1-terminus", + "name": "DeepSeek V3.1 Terminus", + "family": "deepseek", + "attachment": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-11", - "release_date": "2025-11-25", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 128000 } + "knowledge": "2025-07", + "release_date": "2025-09-22", + "last_updated": "2025-09-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.27, + "output": 1 + } }, - "gpt-5-4": { - "id": "gpt-5-4", - "name": "GPT-5.4", - "family": "gpt", - "attachment": true, + "openrouter/owl-alpha": { + "id": "openrouter/owl-alpha", + "name": "Owl Alpha", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "temperature": true, + "release_date": "2026-04-28", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 15, "cache_read": 0.25 }, - "limit": { "context": 272000, "output": 128000 } + "limit": { + "context": 1048756, + "output": 262144 + }, + "status": "alpha", + "cost": { + "input": 0, + "output": 0 + } }, - "gemini-3-flash-preview": { - "id": "gemini-3-flash-preview", - "name": "Gemini 3 Flash Preview", - "family": "gemini-flash", + "openrouter/pareto-code": { + "id": "openrouter/pareto-code", + "name": "Pareto Code Router", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 3, "cache_read": 0.05 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 200000, + "output": 200000 + } }, - "gemini-3-1-pro-preview": { - "id": "gemini-3-1-pro-preview", - "name": "Gemini 3.1 Pro Preview", - "family": "gemini-pro", - "attachment": true, + "openrouter/elephant-alpha": { + "id": "openrouter/elephant-alpha", + "name": "Elephant (free)", + "family": "elephant", + "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2026-01", - "release_date": "2026-02-18", - "last_updated": "2026-02-18", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, + "release_date": "2026-04-13", + "last_updated": "2026-04-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 12, "cache_read": 0.2 }, - "limit": { "context": 1000000, "output": 64000 } + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gemini-2.5-pro": { - "id": "gemini-2.5-pro", - "name": "Gemini 2.5 Pro", - "family": "gemini-pro", + "openrouter/free": { + "id": "openrouter/free", + "name": "Free Models Router", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-03-20", - "last_updated": "2025-06-05", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "release_date": "2026-02-01", + "last_updated": "2026-02-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.31 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 200000, + "input": 200000, + "output": 8000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gemini-2.5-flash": { - "id": "gemini-2.5-flash", - "name": "Gemini 2.5 Flash", - "family": "gemini-flash", - "attachment": true, + "arcee-ai/trinity-large-thinking": { + "id": "arcee-ai/trinity-large-thinking", + "name": "Trinity Large Thinking", + "family": "trinity", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-07-17", - "last_updated": "2025-07-17", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.5, "cache_read": 0.075 }, - "limit": { "context": 1048576, "output": 65536 } + "release_date": "2026-04-01", + "last_updated": "2026-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 80000 + }, + "cost": { + "input": 0.22, + "output": 0.85 + } }, - "grok-4-1-fast-reasoning": { - "id": "grok-4-1-fast-reasoning", - "name": "Grok 4.1 Fast (Reasoning)", - "family": "grok", - "attachment": true, - "reasoning": true, + "arcee-ai/trinity-large-preview:free": { + "id": "arcee-ai/trinity-large-preview:free", + "name": "Trinity Large Preview", + "family": "trinity", + "attachment": false, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-11", - "release_date": "2025-11-25", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 128000 } - }, - "gpt-5-nano": { - "id": "gpt-5-nano", - "name": "GPT-5 Nano", - "family": "gpt-nano", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.05, "output": 0.4, "cache_read": 0.01 }, - "limit": { "context": 400000, "output": 128000 } - }, - "gpt-5-3-codex": { - "id": "gpt-5-3-codex", - "name": "GPT-5.3 Codex", - "family": "gpt", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2026-01-31", - "release_date": "2026-02-15", - "last_updated": "2026-02-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "output": 128000 } + "knowledge": "2025-06", + "release_date": "2026-01-28", + "last_updated": "2026-01-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gpt-oss-20b": { - "id": "gpt-oss-20b", - "name": "GPT OSS 20B", - "family": "gpt-oss", + "cognitivecomputations/dolphin-mistral-24b-venice-edition:free": { + "id": "cognitivecomputations/dolphin-mistral-24b-venice-edition:free", + "name": "Uncensored (free)", + "family": "mistral", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "structured_output": true, "temperature": true, - "release_date": "1970-01-01", - "last_updated": "1970-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-06", + "release_date": "2025-07-09", + "last_updated": "2026-01-31", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.07, "output": 0.2 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "claude-sonnet-4-5": { - "id": "claude-sonnet-4-5", - "name": "Claude Sonnet 4.5", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, - "tool_call": true, + "bytedance-seed/seedream-4.5": { + "id": "bytedance-seed/seedream-4.5", + "name": "Seedream 4.5", + "family": "seed", + "attachment": false, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "knowledge": "2025-06", + "release_date": "2025-12-23", + "last_updated": "2026-01-31", + "modalities": { + "input": ["image", "text"], + "output": ["image"] + }, + "open_weights": true, + "limit": { + "context": 4096, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "claude-haiku-4-5": { - "id": "claude-haiku-4-5", - "name": "Claude Haiku 4.5", - "family": "claude-haiku", - "attachment": true, - "reasoning": true, - "tool_call": true, + "black-forest-labs/flux.2-max": { + "id": "black-forest-labs/flux.2-max", + "name": "FLUX.2 Max", + "family": "flux", + "attachment": false, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2025-02-28", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-06", + "release_date": "2025-12-16", + "last_updated": "2026-01-31", + "modalities": { + "input": ["image", "text"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 64000 } - } - } - }, - "google": { - "id": "google", - "env": ["GOOGLE_GENERATIVE_AI_API_KEY", "GEMINI_API_KEY"], - "npm": "@ai-sdk/google", - "name": "Google", - "doc": "https://ai.google.dev/gemini-api/docs/pricing", - "models": { - "gemini-2.5-flash-lite": { - "id": "gemini-2.5-flash-lite", - "name": "Gemini 2.5 Flash Lite", - "family": "gemini-flash-lite", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, + "limit": { + "context": 46864, + "output": 46864 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "black-forest-labs/flux.2-flex": { + "id": "black-forest-labs/flux.2-flex", + "name": "FLUX.2 Flex", + "family": "flux", + "attachment": false, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "knowledge": "2025-06", + "release_date": "2025-11-25", + "last_updated": "2026-01-31", + "modalities": { + "input": ["image", "text"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.025 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 67344, + "output": 67344 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gemini-2.5-flash-lite-preview-09-2025": { - "id": "gemini-2.5-flash-lite-preview-09-2025", - "name": "Gemini 2.5 Flash Lite Preview 09-25", - "family": "gemini-flash-lite", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, + "black-forest-labs/flux.2-pro": { + "id": "black-forest-labs/flux.2-pro", + "name": "FLUX.2 Pro", + "family": "flux", + "attachment": false, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "knowledge": "2025-06", + "release_date": "2025-11-25", + "last_updated": "2026-01-31", + "modalities": { + "input": ["image", "text"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.025 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 46864, + "output": 46864 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gemini-2.5-flash-preview-04-17": { - "id": "gemini-2.5-flash-preview-04-17", - "name": "Gemini 2.5 Flash Preview 04-17", - "family": "gemini-flash", - "attachment": true, - "reasoning": true, - "tool_call": true, + "black-forest-labs/flux.2-klein-4b": { + "id": "black-forest-labs/flux.2-klein-4b", + "name": "FLUX.2 Klein 4B", + "family": "flux", + "attachment": false, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-04-17", - "last_updated": "2025-04-17", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.6, "cache_read": 0.0375 }, - "limit": { "context": 1048576, "output": 65536 } + "knowledge": "2025-06", + "release_date": "2026-01-14", + "last_updated": "2026-01-31", + "modalities": { + "input": ["image", "text"], + "output": ["image"] + }, + "open_weights": true, + "limit": { + "context": 40960, + "output": 40960 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gemini-3.1-pro-preview": { - "id": "gemini-3.1-pro-preview", - "name": "Gemini 3.1 Pro Preview", - "family": "gemini-pro", - "attachment": true, + "nousresearch/hermes-3-llama-3.1-405b:free": { + "id": "nousresearch/hermes-3-llama-3.1-405b:free", + "name": "Hermes 3 405B Instruct (free)", + "family": "hermes", + "attachment": false, "reasoning": true, - "tool_call": true, - "structured_output": true, + "tool_call": false, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-02-19", - "last_updated": "2026-02-19", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { - "input": 2, - "output": 12, - "cache_read": 0.2, - "context_over_200k": { "input": 4, "output": 18, "cache_read": 0.4 } + "knowledge": "2023-12", + "release_date": "2024-08-16", + "last_updated": "2024-08-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 }, - "limit": { "context": 1048576, "output": 65536 } + "cost": { + "input": 0, + "output": 0 + } }, - "gemma-3n-e2b-it": { - "id": "gemma-3n-e2b-it", - "name": "Gemma 3n 2B", - "family": "gemma", - "attachment": true, - "reasoning": false, - "tool_call": false, + "nousresearch/hermes-4-405b": { + "id": "nousresearch/hermes-4-405b", + "name": "Hermes 4 405B", + "family": "hermes", + "attachment": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2025-08-25", + "last_updated": "2025-08-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 8192, "output": 2000 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3 + } }, - "gemini-2.5-flash-preview-05-20": { - "id": "gemini-2.5-flash-preview-05-20", - "name": "Gemini 2.5 Flash Preview 05-20", - "family": "gemini-flash", - "attachment": true, + "nousresearch/hermes-4-70b": { + "id": "nousresearch/hermes-4-70b", + "name": "Hermes 4 70B", + "family": "hermes", + "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-05-20", - "last_updated": "2025-05-20", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.6, "cache_read": 0.0375 }, - "limit": { "context": 1048576, "output": 65536 } + "knowledge": "2023-12", + "release_date": "2025-08-25", + "last_updated": "2025-08-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.13, + "output": 0.4 + } }, - "gemini-3-pro-preview": { - "id": "gemini-3-pro-preview", - "name": "Gemini 3 Pro Preview", - "family": "gemini-pro", - "attachment": true, + "stepfun/step-3.5-flash": { + "id": "stepfun/step-3.5-flash", + "name": "Step 3.5 Flash", + "family": "step", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, "knowledge": "2025-01", - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { - "input": 2, - "output": 12, - "cache_read": 0.2, - "context_over_200k": { "input": 4, "output": 18, "cache_read": 0.4 } + "release_date": "2026-01-29", + "last_updated": "2026-01-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 256000 }, - "limit": { "context": 1000000, "output": 64000 } + "cost": { + "input": 0.1, + "output": 0.3, + "cache_read": 0.02 + } }, - "gemma-3-27b-it": { - "id": "gemma-3-27b-it", - "name": "Gemma 3 27B", - "family": "gemma", + "mistralai/mistral-small-3.1-24b-instruct": { + "id": "mistralai/mistral-small-3.1-24b-instruct", + "name": "Mistral Small 3.1 24B Instruct", + "family": "mistral-small", "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, "knowledge": "2024-10", - "release_date": "2025-03-12", - "last_updated": "2025-03-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-03-17", + "last_updated": "2025-03-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gemma-3-4b-it": { - "id": "gemma-3-4b-it", - "name": "Gemma 3 4B", - "family": "gemma", - "attachment": true, + "mistralai/devstral-2512": { + "id": "mistralai/devstral-2512", + "name": "Devstral 2 2512", + "family": "devstral", + "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-03-13", - "last_updated": "2025-03-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-12", + "release_date": "2025-09-12", + "last_updated": "2025-09-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 32768, "output": 8192 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "gemma-3n-e4b-it": { - "id": "gemma-3n-e4b-it", - "name": "Gemma 3n 4B", - "family": "gemma", - "attachment": true, + "mistralai/codestral-2508": { + "id": "mistralai/codestral-2508", + "name": "Codestral 2508", + "family": "codestral", + "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-05-20", - "last_updated": "2025-05-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2025-08-01", + "last_updated": "2025-08-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 8192, "output": 2000 } + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.3, + "output": 0.9 + } }, - "gemini-2.5-pro-preview-06-05": { - "id": "gemini-2.5-pro-preview-06-05", - "name": "Gemini 2.5 Pro Preview 06-05", - "family": "gemini-pro", + "mistralai/mistral-medium-3.1": { + "id": "mistralai/mistral-medium-3.1", + "name": "Mistral Medium 3.1", + "family": "mistral-medium", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-05", - "last_updated": "2025-06-05", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2025-08-12", + "last_updated": "2025-08-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.31 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.4, + "output": 2 + } }, - "gemini-2.5-pro-preview-05-06": { - "id": "gemini-2.5-pro-preview-05-06", - "name": "Gemini 2.5 Pro Preview 05-06", - "family": "gemini-pro", + "mistralai/mistral-small-2603": { + "id": "mistralai/mistral-small-2603", + "name": "Mistral Small 4", + "family": "mistral-small", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-05-06", - "last_updated": "2025-05-06", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.31 }, - "limit": { "context": 1048576, "output": 65536 } + "knowledge": "2025-06", + "release_date": "2026-03-16", + "last_updated": "2026-03-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "gemini-2.0-flash-lite": { - "id": "gemini-2.0-flash-lite", - "name": "Gemini 2.0 Flash Lite", - "family": "gemini-flash-lite", + "mistralai/mistral-medium-3": { + "id": "mistralai/mistral-medium-3", + "name": "Mistral Medium 3", + "family": "mistral-medium", "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2024-06", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2025-05-07", + "last_updated": "2025-05-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.075, "output": 0.3 }, - "limit": { "context": 1048576, "output": 8192 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.4, + "output": 2 + } }, - "gemini-1.5-flash-8b": { - "id": "gemini-1.5-flash-8b", - "name": "Gemini 1.5 Flash-8B", - "family": "gemini-flash", - "attachment": true, + "mistralai/devstral-small-2505": { + "id": "mistralai/devstral-small-2505", + "name": "Devstral Small", + "family": "devstral", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-10-03", - "last_updated": "2024-10-03", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.0375, "output": 0.15, "cache_read": 0.01 }, - "limit": { "context": 1000000, "output": 8192 } + "knowledge": "2025-05", + "release_date": "2025-05-07", + "last_updated": "2025-05-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.06, + "output": 0.12 + } }, - "gemini-1.5-flash": { - "id": "gemini-1.5-flash", - "name": "Gemini 1.5 Flash", - "family": "gemini-flash", + "mistralai/mistral-small-3.2-24b-instruct": { + "id": "mistralai/mistral-small-3.2-24b-instruct", + "name": "Mistral Small 3.2 24B Instruct", + "family": "mistral-small", "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-05-14", - "last_updated": "2024-05-14", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.075, "output": 0.3, "cache_read": 0.01875 }, - "limit": { "context": 1000000, "output": 8192 } + "knowledge": "2024-10", + "release_date": "2025-06-20", + "last_updated": "2025-06-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 96000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gemini-2.5-pro-preview-tts": { - "id": "gemini-2.5-pro-preview-tts", - "name": "Gemini 2.5 Pro Preview TTS", - "family": "gemini-flash", + "mistralai/devstral-medium-2507": { + "id": "mistralai/devstral-medium-2507", + "name": "Devstral Medium", + "family": "devstral", "attachment": false, "reasoning": false, - "tool_call": false, - "temperature": false, - "knowledge": "2025-01", - "release_date": "2025-05-01", - "last_updated": "2025-05-01", - "modalities": { "input": ["text"], "output": ["audio"] }, - "open_weights": false, - "cost": { "input": 1, "output": 20 }, - "limit": { "context": 8000, "output": 16000 } - }, - "gemini-3-flash-preview": { - "id": "gemini-3-flash-preview", - "name": "Gemini 3 Flash Preview", - "family": "gemini-flash", - "attachment": true, - "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { - "input": 0.5, - "output": 3, - "cache_read": 0.05, - "context_over_200k": { "input": 0.5, "output": 3, "cache_read": 0.05 } + "knowledge": "2025-05", + "release_date": "2025-07-10", + "last_updated": "2025-07-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 }, - "limit": { "context": 1048576, "output": 65536 } + "cost": { + "input": 0.4, + "output": 2 + } }, - "gemini-2.5-pro": { - "id": "gemini-2.5-pro", - "name": "Gemini 2.5 Pro", - "family": "gemini-pro", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-03-20", - "last_updated": "2025-06-05", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.31 }, - "limit": { "context": 1048576, "output": 65536 } + "mistralai/devstral-small-2507": { + "id": "mistralai/devstral-small-2507", + "name": "Devstral Small 1.1", + "family": "devstral", + "attachment": false, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-05", + "release_date": "2025-07-10", + "last_updated": "2025-07-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } }, - "gemini-2.5-flash": { - "id": "gemini-2.5-flash", - "name": "Gemini 2.5 Flash", - "family": "gemini-flash", + "meta-llama/llama-3.2-11b-vision-instruct": { + "id": "meta-llama/llama-3.2-11b-vision-instruct", + "name": "Llama 3.2 11B Vision Instruct", + "family": "llama", "attachment": true, - "reasoning": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2024-09-25", + "last_updated": "2024-09-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "meta-llama/llama-3.2-3b-instruct:free": { + "id": "meta-llama/llama-3.2-3b-instruct:free", + "name": "Llama 3.2 3B Instruct (free)", + "family": "llama", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2024-09-25", + "last_updated": "2024-09-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "meta-llama/llama-3.3-70b-instruct:free": { + "id": "meta-llama/llama-3.3-70b-instruct:free", + "name": "Llama 3.3 70B Instruct (free)", + "family": "llama", + "attachment": false, + "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-03-20", - "last_updated": "2025-06-05", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.5, "cache_read": 0.075, "input_audio": 1 }, - "limit": { "context": 1048576, "output": 65536 } + "knowledge": "2024-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gemini-3.1-pro-preview-customtools": { - "id": "gemini-3.1-pro-preview-customtools", - "name": "Gemini 3.1 Pro Preview Custom Tools", - "family": "gemini-pro", + "x-ai/grok-4.20-multi-agent-beta": { + "id": "x-ai/grok-4.20-multi-agent-beta", + "name": "Grok 4.20 Multi - Agent Beta", + "family": "grok", "attachment": true, "reasoning": true, - "tool_call": true, - "structured_output": true, + "tool_call": false, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-02-19", - "last_updated": "2026-02-19", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, + "release_date": "2026-03-12", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, + "limit": { + "context": 2000000, + "output": 30000 + }, + "status": "beta", "cost": { "input": 2, - "output": 12, + "output": 6, "cache_read": 0.2, - "context_over_200k": { "input": 4, "output": 18, "cache_read": 0.4 } - }, - "limit": { "context": 1048576, "output": 65536 } + "tiers": [ + { + "input": 4, + "output": 12, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 12, + "cache_read": 0.4 + } + } }, - "gemini-2.5-flash-preview-09-2025": { - "id": "gemini-2.5-flash-preview-09-2025", - "name": "Gemini 2.5 Flash Preview 09-25", - "family": "gemini-flash", - "attachment": true, + "x-ai/grok-4-fast": { + "id": "x-ai/grok-4-fast", + "name": "Grok 4 Fast", + "family": "grok", + "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "knowledge": "2024-11", + "release_date": "2025-08-19", + "last_updated": "2025-08-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 2.5, "cache_read": 0.075, "input_audio": 1 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05, + "cache_write": 0.05 + } }, - "gemini-2.0-flash": { - "id": "gemini-2.0-flash", - "name": "Gemini 2.0 Flash", - "family": "gemini-flash", - "attachment": true, - "reasoning": false, + "x-ai/grok-code-fast-1": { + "id": "x-ai/grok-code-fast-1", + "name": "Grok Code Fast 1", + "family": "grok", + "attachment": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2024-06", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "knowledge": "2025-08", + "release_date": "2025-08-26", + "last_updated": "2025-08-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.025 }, - "limit": { "context": 1048576, "output": 8192 } + "limit": { + "context": 256000, + "output": 10000 + }, + "cost": { + "input": 0.2, + "output": 1.5, + "cache_read": 0.02 + } }, - "gemini-1.5-pro": { - "id": "gemini-1.5-pro", - "name": "Gemini 1.5 Pro", - "family": "gemini-pro", - "attachment": true, + "x-ai/grok-3-beta": { + "id": "x-ai/grok-3-beta", + "name": "Grok 3 Beta", + "family": "grok", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-02-15", - "last_updated": "2024-02-15", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 5, "cache_read": 0.3125 }, - "limit": { "context": 1000000, "output": 8192 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75, + "cache_write": 15 + } }, - "gemini-2.5-flash-lite-preview-06-17": { - "id": "gemini-2.5-flash-lite-preview-06-17", - "name": "Gemini 2.5 Flash Lite Preview 06-17", - "family": "gemini-flash-lite", - "attachment": true, + "x-ai/grok-4": { + "id": "x-ai/grok-4", + "name": "Grok 4", + "family": "grok", + "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.025, "input_audio": 0.3 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75, + "cache_write": 15, + "tiers": [ + { + "input": 6, + "output": 30, + "tier": { + "type": "context", + "size": 128000 + } + } + ] + } }, - "gemini-2.5-flash-preview-tts": { - "id": "gemini-2.5-flash-preview-tts", - "name": "Gemini 2.5 Flash Preview TTS", - "family": "gemini-flash", + "x-ai/grok-3-mini": { + "id": "x-ai/grok-3-mini", + "name": "Grok 3 Mini", + "family": "grok", "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "knowledge": "2025-01", - "release_date": "2025-05-01", - "last_updated": "2025-05-01", - "modalities": { "input": ["text"], "output": ["audio"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 10 }, - "limit": { "context": 8000, "output": 16000 } - }, - "gemini-3.1-flash-lite-preview": { - "id": "gemini-3.1-flash-lite-preview", - "name": "Gemini 3.1 Flash Lite Preview", - "family": "gemini-flash-lite", - "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-03-03", - "last_updated": "2026-03-03", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 1.5, "cache_read": 0.025, "cache_write": 1 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 0.5, + "cache_read": 0.075, + "cache_write": 0.5 + } }, - "gemini-flash-lite-latest": { - "id": "gemini-flash-lite-latest", - "name": "Gemini Flash-Lite Latest", - "family": "gemini-flash-lite", - "attachment": true, + "x-ai/grok-4.1-fast": { + "id": "x-ai/grok-4.1-fast", + "name": "Grok 4.1 Fast", + "family": "grok", + "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "knowledge": "2024-11", + "release_date": "2025-11-19", + "last_updated": "2025-11-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.025 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05, + "cache_write": 0.05 + } }, - "gemini-3.1-flash-image-preview": { - "id": "gemini-3.1-flash-image-preview", - "name": "Gemini 3.1 Flash Image (Preview)", - "family": "gemini-flash", + "x-ai/grok-4.20-beta": { + "id": "x-ai/grok-4.20-beta", + "name": "Grok 4.20 Beta", + "family": "grok", "attachment": true, "reasoning": true, - "tool_call": false, + "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-02-26", - "last_updated": "2026-02-26", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text", "image"] }, + "release_date": "2026-03-12", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 60 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "status": "beta", + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.2, + "tiers": [ + { + "input": 4, + "output": 12, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 12, + "cache_read": 0.4 + } + } }, - "gemini-2.5-flash-image": { - "id": "gemini-2.5-flash-image", - "name": "Gemini 2.5 Flash Image", - "family": "gemini-flash", - "attachment": true, + "x-ai/grok-3-mini-beta": { + "id": "x-ai/grok-3-mini-beta", + "name": "Grok 3 Mini Beta", + "family": "grok", + "attachment": false, "reasoning": true, - "tool_call": false, + "tool_call": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-08-26", - "last_updated": "2025-08-26", - "modalities": { "input": ["text", "image"], "output": ["text", "image"] }, + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 30, "cache_read": 0.075 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 0.5, + "cache_read": 0.075, + "cache_write": 0.5 + } }, - "gemini-flash-latest": { - "id": "gemini-flash-latest", - "name": "Gemini Flash Latest", - "family": "gemini-flash", - "attachment": true, - "reasoning": true, + "x-ai/grok-3": { + "id": "x-ai/grok-3", + "name": "Grok 3", + "family": "grok", + "attachment": false, + "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 2.5, "cache_read": 0.075, "input_audio": 1 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75, + "cache_write": 15 + } }, - "gemma-3-12b-it": { - "id": "gemma-3-12b-it", - "name": "Gemma 3 12B", - "family": "gemma", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": true, + "tencent/hy3-preview": { + "id": "tencent/hy3-preview", + "name": "Hy3 preview", + "family": "Hy", + "attachment": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-03-13", - "last_updated": "2025-03-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-04-20", + "last_updated": "2026-04-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 32768, "output": 8192 } + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 0.066, + "output": 0.26, + "cache_read": 0.029, + "cache_write": 0.029 + } }, - "gemini-live-2.5-flash-preview-native-audio": { - "id": "gemini-live-2.5-flash-preview-native-audio", - "name": "Gemini Live 2.5 Flash Preview Native Audio", - "family": "gemini-flash", + "poolside/laguna-m.1:free": { + "id": "poolside/laguna-m.1:free", + "name": "Laguna M.1", "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2025-01", - "release_date": "2025-06-17", - "last_updated": "2025-09-18", - "modalities": { "input": ["text", "audio", "video"], "output": ["text", "audio"] }, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-04-28", + "last_updated": "2026-04-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 2, "input_audio": 3, "output_audio": 12 }, - "limit": { "context": 131072, "output": 65536 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "gemini-embedding-001": { - "id": "gemini-embedding-001", - "name": "Gemini Embedding 001", - "family": "gemini", + "poolside/laguna-xs.2:free": { + "id": "poolside/laguna-xs.2:free", + "name": "Laguna XS.2", "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "knowledge": "2025-05", - "release_date": "2025-05-20", - "last_updated": "2025-05-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0 }, - "limit": { "context": 2048, "output": 3072 } - }, - "gemini-live-2.5-flash": { - "id": "gemini-live-2.5-flash", - "name": "Gemini Live 2.5 Flash", - "family": "gemini-flash", - "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-01", - "last_updated": "2025-09-01", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text", "audio"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 2, "input_audio": 3, "output_audio": 12 }, - "limit": { "context": 128000, "output": 8000 } + "release_date": "2026-04-28", + "last_updated": "2026-04-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "gemini-2.5-flash-image-preview": { - "id": "gemini-2.5-flash-image-preview", - "name": "Gemini 2.5 Flash Image (Preview)", - "family": "gemini-flash", - "attachment": true, - "reasoning": true, - "tool_call": false, - "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-08-26", - "last_updated": "2025-08-26", - "modalities": { "input": ["text", "image"], "output": ["text", "image"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 30, "cache_read": 0.075 }, - "limit": { "context": 32768, "output": 32768 } - } - } - }, - "cloudferro-sherlock": { - "id": "cloudferro-sherlock", - "env": ["CLOUDFERRO_SHERLOCK_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api-sherlock.cloudferro.com/openai/v1/", - "name": "CloudFerro Sherlock", - "doc": "https://docs.sherlock.cloudferro.com/", - "models": { - "openai/gpt-oss-120b": { - "id": "openai/gpt-oss-120b", - "name": "OpenAI GPT OSS 120B", - "family": "gpt-oss", + "prime-intellect/intellect-3": { + "id": "prime-intellect/intellect-3", + "name": "Intellect 3", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-08-28", - "last_updated": "2025-08-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-01-15", + "last_updated": "2025-01-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 2.92, "output": 2.92 }, - "limit": { "context": 131000, "output": 131000 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.2, + "output": 1.1 + } }, - "meta-llama/Llama-3.3-70B-Instruct": { - "id": "meta-llama/Llama-3.3-70B-Instruct", - "name": "Llama 3.3 70B Instruct", - "family": "llama", + "nvidia/nemotron-3-super-120b-a12b": { + "id": "nvidia/nemotron-3-super-120b-a12b", + "name": "Nemotron 3 Super", + "family": "nemotron", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10-09", - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2026-03-11", + "last_updated": "2026-03-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 2.92, "output": 2.92 }, - "limit": { "context": 70000, "output": 70000 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.1, + "output": 0.5 + } }, - "MiniMaxAI/MiniMax-M2.5": { - "id": "MiniMaxAI/MiniMax-M2.5", - "name": "MiniMax-M2.5", - "family": "minimax", - "attachment": false, + "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free": { + "id": "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free", + "name": "Nemotron 3 Nano Omni (free)", + "family": "nemotron", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2026-01", - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-28", + "last_updated": "2026-04-28", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 196000, "output": 196000 } + "limit": { + "context": 256000, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "speakleash/Bielik-11B-v3.0-Instruct": { - "id": "speakleash/Bielik-11B-v3.0-Instruct", - "name": "Bielik 11B v3.0 Instruct", + "nvidia/nemotron-3-nano-30b-a3b:free": { + "id": "nvidia/nemotron-3-nano-30b-a3b:free", + "name": "Nemotron 3 Nano 30B A3B (free)", + "family": "nemotron", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-03-13", - "last_updated": "2025-03-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-11", + "release_date": "2025-12-14", + "last_updated": "2026-01-31", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.67, "output": 0.67 }, - "limit": { "context": 32000, "output": 32000 } + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "speakleash/Bielik-11B-v2.6-Instruct": { - "id": "speakleash/Bielik-11B-v2.6-Instruct", - "name": "Bielik 11B v2.6 Instruct", + "nvidia/nemotron-nano-9b-v2:free": { + "id": "nvidia/nemotron-nano-9b-v2:free", + "name": "Nemotron Nano 9B V2 (free)", + "family": "nemotron", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-03-13", - "last_updated": "2025-03-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-09", + "release_date": "2025-09-05", + "last_updated": "2025-08-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.67, "output": 0.67 }, - "limit": { "context": 32000, "output": 32000 } - } - } - }, - "google-vertex-anthropic": { - "id": "google-vertex-anthropic", - "env": ["GOOGLE_VERTEX_PROJECT", "GOOGLE_VERTEX_LOCATION", "GOOGLE_APPLICATION_CREDENTIALS"], - "npm": "@ai-sdk/google-vertex/anthropic", - "name": "Vertex (Anthropic)", - "doc": "https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/claude", - "models": { - "claude-opus-4-5@20251101": { - "id": "claude-opus-4-5@20251101", - "name": "Claude Opus 4.5", - "family": "claude-opus", - "attachment": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "nvidia/nemotron-3-super-120b-a12b:free": { + "id": "nvidia/nemotron-3-super-120b-a12b:free", + "name": "Nemotron 3 Super (free)", + "family": "nemotron", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-11-24", - "last_updated": "2025-11-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 200000, "output": 64000 } + "knowledge": "2024-04", + "release_date": "2026-03-11", + "last_updated": "2026-03-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "claude-haiku-4-5@20251001": { - "id": "claude-haiku-4-5@20251001", - "name": "Claude Haiku 4.5", - "family": "claude-haiku", - "attachment": true, + "nvidia/nemotron-nano-9b-v2": { + "id": "nvidia/nemotron-nano-9b-v2", + "name": "nvidia-nemotron-nano-9b-v2", + "family": "nemotron", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-02-28", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 64000 } + "knowledge": "2024-09", + "release_date": "2025-08-18", + "last_updated": "2025-08-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.04, + "output": 0.16 + } }, - "claude-3-5-sonnet@20241022": { - "id": "claude-3-5-sonnet@20241022", - "name": "Claude Sonnet 3.5 v2", - "family": "claude-sonnet", - "attachment": true, - "reasoning": false, + "nvidia/nemotron-nano-12b-v2-vl:free": { + "id": "nvidia/nemotron-nano-12b-v2-vl:free", + "name": "Nemotron Nano 12B 2 VL (free)", + "family": "nemotron", + "attachment": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-04-30", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 8192 } + "knowledge": "2025-11", + "release_date": "2025-10-28", + "last_updated": "2026-01-31", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "claude-3-7-sonnet@20250219": { - "id": "claude-3-7-sonnet@20250219", - "name": "Claude Sonnet 3.7", - "family": "claude-sonnet", - "attachment": true, + "inception/mercury-edit-2": { + "id": "inception/mercury-edit-2", + "name": "Mercury Edit 2", + "attachment": false, "reasoning": true, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2024-10-31", - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-03-30", + "last_updated": "2026-03-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.25, + "output": 0.75, + "cache_read": 0.025 + } }, - "claude-opus-4-1@20250805": { - "id": "claude-opus-4-1@20250805", - "name": "Claude Opus 4.1", - "family": "claude-opus", - "attachment": true, + "inception/mercury-2": { + "id": "inception/mercury-2", + "name": "Mercury 2", + "family": "mercury", + "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-03-04", + "last_updated": "2026-03-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 128000, + "output": 50000 + }, + "cost": { + "input": 0.25, + "output": 0.75, + "cache_read": 0.025 + } }, - "claude-3-5-haiku@20241022": { - "id": "claude-3-5-haiku@20241022", - "name": "Claude Haiku 3.5", - "family": "claude-haiku", + "openai/gpt-5.1-codex-max": { + "id": "openai/gpt-5.1-codex-max", + "name": "GPT-5.1-Codex-Max", + "family": "gpt-codex", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-07-31", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.8, "output": 4, "cache_read": 0.08, "cache_write": 1 }, - "limit": { "context": 200000, "output": 8192 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.1, + "output": 9, + "cache_read": 0.11 + } }, - "claude-opus-4@20250514": { - "id": "claude-opus-4@20250514", - "name": "Claude Opus 4", - "family": "claude-opus", + "openai/gpt-5.2-chat": { + "id": "openai/gpt-5.2-chat", + "name": "GPT-5.2 Chat", + "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "claude-sonnet-4-6@default": { - "id": "claude-sonnet-4-6@default", - "name": "Claude Sonnet 4.6", - "family": "claude-sonnet", - "attachment": true, + "openai/gpt-oss-120b:exacto": { + "id": "openai/gpt-oss-120b:exacto", + "name": "GPT OSS 120B (exacto)", + "family": "gpt-oss", + "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-08", - "release_date": "2026-02-17", - "last_updated": "2026-02-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75, - "context_over_200k": { "input": 6, "output": 22.5, "cache_read": 0.6, "cache_write": 7.5 } + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 }, - "limit": { "context": 200000, "output": 64000 } + "cost": { + "input": 0.05, + "output": 0.24 + } }, - "claude-sonnet-4@20250514": { - "id": "claude-sonnet-4@20250514", - "name": "Claude Sonnet 4", - "family": "claude-sonnet", + "openai/gpt-5-chat": { + "id": "openai/gpt-5-chat", + "name": "GPT-5 Chat (latest)", + "family": "gpt-codex", "attachment": true, "reasoning": true, - "tool_call": true, + "tool_call": false, + "structured_output": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2024-09-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, - "claude-sonnet-4-5@20250929": { - "id": "claude-sonnet-4-5@20250929", - "name": "Claude Sonnet 4.5", - "family": "claude-sonnet", + "openai/gpt-5.2-pro": { + "id": "openai/gpt-5.2-pro", + "name": "GPT-5.2 Pro", + "family": "gpt-pro", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 21, + "output": 168 + } }, - "claude-opus-4-6@default": { - "id": "claude-opus-4-6@default", - "name": "Claude Opus 4.6", - "family": "claude-opus", + "openai/gpt-5-mini": { + "id": "openai/gpt-5-mini", + "name": "GPT-5 Mini", + "family": "gpt-mini", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-05", - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2024-10-01", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { - "input": 5, - "output": 25, - "cache_read": 0.5, - "cache_write": 6.25, - "context_over_200k": { "input": 10, "output": 37.5, "cache_read": 1, "cache_write": 12.5 } + "limit": { + "context": 400000, + "output": 128000 }, - "limit": { "context": 1000000, "output": 128000 } - } - } - }, - "google-vertex": { - "id": "google-vertex", - "env": ["GOOGLE_VERTEX_PROJECT", "GOOGLE_VERTEX_LOCATION", "GOOGLE_APPLICATION_CREDENTIALS"], - "npm": "@ai-sdk/google-vertex", - "name": "Vertex", - "doc": "https://cloud.google.com/vertex-ai/generative-ai/docs/models", - "models": { - "gemini-2.5-flash-lite": { - "id": "gemini-2.5-flash-lite", - "name": "Gemini 2.5 Flash Lite", - "family": "gemini-flash-lite", + "cost": { + "input": 0.25, + "output": 2 + } + }, + "openai/gpt-5-nano": { + "id": "openai/gpt-5-nano", + "name": "GPT-5 Nano", + "family": "gpt-nano", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "knowledge": "2024-10-01", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.025 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.05, + "output": 0.4 + } }, - "gemini-2.5-flash-lite-preview-09-2025": { - "id": "gemini-2.5-flash-lite-preview-09-2025", - "name": "Gemini 2.5 Flash Lite Preview 09-25", - "family": "gemini-flash-lite", + "openai/gpt-5.3-codex": { + "id": "openai/gpt-5.3-codex", + "name": "GPT-5.3-Codex", + "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-02-24", + "last_updated": "2026-02-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.025 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "gemini-2.5-flash-preview-04-17": { - "id": "gemini-2.5-flash-preview-04-17", - "name": "Gemini 2.5 Flash Preview 04-17", - "family": "gemini-flash", + "openai/gpt-5.2": { + "id": "openai/gpt-5.2", + "name": "GPT-5.2", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-04-17", - "last_updated": "2025-04-17", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.6, "cache_read": 0.0375 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "gemini-3.1-pro-preview": { - "id": "gemini-3.1-pro-preview", - "name": "Gemini 3.1 Pro Preview", - "family": "gemini-pro", - "attachment": true, + "openai/gpt-oss-20b:free": { + "id": "openai/gpt-oss-20b:free", + "name": "gpt-oss-20b (free)", + "family": "gpt-oss", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-02-19", - "last_updated": "2026-02-19", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { - "input": 2, - "output": 12, - "cache_read": 0.2, - "context_over_200k": { "input": 4, "output": 18, "cache_read": 0.4 } + "release_date": "2025-08-05", + "last_updated": "2026-01-31", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 }, - "limit": { "context": 1048576, "output": 65536 } + "cost": { + "input": 0, + "output": 0 + } }, - "gemini-2.5-flash-preview-05-20": { - "id": "gemini-2.5-flash-preview-05-20", - "name": "Gemini 2.5 Flash Preview 05-20", - "family": "gemini-flash", + "openai/gpt-4o-mini": { + "id": "openai/gpt-4o-mini", + "name": "GPT-4o-mini", + "family": "gpt-mini", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-05-20", - "last_updated": "2025-05-20", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.6, "cache_read": 0.0375 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.08 + } }, - "gemini-3-pro-preview": { - "id": "gemini-3-pro-preview", - "name": "Gemini 3 Pro Preview", - "family": "gemini-pro", + "openai/gpt-5.4-mini": { + "id": "openai/gpt-5.4-mini", + "name": "GPT-5.4 Mini", + "family": "gpt-mini", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { - "input": 2, - "output": 12, - "cache_read": 0.2, - "context_over_200k": { "input": 4, "output": 18, "cache_read": 0.4 } + "limit": { + "context": 400000, + "output": 128000 }, - "limit": { "context": 1048576, "output": 65536 } + "cost": { + "input": 0.75, + "output": 4.5, + "cache_read": 0.075 + } }, - "gemini-2.5-pro-preview-06-05": { - "id": "gemini-2.5-pro-preview-06-05", - "name": "Gemini 2.5 Pro Preview 06-05", - "family": "gemini-pro", + "openai/gpt-5.1-chat": { + "id": "openai/gpt-5.1-chat", + "name": "GPT-5.1 Chat", + "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-05", - "last_updated": "2025-06-05", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.31 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "gemini-2.5-pro-preview-05-06": { - "id": "gemini-2.5-pro-preview-05-06", - "name": "Gemini 2.5 Pro Preview 05-06", - "family": "gemini-pro", + "openai/o4-mini": { + "id": "openai/o4-mini", + "name": "o4 Mini", + "family": "o-mini", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-05-06", - "last_updated": "2025-05-06", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "knowledge": "2024-06", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.31 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.28 + } }, - "gemini-2.0-flash-lite": { - "id": "gemini-2.0-flash-lite", - "name": "Gemini 2.0 Flash Lite", - "family": "gemini-flash-lite", + "openai/gpt-5.4-nano": { + "id": "openai/gpt-5.4-nano", + "name": "GPT-5.4 Nano", + "family": "gpt-nano", "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-06", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.075, "output": 0.3 }, - "limit": { "context": 1048576, "output": 8192 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.2, + "output": 1.25, + "cache_read": 0.02 + } }, - "gemini-3-flash-preview": { - "id": "gemini-3-flash-preview", - "name": "Gemini 3 Flash Preview", - "family": "gemini-flash", + "openai/gpt-5.2-codex": { + "id": "openai/gpt-5.2-codex", + "name": "GPT-5.2-Codex", + "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-01-14", + "last_updated": "2026-01-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { - "input": 0.5, - "output": 3, - "cache_read": 0.05, - "context_over_200k": { "input": 0.5, "output": 3, "cache_read": 0.05 } + "limit": { + "context": 400000, + "output": 128000 }, - "limit": { "context": 1048576, "output": 65536 } + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "gemini-2.5-pro": { - "id": "gemini-2.5-pro", - "name": "Gemini 2.5 Pro", - "family": "gemini-pro", + "openai/gpt-5.1-codex-mini": { + "id": "openai/gpt-5.1-codex-mini", + "name": "GPT-5.1-Codex-Mini", + "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-03-20", - "last_updated": "2025-06-05", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.31 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 400000, + "output": 100000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.025 + } }, - "gemini-2.5-flash": { - "id": "gemini-2.5-flash", - "name": "Gemini 2.5 Flash", - "family": "gemini-flash", + "openai/gpt-5-image": { + "id": "openai/gpt-5-image", + "name": "GPT-5 Image", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "knowledge": "2024-10-01", + "release_date": "2025-10-14", + "last_updated": "2025-10-14", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text", "image"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 2.5, "cache_read": 0.075, "cache_write": 0.383 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 10, + "cache_read": 1.25 + } }, - "gemini-3.1-pro-preview-customtools": { - "id": "gemini-3.1-pro-preview-customtools", - "name": "Gemini 3.1 Pro Preview Custom Tools", - "family": "gemini-pro", + "openai/gpt-5.1": { + "id": "openai/gpt-5.1", + "name": "GPT-5.1", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-02-19", - "last_updated": "2026-02-19", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { - "input": 2, - "output": 12, - "cache_read": 0.2, - "context_over_200k": { "input": 4, "output": 18, "cache_read": 0.4 } + "limit": { + "context": 400000, + "output": 128000 }, - "limit": { "context": 1048576, "output": 65536 } + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "gemini-2.5-flash-preview-09-2025": { - "id": "gemini-2.5-flash-preview-09-2025", - "name": "Gemini 2.5 Flash Preview 09-25", - "family": "gemini-flash", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.5, "cache_read": 0.075, "cache_write": 0.383 }, - "limit": { "context": 1048576, "output": 65536 } - }, - "gemini-2.0-flash": { - "id": "gemini-2.0-flash", - "name": "Gemini 2.0 Flash", - "family": "gemini-flash", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-06", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.6, "cache_read": 0.025 }, - "limit": { "context": 1048576, "output": 8192 } - }, - "gemini-2.5-flash-lite-preview-06-17": { - "id": "gemini-2.5-flash-lite-preview-06-17", - "name": "Gemini 2.5 Flash Lite Preview 06-17", - "family": "gemini-flash-lite", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.025 }, - "limit": { "context": 65536, "output": 65536 } - }, - "gemini-flash-lite-latest": { - "id": "gemini-flash-lite-latest", - "name": "Gemini Flash-Lite Latest", - "family": "gemini-flash-lite", + "openai/gpt-5.4-pro": { + "id": "openai/gpt-5.4-pro", + "name": "GPT-5.4 Pro", + "family": "gpt-pro", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "structured_output": false, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.025 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 30, + "output": 180, + "cache_read": 30 + } }, - "gemini-flash-latest": { - "id": "gemini-flash-latest", - "name": "Gemini Flash Latest", - "family": "gemini-flash", + "openai/gpt-5-codex": { + "id": "openai/gpt-5-codex", + "name": "GPT-5 Codex", + "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.5, "cache_read": 0.075, "cache_write": 0.383 }, - "limit": { "context": 1048576, "output": 65536 } - }, - "gemini-embedding-001": { - "id": "gemini-embedding-001", - "name": "Gemini Embedding 001", - "family": "gemini", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "knowledge": "2025-05", - "release_date": "2025-05-20", - "last_updated": "2025-05-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10-01", + "release_date": "2025-09-15", + "last_updated": "2025-09-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0 }, - "limit": { "context": 2048, "output": 3072 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "openai/gpt-oss-20b-maas": { - "id": "openai/gpt-oss-20b-maas", + "openai/gpt-oss-20b": { + "id": "openai/gpt-oss-20b", "name": "GPT OSS 20B", "family": "gpt-oss", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, "release_date": "2025-08-05", "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.07, "output": 0.25 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.05, + "output": 0.2 + } }, - "openai/gpt-oss-120b-maas": { - "id": "openai/gpt-oss-120b-maas", - "name": "GPT OSS 120B", + "openai/gpt-5-pro": { + "id": "openai/gpt-5-pro", + "name": "GPT-5 Pro", + "family": "gpt-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-10-06", + "last_updated": "2025-10-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 272000 + }, + "cost": { + "input": 15, + "output": 120 + } + }, + "openai/gpt-oss-120b:free": { + "id": "openai/gpt-oss-120b:free", + "name": "gpt-oss-120b (free)", "family": "gpt-oss", "attachment": false, "reasoning": true, @@ -16488,803 +24271,1142 @@ "temperature": true, "release_date": "2025-08-05", "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.09, "output": 0.36 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "zai-org/glm-4.7-maas": { - "id": "zai-org/glm-4.7-maas", - "name": "GLM-4.7", - "family": "glm", - "attachment": false, + "openai/gpt-5": { + "id": "openai/gpt-5", + "name": "GPT-5", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-01-06", - "last_updated": "2026-01-06", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.2 }, - "limit": { "context": 200000, "output": 128000 }, - "provider": { - "npm": "@ai-sdk/openai-compatible", - "api": "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi" + "knowledge": "2024-10-01", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10 } }, - "zai-org/glm-5-maas": { - "id": "zai-org/glm-5-maas", - "name": "GLM-5", - "family": "glm", + "openai/gpt-oss-safeguard-20b": { + "id": "openai/gpt-oss-safeguard-20b", + "name": "GPT OSS Safeguard 20B", + "family": "gpt-oss", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1, "output": 3.2, "cache_read": 0.1 }, - "limit": { "context": 202752, "output": 131072 }, - "provider": { - "npm": "@ai-sdk/openai-compatible", - "api": "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi" + "release_date": "2025-10-29", + "last_updated": "2025-10-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.075, + "output": 0.3 } }, - "deepseek-ai/deepseek-v3.1-maas": { - "id": "deepseek-ai/deepseek-v3.1-maas", - "name": "DeepSeek V3.1", - "family": "deepseek", + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "name": "GPT OSS 120B", + "family": "gpt-oss", "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-08-28", - "last_updated": "2025-08-28", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 1.7 }, - "limit": { "context": 163840, "output": 32768 }, - "provider": { - "npm": "@ai-sdk/openai-compatible", - "api": "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi" + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.072, + "output": 0.28 } }, - "qwen/qwen3-235b-a22b-instruct-2507-maas": { - "id": "qwen/qwen3-235b-a22b-instruct-2507-maas", - "name": "Qwen3 235B A22B Instruct", - "family": "qwen", - "attachment": false, - "reasoning": true, + "openai/gpt-4.1": { + "id": "openai/gpt-4.1", + "name": "GPT-4.1", + "family": "gpt", + "attachment": true, + "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-08-13", - "last_updated": "2025-08-13", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.22, "output": 0.88 }, - "limit": { "context": 262144, "output": 16384 }, - "provider": { - "npm": "@ai-sdk/openai-compatible", - "api": "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi" + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 } }, - "meta/llama-3.3-70b-instruct-maas": { - "id": "meta/llama-3.3-70b-instruct-maas", - "name": "Llama 3.3 70B Instruct", - "family": "llama", - "attachment": false, + "openai/gpt-4.1-mini": { + "id": "openai/gpt-4.1-mini", + "name": "GPT-4.1 Mini", + "family": "gpt-mini", + "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2025-04-29", - "last_updated": "2025-04-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.72, "output": 0.72 }, - "limit": { "context": 128000, "output": 8192 }, - "provider": { - "npm": "@ai-sdk/openai-compatible", - "api": "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi" + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 1.6, + "cache_read": 0.1 } }, - "meta/llama-4-maverick-17b-128e-instruct-maas": { - "id": "meta/llama-4-maverick-17b-128e-instruct-maas", - "name": "Llama 4 Maverick 17B 128E Instruct", - "family": "llama", + "openai/gpt-5.1-codex": { + "id": "openai/gpt-5.1-codex", + "name": "GPT-5.1-Codex", + "family": "gpt-codex", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-04-29", - "last_updated": "2025-04-29", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.35, "output": 1.15 }, - "limit": { "context": 524288, "output": 8192 }, - "provider": { - "npm": "@ai-sdk/openai-compatible", - "api": "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi" + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 } - } - } - }, - "chutes": { - "id": "chutes", - "env": ["CHUTES_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://llm.chutes.ai/v1", - "name": "Chutes", - "doc": "https://llm.chutes.ai/v1/models", - "models": { - "unsloth/gemma-3-27b-it": { - "id": "unsloth/gemma-3-27b-it", - "name": "gemma 3 27b it", - "family": "unsloth", + }, + "z-ai/glm-4.7": { + "id": "z-ai/glm-4.7", + "name": "GLM-4.7", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_details" + }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.04, "output": 0.15, "cache_read": 0.02 }, - "limit": { "context": 128000, "output": 65536 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.11 + } }, - "unsloth/gemma-3-4b-it": { - "id": "unsloth/gemma-3-4b-it", - "name": "gemma 3 4b it", - "family": "unsloth", + "z-ai/glm-4.5-air:free": { + "id": "z-ai/glm-4.5-air:free", + "name": "GLM 4.5 Air (free)", + "family": "glm-air", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": false, - "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.01, "output": 0.03 }, - "limit": { "context": 96000, "output": 96000 } + "limit": { + "context": 128000, + "output": 96000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "unsloth/Mistral-Nemo-Instruct-2407": { - "id": "unsloth/Mistral-Nemo-Instruct-2407", - "name": "Mistral Nemo Instruct 2407", - "family": "unsloth", + "z-ai/glm-5": { + "id": "z-ai/glm-5", + "name": "GLM-5", + "family": "glm", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.02, "output": 0.04, "cache_read": 0.01 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 202752, + "output": 131000 + }, + "cost": { + "input": 1, + "output": 3.2, + "cache_read": 0.2 + } }, - "unsloth/Llama-3.2-3B-Instruct": { - "id": "unsloth/Llama-3.2-3B-Instruct", - "name": "Llama 3.2 3B Instruct", - "family": "unsloth", + "z-ai/glm-5.1": { + "id": "z-ai/glm-5.1", + "name": "GLM-5.1", + "family": "glm", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-02-12", - "last_updated": "2025-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-07", + "last_updated": "2026-04-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.01, "output": 0.01, "cache_read": 0.005 }, - "limit": { "context": 16384, "output": 16384 } + "limit": { + "context": 202752, + "output": 131072 + }, + "cost": { + "input": 1.4, + "output": 4.4, + "cache_read": 0.26 + } }, - "unsloth/Llama-3.2-1B-Instruct": { - "id": "unsloth/Llama-3.2-1B-Instruct", - "name": "Llama 3.2 1B Instruct", + "z-ai/glm-4.5": { + "id": "z-ai/glm-4.5", + "name": "GLM 4.5", + "family": "glm", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.01, "output": 0.01, "cache_read": 0.005 }, - "limit": { "context": 32768, "output": 8192 } + "limit": { + "context": 128000, + "output": 96000 + }, + "cost": { + "input": 0.6, + "output": 2.2 + } }, - "unsloth/Mistral-Small-24B-Instruct-2501": { - "id": "unsloth/Mistral-Small-24B-Instruct-2501", - "name": "Mistral Small 24B Instruct 2501", - "family": "unsloth", + "z-ai/glm-4.6:exacto": { + "id": "z-ai/glm-4.6:exacto", + "name": "GLM 4.6 (exacto)", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-09", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.03, "output": 0.11 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 0.6, + "output": 1.9, + "cache_read": 0.11 + } }, - "unsloth/gemma-3-12b-it": { - "id": "unsloth/gemma-3-12b-it", - "name": "gemma 3 12b it", - "family": "unsloth", + "z-ai/glm-4.5-air": { + "id": "z-ai/glm-4.5-air", + "name": "GLM 4.5 Air", + "family": "glm-air", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.03, "output": 0.1 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 128000, + "output": 96000 + }, + "cost": { + "input": 0.2, + "output": 1.1 + } }, - "openai/gpt-oss-120b-TEE": { - "id": "openai/gpt-oss-120b-TEE", - "name": "gpt oss 120b TEE", - "family": "gpt-oss", + "z-ai/glm-5-turbo": { + "id": "z-ai/glm-5-turbo", + "name": "GLM-5-Turbo", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.04, "output": 0.18 }, - "limit": { "context": 131072, "output": 65536 } + "release_date": "2026-03-16", + "last_updated": "2026-03-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 202752, + "output": 131072 + }, + "cost": { + "input": 0.96, + "output": 3.2, + "cache_read": 0.192, + "cache_write": 0 + } }, - "openai/gpt-oss-20b": { - "id": "openai/gpt-oss-20b", - "name": "gpt oss 20b", - "family": "gpt-oss", - "attachment": false, + "z-ai/glm-4.5v": { + "id": "z-ai/glm-4.5v", + "name": "GLM 4.5V", + "family": "glm", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-08-11", + "last_updated": "2025-08-11", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.02, "output": 0.1 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 64000, + "output": 16384 + }, + "cost": { + "input": 0.6, + "output": 1.8 + } }, - "NousResearch/Hermes-4-405B-FP8-TEE": { - "id": "NousResearch/Hermes-4-405B-FP8-TEE", - "name": "Hermes 4 405B FP8 TEE", - "family": "nousresearch", + "z-ai/glm-4.6": { + "id": "z-ai/glm-4.6", + "name": "GLM 4.6", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-09", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 131072, "output": 65536 } + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.11 + } }, - "NousResearch/Hermes-4-14B": { - "id": "NousResearch/Hermes-4-14B", - "name": "Hermes 4 14B", - "family": "nousresearch", + "z-ai/glm-4.7-flash": { + "id": "z-ai/glm-4.7-flash", + "name": "GLM-4.7-Flash", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "interleaved": { + "field": "reasoning_details" + }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01-19", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.01, "output": 0.05 }, - "limit": { "context": 40960, "output": 40960 } + "limit": { + "context": 200000, + "output": 65535 + }, + "cost": { + "input": 0.07, + "output": 0.4 + } }, - "NousResearch/Hermes-4.3-36B": { - "id": "NousResearch/Hermes-4.3-36B", - "name": "Hermes 4.3 36B", - "family": "nousresearch", + "sourceful/riverflow-v2-standard-preview": { + "id": "sourceful/riverflow-v2-standard-preview", + "name": "Riverflow V2 Standard Preview", + "family": "sourceful", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-06", + "release_date": "2025-12-08", + "last_updated": "2026-01-28", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.39 }, - "limit": { "context": 32768, "output": 8192 } + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "NousResearch/DeepHermes-3-Mistral-24B-Preview": { - "id": "NousResearch/DeepHermes-3-Mistral-24B-Preview", - "name": "DeepHermes 3 Mistral 24B Preview", - "family": "nousresearch", + "sourceful/riverflow-v2-fast-preview": { + "id": "sourceful/riverflow-v2-fast-preview", + "name": "Riverflow V2 Fast Preview", + "family": "sourceful", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.02, "output": 0.1 }, - "limit": { "context": 32768, "output": 32768 } - }, - "NousResearch/Hermes-4-70B": { - "id": "NousResearch/Hermes-4-70B", - "name": "Hermes 4 70B", - "family": "nousresearch", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, - "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.11, "output": 0.38 }, - "limit": { "context": 131072, "output": 131072 } - }, - "zai-org/GLM-4.6-TEE": { - "id": "zai-org/GLM-4.6-TEE", - "name": "GLM 4.6 TEE", - "family": "glm", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, + "tool_call": false, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-06", + "release_date": "2025-12-08", + "last_updated": "2026-01-28", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, "open_weights": true, - "cost": { "input": 0.4, "output": 1.7, "cache_read": 0.2 }, - "limit": { "context": 202752, "output": 65536 } + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "zai-org/GLM-4.5-Air": { - "id": "zai-org/GLM-4.5-Air", - "name": "GLM 4.5 Air", - "family": "glm", + "sourceful/riverflow-v2-max-preview": { + "id": "sourceful/riverflow-v2-max-preview", + "name": "Riverflow V2 Max Preview", + "family": "sourceful", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-06", + "release_date": "2025-12-08", + "last_updated": "2026-01-28", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, "open_weights": true, - "cost": { "input": 0.05, "output": 0.22 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "zai-org/GLM-4.6V": { - "id": "zai-org/GLM-4.6V", - "name": "GLM 4.6V", - "family": "glm", + "minimax/minimax-m2.7": { + "id": "minimax/minimax-m2.7", + "name": "MiniMax M2.7", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 0.9, "cache_read": 0.15 }, - "limit": { "context": 131072, "output": 65536 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06, + "cache_write": 0.375 + } }, - "zai-org/GLM-4.7-TEE": { - "id": "zai-org/GLM-4.7-TEE", - "name": "GLM 4.7 TEE", - "family": "glm", + "minimax/minimax-m2": { + "id": "minimax/minimax-m2", + "name": "MiniMax M2", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "interleaved": { + "field": "reasoning_details" + }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-10-23", + "last_updated": "2025-10-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.4, "output": 1.5 }, - "limit": { "context": 202752, "output": 65535 } + "limit": { + "context": 196600, + "output": 118000 + }, + "cost": { + "input": 0.28, + "output": 1.15, + "cache_read": 0.28, + "cache_write": 1.15 + } }, - "zai-org/GLM-4.6-FP8": { - "id": "zai-org/GLM-4.6-FP8", - "name": "GLM 4.6 FP8", - "attachment": false, + "minimax/minimax-01": { + "id": "minimax/minimax-01", + "name": "MiniMax-01", + "family": "minimax", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-01-15", + "last_updated": "2025-01-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 202752, "output": 65535 } + "limit": { + "context": 1000000, + "output": 1000000 + }, + "cost": { + "input": 0.2, + "output": 1.1 + } }, - "zai-org/GLM-4.7-Flash": { - "id": "zai-org/GLM-4.7-Flash", - "name": "GLM 4.7 Flash", + "minimax/minimax-m2.1": { + "id": "minimax/minimax-m2.1", + "name": "MiniMax M2.1", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_details" + }, "structured_output": true, "temperature": true, - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.06, "output": 0.35 }, - "limit": { "context": 202752, "output": 65535 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "zai-org/GLM-4.5-TEE": { - "id": "zai-org/GLM-4.5-TEE", - "name": "GLM 4.5 TEE", - "family": "glm", + "minimax/minimax-m2.5:free": { + "id": "minimax/minimax-m2.5:free", + "name": "MiniMax M2.5 (free)", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "interleaved": { + "field": "reasoning_details" + }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.35, "output": 1.55 }, - "limit": { "context": 131072, "output": 65536 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "zai-org/GLM-4.5-FP8": { - "id": "zai-org/GLM-4.5-FP8", - "name": "GLM 4.5 FP8", + "minimax/minimax-m1": { + "id": "minimax/minimax-m1", + "name": "MiniMax M1", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 131072, "output": 65536 } + "limit": { + "context": 1000000, + "output": 40000 + }, + "cost": { + "input": 0.4, + "output": 2.2 + } }, - "zai-org/GLM-5-TEE": { - "id": "zai-org/GLM-5-TEE", - "name": "GLM 5 TEE", - "family": "glm", + "minimax/minimax-m2.5": { + "id": "minimax/minimax-m2.5", + "name": "MiniMax M2.5", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "interleaved": { + "field": "reasoning_details" + }, "structured_output": true, "temperature": true, - "release_date": "2026-02-14", - "last_updated": "2026-02-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.95, "output": 3.15, "cache_read": 0.475 }, - "limit": { "context": 202752, "output": 65535 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03 + } }, - "zai-org/GLM-5-Turbo": { - "id": "zai-org/GLM-5-Turbo", - "name": "GLM 5 Turbo", - "family": "glm", + "qwen/qwen3-coder-plus": { + "id": "qwen/qwen3-coder-plus", + "name": "Qwen3 Coder Plus", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "release_date": "2026-03-11", - "last_updated": "2026-03-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-09-23", + "last_updated": "2025-09-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.49, "output": 1.96, "cache_read": 0.245 }, - "limit": { "context": 202752, "output": 65535 } + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.65, + "output": 3.25, + "cache_read": 0.13, + "cache_write": 0.8125 + } }, - "zai-org/GLM-4.7-FP8": { - "id": "zai-org/GLM-4.7-FP8", - "name": "GLM 4.7 FP8", - "attachment": false, + "qwen/qwen3.5-397b-a17b": { + "id": "qwen/qwen3.5-397b-a17b", + "name": "Qwen3.5 397B A17B", + "family": "qwen", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 202752, "output": 65535 } - }, - "nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16": { - "id": "nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16", - "name": "NVIDIA Nemotron 3 Nano 30B A3B BF16", - "family": "nemotron", - "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2026-02-16", + "last_updated": "2026-02-16", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.06, "output": 0.24 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.6, + "output": 3.6 + } }, - "rednote-hilab/dots.ocr": { - "id": "rednote-hilab/dots.ocr", - "name": "dots.ocr", - "family": "rednote", - "attachment": false, + "qwen/qwen2.5-vl-72b-instruct": { + "id": "qwen/qwen2.5-vl-72b-instruct", + "name": "Qwen2.5 VL 72B Instruct", + "family": "qwen", + "attachment": true, "reasoning": false, "tool_call": false, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-02-01", + "last_updated": "2025-02-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.01, "output": 0.01, "cache_read": 0.005 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 32768, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "miromind-ai/MiroThinker-v1.5-235B": { - "id": "miromind-ai/MiroThinker-v1.5-235B", - "name": "MiroThinker V1.5 235B", + "qwen/qwen-plus": { + "id": "qwen/qwen-plus", + "name": "Qwen: Qwen-Plus", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "structured_output": false, "temperature": true, - "release_date": "2026-01-10", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.15 }, - "limit": { "context": 262144, "output": 8192 } + "knowledge": "2024-04", + "release_date": "2025-01-25", + "last_updated": "2025-01-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 32768 + }, + "cost": { + "input": 0.26, + "output": 0.78, + "cache_read": 0.052, + "cache_write": 0.325 + } }, - "MiniMaxAI/MiniMax-M2.5-TEE": { - "id": "MiniMaxAI/MiniMax-M2.5-TEE", - "name": "MiniMax M2.5 TEE", - "family": "minimax", - "attachment": false, + "qwen/qwen3.5-flash-02-23": { + "id": "qwen/qwen3.5-flash-02-23", + "name": "Qwen: Qwen3.5-Flash", + "family": "qwen", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "release_date": "2026-02-15", - "last_updated": "2026-02-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.1, "cache_read": 0.15 }, - "limit": { "context": 196608, "output": 65536 } + "release_date": "2026-02-25", + "last_updated": "2026-02-25", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.065, + "output": 0.26 + } }, - "MiniMaxAI/MiniMax-M2.1-TEE": { - "id": "MiniMaxAI/MiniMax-M2.1-TEE", - "name": "MiniMax M2.1 TEE", - "family": "minimax", - "attachment": false, + "qwen/qwen3.6-plus": { + "id": "qwen/qwen3.6-plus", + "name": "Qwen3.6 Plus", + "family": "qwen", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.27, "output": 1.12 }, - "limit": { "context": 196608, "output": 65536 } + "knowledge": "2025-04", + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.325, + "output": 1.95, + "cache_read": 0.0325, + "cache_write": 0.40625 + } }, - "deepseek-ai/DeepSeek-R1-Distill-Llama-70B": { - "id": "deepseek-ai/DeepSeek-R1-Distill-Llama-70B", - "name": "DeepSeek R1 Distill Llama 70B", - "family": "deepseek-thinking", + "qwen/qwen3-max": { + "id": "qwen/qwen3-max", + "name": "Qwen3 Max", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.03, "output": 0.11 }, - "limit": { "context": 131072, "output": 131072 } + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 1.2, + "output": 6, + "cache_read": 0.156, + "cache_write": 0.975 + } }, - "deepseek-ai/DeepSeek-V3.1-Terminus-TEE": { - "id": "deepseek-ai/DeepSeek-V3.1-Terminus-TEE", - "name": "DeepSeek V3.1 Terminus TEE", - "family": "deepseek", + "qwen/qwen3-coder:exacto": { + "id": "qwen/qwen3-coder:exacto", + "name": "Qwen3 Coder (exacto)", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.23, "output": 0.9 }, - "limit": { "context": 163840, "output": 65536 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.38, + "output": 1.53 + } }, - "deepseek-ai/DeepSeek-R1-0528-TEE": { - "id": "deepseek-ai/DeepSeek-R1-0528-TEE", - "name": "DeepSeek R1 0528 TEE", - "family": "deepseek-thinking", + "qwen/qwen3-30b-a3b-instruct-2507": { + "id": "qwen/qwen3-30b-a3b-instruct-2507", + "name": "Qwen3 30B A3B Instruct 2507", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-29", + "last_updated": "2025-07-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.4, "output": 1.75 }, - "limit": { "context": 163840, "output": 65536 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.2, + "output": 0.8 + } }, - "deepseek-ai/DeepSeek-V3-0324-TEE": { - "id": "deepseek-ai/DeepSeek-V3-0324-TEE", - "name": "DeepSeek V3 0324 TEE", - "family": "deepseek", - "attachment": false, + "qwen/qwen-3.6-27b": { + "id": "qwen/qwen-3.6-27b", + "name": "Qwen3.6 27B", + "family": "qwen", + "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.19, "output": 0.87, "cache_read": 0.095 }, - "limit": { "context": 163840, "output": 65536 } + "limit": { + "context": 262144, + "output": 81920 + }, + "cost": { + "input": 0.195, + "output": 1.56 + } }, - "deepseek-ai/DeepSeek-V3.2-TEE": { - "id": "deepseek-ai/DeepSeek-V3.2-TEE", - "name": "DeepSeek V3.2 TEE", - "family": "deepseek", + "qwen/qwen3-235b-a22b-thinking-2507": { + "id": "qwen/qwen3-235b-a22b-thinking-2507", + "name": "Qwen3 235B A22B Thinking 2507", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, - "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.28, "output": 0.42, "cache_read": 0.14 }, - "limit": { "context": 131072, "output": 65536 } - }, - "deepseek-ai/DeepSeek-V3.2-Speciale-TEE": { - "id": "deepseek-ai/DeepSeek-V3.2-Speciale-TEE", - "name": "DeepSeek V3.2 Speciale TEE", - "family": "deepseek", - "attachment": false, - "reasoning": true, - "tool_call": false, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-25", + "last_updated": "2025-07-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.27, "output": 0.41 }, - "limit": { "context": 163840, "output": 65536 } + "limit": { + "context": 262144, + "output": 81920 + }, + "cost": { + "input": 0.078, + "output": 0.312 + } }, - "deepseek-ai/DeepSeek-R1-TEE": { - "id": "deepseek-ai/DeepSeek-R1-TEE", - "name": "DeepSeek R1 TEE", - "family": "deepseek-thinking", + "qwen/qwen3-next-80b-a3b-thinking": { + "id": "qwen/qwen3-next-80b-a3b-thinking", + "name": "Qwen3 Next 80B A3B Thinking", + "family": "qwen", "attachment": false, "reasoning": true, - "tool_call": false, - "interleaved": { "field": "reasoning_content" }, + "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-09-11", + "last_updated": "2025-09-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 163840, "output": 163840 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.14, + "output": 1.4 + } }, - "deepseek-ai/DeepSeek-V3.1-TEE": { - "id": "deepseek-ai/DeepSeek-V3.1-TEE", - "name": "DeepSeek V3.1 TEE", - "family": "deepseek", + "qwen/qwen3-30b-a3b-thinking-2507": { + "id": "qwen/qwen3-30b-a3b-thinking-2507", + "name": "Qwen3 30B A3B Thinking 2507", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-29", + "last_updated": "2025-07-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 163840, "output": 65536 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.2, + "output": 0.8 + } }, - "deepseek-ai/DeepSeek-V3": { - "id": "deepseek-ai/DeepSeek-V3", - "name": "DeepSeek V3", - "family": "deepseek", + "qwen/qwen3-coder-flash": { + "id": "qwen/qwen3-coder-flash", + "name": "Qwen3 Coder Flash", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": true, + "tool_call": true, + "structured_output": false, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 163840, "output": 163840 } + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 66536 + }, + "cost": { + "input": 0.3, + "output": 1.5, + "cache_read": 0.039, + "cache_write": 0.24375 + } }, - "Qwen/Qwen3-30B-A3B": { - "id": "Qwen/Qwen3-30B-A3B", - "name": "Qwen3 30B A3B", + "qwen/qwen3-next-80b-a3b-instruct": { + "id": "qwen/qwen3-next-80b-a3b-instruct", + "name": "Qwen3 Next 80B A3B Instruct", "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-09-11", + "last_updated": "2025-09-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.06, "output": 0.22 }, - "limit": { "context": 40960, "output": 40960 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.14, + "output": 1.4 + } }, - "Qwen/Qwen2.5-Coder-32B-Instruct": { - "id": "Qwen/Qwen2.5-Coder-32B-Instruct", + "qwen/qwen-2.5-coder-32b-instruct": { + "id": "qwen/qwen-2.5-coder-32b-instruct", "name": "Qwen2.5 Coder 32B Instruct", "family": "qwen", "attachment": false, @@ -17292,2023 +25414,3424 @@ "tool_call": false, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2024-11-11", + "last_updated": "2024-11-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.03, "output": 0.11 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 32768, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "Qwen/Qwen2.5-VL-72B-Instruct-TEE": { - "id": "Qwen/Qwen2.5-VL-72B-Instruct-TEE", - "name": "Qwen2.5 VL 72B Instruct TEE", + "qwen/qwen3-coder-30b-a3b-instruct": { + "id": "qwen/qwen3-coder-30b-a3b-instruct", + "name": "Qwen3 Coder 30B A3B Instruct", "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-31", + "last_updated": "2025-07-31", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 160000, + "output": 65536 + }, + "cost": { + "input": 0.07, + "output": 0.27 + } }, - "Qwen/Qwen3Guard-Gen-0.6B": { - "id": "Qwen/Qwen3Guard-Gen-0.6B", - "name": "Qwen3Guard Gen 0.6B", + "qwen/qwen3-coder": { + "id": "qwen/qwen3-coder", + "name": "Qwen3 Coder", "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.01, "output": 0.01, "cache_read": 0.005 }, - "limit": { "context": 32768, "output": 8192 } - }, - "Qwen/Qwen3-32B": { - "id": "Qwen/Qwen3-32B", - "name": "Qwen3 32B", - "family": "qwen", - "attachment": false, - "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.08, "output": 0.24, "cache_read": 0.04 }, - "limit": { "context": 40960, "output": 40960 } + "limit": { + "context": 262144, + "output": 66536 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "Qwen/Qwen3-14B": { - "id": "Qwen/Qwen3-14B", - "name": "Qwen3 14B", + "qwen/qwen3.5-plus-02-15": { + "id": "qwen/qwen3.5-plus-02-15", + "name": "Qwen3.5 Plus 2026-02-15", "family": "qwen", - "attachment": false, + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.05, "output": 0.22 }, - "limit": { "context": 40960, "output": 40960 } + "knowledge": "2025-04", + "release_date": "2026-02-16", + "last_updated": "2026-02-16", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.4, + "output": 2.4 + } }, - "Qwen/Qwen3-Coder-Next": { - "id": "Qwen/Qwen3-Coder-Next", - "name": "Qwen3 Coder Next", + "qwen/qwen3-235b-a22b-07-25": { + "id": "qwen/qwen3-235b-a22b-07-25", + "name": "Qwen3 235B A22B Instruct 2507", "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04-28", + "last_updated": "2025-07-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.07, "output": 0.3 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0.15, + "output": 0.85 + } }, - "Qwen/Qwen3-235B-A22B": { - "id": "Qwen/Qwen3-235B-A22B", - "name": "Qwen3 235B A22B", - "family": "qwen", - "attachment": false, + "google/gemini-2.5-pro-preview-05-06": { + "id": "google/gemini-2.5-pro-preview-05-06", + "name": "Gemini 2.5 Pro Preview 05-06", + "family": "gemini-pro", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 40960, "output": 40960 } + "knowledge": "2025-01", + "release_date": "2025-05-06", + "last_updated": "2025-05-06", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.31 + } }, - "Qwen/Qwen3-235B-A22B-Thinking-2507": { - "id": "Qwen/Qwen3-235B-A22B-Thinking-2507", - "name": "Qwen3 235B A22B Thinking 2507", - "family": "qwen", - "attachment": false, + "google/gemini-3.1-pro-preview-customtools": { + "id": "google/gemini-3.1-pro-preview-customtools", + "name": "Gemini 3.1 Pro Preview Custom Tools", + "family": "gemini-pro", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "interleaved": { + "field": "reasoning_details" + }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.11, "output": 0.6 }, - "limit": { "context": 262144, "output": 262144 } + "knowledge": "2025-01", + "release_date": "2026-02-19", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12, + "reasoning": 12, + "tiers": [ + { + "input": 4, + "output": 18, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 18, + "cache_read": 0.4 + } + } }, - "Qwen/Qwen3-Next-80B-A3B-Instruct": { - "id": "Qwen/Qwen3-Next-80B-A3B-Instruct", - "name": "Qwen3 Next 80B A3B Instruct", - "family": "qwen", - "attachment": false, + "google/gemma-3-4b-it:free": { + "id": "google/gemma-3-4b-it:free", + "name": "Gemma 3 4B (free)", + "family": "gemma", + "attachment": true, "reasoning": false, - "tool_call": true, - "structured_output": true, + "tool_call": false, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-03-13", + "last_updated": "2025-03-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.8 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 32768, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "Qwen/Qwen3.5-397B-A17B-TEE": { - "id": "Qwen/Qwen3.5-397B-A17B-TEE", - "name": "Qwen3.5 397B A17B TEE", - "family": "qwen", + "google/gemini-2.5-flash-lite-preview-09-2025": { + "id": "google/gemini-2.5-flash-lite-preview-09-2025", + "name": "Gemini 2.5 Flash Lite Preview 09-25", + "family": "gemini-flash-lite", "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "release_date": "2026-02-18", - "last_updated": "2026-02-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.39, "output": 2.34, "cache_read": 0.195 }, - "limit": { "context": 262144, "output": 65536 } + "knowledge": "2025-01", + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.025 + } }, - "Qwen/Qwen2.5-VL-32B-Instruct": { - "id": "Qwen/Qwen2.5-VL-32B-Instruct", - "name": "Qwen2.5 VL 32B Instruct", - "family": "qwen", - "attachment": false, + "google/gemini-2.0-flash-001": { + "id": "google/gemini-2.0-flash-001", + "name": "Gemini 2.0 Flash", + "family": "gemini-flash", + "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.05, "output": 0.22 }, - "limit": { "context": 16384, "output": 16384 } + "knowledge": "2024-06", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.025 + } }, - "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8-TEE": { - "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8-TEE", - "name": "Qwen3 Coder 480B A35B Instruct FP8 TEE", - "family": "qwen", - "attachment": false, + "google/gemma-3n-e4b-it": { + "id": "google/gemma-3n-e4b-it", + "name": "Gemma 3n 4B", + "family": "gemma", + "attachment": true, "reasoning": false, - "tool_call": true, - "structured_output": true, + "tool_call": false, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-06", + "release_date": "2025-05-20", + "last_updated": "2025-05-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.22, "output": 0.95, "cache_read": 0.11 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.02, + "output": 0.04 + } }, - "Qwen/Qwen3-30B-A3B-Instruct-2507": { - "id": "Qwen/Qwen3-30B-A3B-Instruct-2507", - "name": "Qwen3 30B A3B Instruct 2507", - "family": "qwen", - "attachment": false, - "reasoning": false, + "google/gemini-3.1-flash-lite-preview": { + "id": "google/gemini-3.1-flash-lite-preview", + "name": "Gemini 3.1 Flash Lite Preview", + "family": "gemini-flash-lite", + "attachment": true, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.08, "output": 0.33 }, - "limit": { "context": 262144, "output": 262144 } + "release_date": "2026-03-03", + "last_updated": "2026-03-03", + "modalities": { + "input": ["text", "image", "video", "pdf", "audio"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.25, + "output": 1.5, + "reasoning": 1.5, + "cache_read": 0.025, + "cache_write": 0.083, + "input_audio": 0.5, + "output_audio": 0.5 + } }, - "Qwen/Qwen3-235B-A22B-Instruct-2507-TEE": { - "id": "Qwen/Qwen3-235B-A22B-Instruct-2507-TEE", - "name": "Qwen3 235B A22B Instruct 2507 TEE", - "family": "qwen", - "attachment": false, + "google/gemma-3n-e4b-it:free": { + "id": "google/gemma-3n-e4b-it:free", + "name": "Gemma 3n 4B (free)", + "family": "gemma", + "attachment": true, "reasoning": false, - "tool_call": true, - "structured_output": true, + "tool_call": false, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-06", + "release_date": "2025-05-20", + "last_updated": "2025-05-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.08, "output": 0.55, "cache_read": 0.04 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 8192, + "output": 2000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "Qwen/Qwen3-VL-235B-A22B-Instruct": { - "id": "Qwen/Qwen3-VL-235B-A22B-Instruct", - "name": "Qwen3 VL 235B A22B Instruct", - "family": "qwen", - "attachment": false, - "reasoning": false, + "google/gemini-3.1-pro-preview": { + "id": "google/gemini-3.1-pro-preview", + "name": "Gemini 3.1 Pro Preview", + "family": "gemini-pro", + "attachment": true, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_details" + }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 262144, "output": 262144 } + "knowledge": "2025-01", + "release_date": "2026-02-19", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12, + "reasoning": 12, + "tiers": [ + { + "input": 4, + "output": 18, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 18, + "cache_read": 0.4 + } + } }, - "Qwen/Qwen2.5-72B-Instruct": { - "id": "Qwen/Qwen2.5-72B-Instruct", - "name": "Qwen2.5 72B Instruct", - "family": "qwen", - "attachment": false, - "reasoning": false, + "google/gemini-3-flash-preview": { + "id": "google/gemini-3-flash-preview", + "name": "Gemini 3 Flash Preview", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_details" + }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.13, "output": 0.52 }, - "limit": { "context": 32768, "output": 32768 } + "knowledge": "2025-01", + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.05 + } }, - "chutesai/Mistral-Small-3.2-24B-Instruct-2506": { - "id": "chutesai/Mistral-Small-3.2-24B-Instruct-2506", - "name": "Mistral Small 3.2 24B Instruct 2506", - "family": "chutesai", - "attachment": false, - "reasoning": false, + "google/gemini-3-pro-preview": { + "id": "google/gemini-3-pro-preview", + "name": "Gemini 3 Pro Preview", + "family": "gemini-pro", + "attachment": true, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_details" + }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.06, "output": 0.18 }, - "limit": { "context": 131072, "output": 131072 } + "knowledge": "2025-01", + "release_date": "2025-11-18", + "last_updated": "2025-11", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "output": 66000 + }, + "cost": { + "input": 2, + "output": 12 + } }, - "chutesai/Mistral-Small-3.1-24B-Instruct-2503": { - "id": "chutesai/Mistral-Small-3.1-24B-Instruct-2503", - "name": "Mistral Small 3.1 24B Instruct 2503", - "family": "chutesai", - "attachment": false, + "google/gemma-3n-e2b-it:free": { + "id": "google/gemma-3n-e2b-it:free", + "name": "Gemma 3n 2B (free)", + "family": "gemma", + "attachment": true, "reasoning": false, - "tool_call": true, - "structured_output": true, + "tool_call": false, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-06", + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.03, "output": 0.11, "cache_read": 0.015 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 8192, + "output": 2000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "moonshotai/Kimi-K2-Thinking-TEE": { - "id": "moonshotai/Kimi-K2-Thinking-TEE", - "name": "Kimi K2 Thinking TEE", - "family": "kimi-thinking", + "google/gemma-2-9b-it": { + "id": "google/gemma-2-9b-it", + "name": "Gemma 2 9B", + "family": "gemma", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-06", + "release_date": "2024-06-28", + "last_updated": "2024-06-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.4, "output": 1.75 }, - "limit": { "context": 262144, "output": 65535 } + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 0.03, + "output": 0.09 + } }, - "moonshotai/Kimi-K2-Instruct-0905": { - "id": "moonshotai/Kimi-K2-Instruct-0905", - "name": "Kimi K2 Instruct 0905", - "family": "kimi", - "attachment": false, - "reasoning": false, + "google/gemma-4-31b-it": { + "id": "google/gemma-4-31b-it", + "name": "Gemma 4 31B", + "family": "gemma", + "attachment": true, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.39, "output": 1.9, "cache_read": 0.195 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.14, + "output": 0.4 + } }, - "moonshotai/Kimi-K2.5-TEE": { - "id": "moonshotai/Kimi-K2.5-TEE", - "name": "Kimi K2.5 TEE", - "family": "kimi", - "attachment": false, + "google/gemini-2.5-pro-preview-06-05": { + "id": "google/gemini-2.5-pro-preview-06-05", + "name": "Gemini 2.5 Pro Preview 06-05", + "family": "gemini-pro", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 3 }, - "limit": { "context": 262144, "output": 65535 } + "knowledge": "2025-01", + "release_date": "2025-06-05", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.31 + } }, - "OpenGVLab/InternVL3-78B-TEE": { - "id": "OpenGVLab/InternVL3-78B-TEE", - "name": "InternVL3 78B TEE", - "family": "opengvlab", - "attachment": false, + "google/gemma-3-12b-it": { + "id": "google/gemma-3-12b-it", + "name": "Gemma 3 12B", + "family": "gemma", + "attachment": true, "reasoning": false, "tool_call": false, "structured_output": true, "temperature": true, - "release_date": "2025-01-06", - "last_updated": "2026-01-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-03-13", + "last_updated": "2025-03-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.39 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.03, + "output": 0.1 + } }, - "XiaomiMiMo/MiMo-V2-Flash": { - "id": "XiaomiMiMo/MiMo-V2-Flash", - "name": "MiMo V2 Flash", - "family": "mimo", - "attachment": false, - "reasoning": true, + "google/gemma-3-27b-it:free": { + "id": "google/gemma-3-27b-it:free", + "name": "Gemma 3 27B (free)", + "family": "gemma", + "attachment": true, + "reasoning": false, "tool_call": true, - "structured_output": false, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-03-12", + "last_updated": "2025-03-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.09, "output": 0.29 }, - "limit": { "context": 262144, "output": 32000 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "tngtech/TNG-R1T-Chimera-TEE": { - "id": "tngtech/TNG-R1T-Chimera-TEE", - "name": "TNG R1T Chimera TEE", - "family": "tngtech", - "attachment": false, + "google/gemini-2.5-flash": { + "id": "google/gemini-2.5-flash", + "name": "Gemini 2.5 Flash", + "family": "gemini-flash", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.25, "output": 0.85 }, - "limit": { "context": 163840, "output": 65536 } + "knowledge": "2025-01", + "release_date": "2025-07-17", + "last_updated": "2025-07-17", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.0375 + } }, - "tngtech/TNG-R1T-Chimera-Turbo": { - "id": "tngtech/TNG-R1T-Chimera-Turbo", - "name": "TNG R1T Chimera Turbo", - "attachment": false, + "google/gemini-3.1-flash-image-preview": { + "id": "google/gemini-3.1-flash-image-preview", + "name": "Gemini 3.1 Flash Image Preview (Nano Banana 2)", + "family": "gemini-flash", + "attachment": true, "reasoning": true, - "tool_call": true, + "tool_call": false, "structured_output": true, "temperature": true, - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.22, "output": 0.6 }, - "limit": { "context": 163840, "output": 65536 } - }, - "tngtech/DeepSeek-R1T-Chimera": { - "id": "tngtech/DeepSeek-R1T-Chimera", - "name": "DeepSeek R1T Chimera", - "family": "tngtech", - "attachment": false, - "reasoning": true, - "tool_call": false, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, - "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 163840, "output": 163840 } + "knowledge": "2025-01", + "release_date": "2026-02-26", + "last_updated": "2026-02-26", + "modalities": { + "input": ["text", "image"], + "output": ["text", "image"] + }, + "open_weights": false, + "limit": { + "context": 65536, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 3 + } }, - "tngtech/DeepSeek-TNG-R1T2-Chimera": { - "id": "tngtech/DeepSeek-TNG-R1T2-Chimera", - "name": "DeepSeek TNG R1T2 Chimera", - "family": "tngtech", - "attachment": false, + "google/gemma-4-31b-it:free": { + "id": "google/gemma-4-31b-it:free", + "name": "Gemma 4 31B (free)", + "family": "gemma", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.25, "output": 0.85 }, - "limit": { "context": 163840, "output": 163840 } + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "mistralai/Devstral-2-123B-Instruct-2512-TEE": { - "id": "mistralai/Devstral-2-123B-Instruct-2512-TEE", - "name": "Devstral 2 123B Instruct 2512 TEE", - "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01-10", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.05, "output": 0.22 }, - "limit": { "context": 262144, "output": 65536 } - } - } - }, - "atomic-chat": { - "id": "atomic-chat", - "env": ["ATOMIC_CHAT_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "http://127.0.0.1:1337/v1", - "name": "Atomic Chat", - "doc": "https://atomic.chat", - "models": { - "gemma-4-E4B-it-IQ4_XS": { - "id": "gemma-4-E4B-it-IQ4_XS", - "name": "Gemma 4 E4B Instruct (IQ4_XS)", + "google/gemma-3-12b-it:free": { + "id": "google/gemma-3-12b-it:free", + "name": "Gemma 3 12B (free)", "family": "gemma", - "attachment": false, + "attachment": true, "reasoning": false, "tool_call": false, "temperature": true, - "release_date": "2026-04-02", - "last_updated": "2026-04-02", - "modalities": {"input":["text"],"output":["text"]}, + "knowledge": "2024-10", + "release_date": "2025-03-13", + "last_updated": "2025-03-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": {"input":0,"output":0}, - "limit": {"context":32768,"output":8192} + "limit": { + "context": 32768, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gemma-4-E4B-it-MLX-4bit": { - "id": "gemma-4-E4B-it-MLX-4bit", - "name": "Gemma 4 E4B Instruct (MLX 4-bit)", + "google/gemma-3-4b-it": { + "id": "google/gemma-3-4b-it", + "name": "Gemma 3 4B", "family": "gemma", - "attachment": false, + "attachment": true, "reasoning": false, "tool_call": false, "temperature": true, - "release_date": "2026-04-02", - "last_updated": "2026-04-02", - "modalities": {"input":["text"],"output":["text"]}, + "knowledge": "2024-10", + "release_date": "2025-03-13", + "last_updated": "2025-03-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": {"input":0,"output":0}, - "limit": {"context":32768,"output":8192} + "limit": { + "context": 96000, + "output": 96000 + }, + "cost": { + "input": 0.01703, + "output": 0.06815 + } }, - "Qwen3_5-9B-Q4_K_M": { - "id": "Qwen3_5-9B-Q4_K_M", - "name": "Qwen 3.5 9B (Q4_K_M)", - "family": "qwen", + "google/gemini-2.5-flash-preview-09-2025": { + "id": "google/gemini-2.5-flash-preview-09-2025", + "name": "Gemini 2.5 Flash Preview 09-25", + "family": "gemini-flash", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-03-05", - "last_updated": "2026-04-04", - "modalities": {"input":["text","image"],"output":["text"]}, - "open_weights": true, - "cost": {"input":0,"output":0}, - "limit": {"context":32768,"output":8192} + "knowledge": "2025-01", + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.031 + } }, - "Meta-Llama-3_1-8B-Instruct-GGUF": { - "id": "Meta-Llama-3_1-8B-Instruct-GGUF", - "name": "Meta Llama 3.1 8B Instruct (GGUF)", - "family": "llama", - "attachment": false, + "google/gemma-3-27b-it": { + "id": "google/gemma-3-27b-it", + "name": "Gemma 3 27B", + "family": "gemma", + "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": {"input":["text"],"output":["text"]}, + "knowledge": "2024-10", + "release_date": "2025-03-12", + "last_updated": "2025-03-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": {"input":0,"output":0}, - "limit": {"context":131072,"output":4096} + "limit": { + "context": 96000, + "output": 96000 + }, + "cost": { + "input": 0.04, + "output": 0.15 + } }, - "Qwen3_5-9B-MLX-4bit": { - "id": "Qwen3_5-9B-MLX-4bit", - "name": "Qwen 3.5 9B (MLX 4-bit)", - "family": "qwen", + "google/gemma-4-26b-a4b-it": { + "id": "google/gemma-4-26b-a4b-it", + "name": "Gemma 4 26B A4B", + "family": "gemma", "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2026-03-05", - "last_updated": "2026-04-04", - "modalities": {"input":["text","image"],"output":["text"]}, - "open_weights": true, - "cost": {"input":0,"output":0}, - "limit": {"context":32768,"output":8192} - } - } - }, - "lmstudio": { - "id": "lmstudio", - "env": ["LMSTUDIO_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "http://127.0.0.1:1234/v1", - "name": "LMStudio", - "doc": "https://lmstudio.ai/models", - "models": { - "openai/gpt-oss-20b": { - "id": "openai/gpt-oss-20b", - "name": "GPT OSS 20B", - "family": "gpt-oss", - "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-04-03", + "last_updated": "2026-04-03", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.13, + "output": 0.4 + } }, - "qwen/qwen3-coder-30b": { - "id": "qwen/qwen3-coder-30b", - "name": "Qwen3 Coder 30B", - "family": "qwen", - "attachment": false, - "reasoning": false, + "google/gemma-4-26b-a4b-it:free": { + "id": "google/gemma-4-26b-a4b-it:free", + "name": "Gemma 4 26B A4B (free)", + "family": "gemma", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-04-03", + "last_updated": "2026-04-03", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "qwen/qwen3-30b-a3b-2507": { - "id": "qwen/qwen3-30b-a3b-2507", - "name": "Qwen3 30B A3B 2507", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-30", - "last_updated": "2025-07-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 16384 } - } - } - }, - "kimi-for-coding": { - "id": "kimi-for-coding", - "env": ["KIMI_API_KEY"], - "npm": "@ai-sdk/anthropic", - "api": "https://api.kimi.com/coding/v1", - "name": "Kimi For Coding", - "doc": "https://www.kimi.com/coding/docs/en/third-party-agents.html", - "models": { - "kimi-k2-thinking": { - "id": "kimi-k2-thinking", - "name": "Kimi K2 Thinking", - "family": "kimi-thinking", - "attachment": false, + "google/gemini-2.5-flash-lite": { + "id": "google/gemini-2.5-flash-lite", + "name": "Gemini 2.5 Flash Lite", + "family": "gemini-flash-lite", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-11", - "last_updated": "2025-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 262144, "output": 32768 } + "knowledge": "2025-01", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.025 + } }, - "k2p5": { - "id": "k2p5", + "moonshotai/kimi-k2.5": { + "id": "moonshotai/kimi-k2.5", "name": "Kimi K2.5", - "family": "kimi-thinking", - "attachment": false, + "family": "kimi", + "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_details" + }, "structured_output": true, "temperature": true, "knowledge": "2025-01", - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 262144, "output": 32768 } - } - } - }, - "alibaba-cn": { - "id": "alibaba-cn", - "env": ["DASHSCOPE_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://dashscope.aliyuncs.com/compatible-mode/v1", - "name": "Alibaba (China)", - "doc": "https://www.alibabacloud.com/help/en/model-studio/models", - "models": { - "qwen-math-plus": { - "id": "qwen-math-plus", - "name": "Qwen Math Plus", - "family": "qwen", + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 3, + "cache_read": 0.1 + } + }, + "moonshotai/kimi-k2-0905": { + "id": "moonshotai/kimi-k2-0905", + "name": "Kimi K2 Instruct 0905", + "family": "kimi", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-08-16", - "last_updated": "2024-09-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.574, "output": 1.721 }, - "limit": { "context": 4096, "output": 3072 } + "knowledge": "2024-10", + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 16384 + }, + "cost": { + "input": 0.6, + "output": 2.5 + } }, - "qwen2-5-72b-instruct": { - "id": "qwen2-5-72b-instruct", - "name": "Qwen2.5 72B Instruct", - "family": "qwen", - "attachment": false, - "reasoning": false, + "moonshotai/kimi-k2.6": { + "id": "moonshotai/kimi-k2.6", + "name": "Kimi K2.6", + "family": "kimi", + "attachment": true, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_details" + }, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-09", - "last_updated": "2024-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-20", + "last_updated": "2026-04-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.574, "output": 1.721 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.95, + "output": 4, + "cache_read": 0.16 + } }, - "qwen3-coder-30b-a3b-instruct": { - "id": "qwen3-coder-30b-a3b-instruct", - "name": "Qwen3-Coder 30B-A3B Instruct", - "family": "qwen", + "moonshotai/kimi-k2-0905:exacto": { + "id": "moonshotai/kimi-k2-0905:exacto", + "name": "Kimi K2 Instruct 0905 (exacto)", + "family": "kimi", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.216, "output": 0.861 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 262144, + "output": 16384 + }, + "cost": { + "input": 0.6, + "output": 2.5 + } }, - "qwen3-8b": { - "id": "qwen3-8b", - "name": "Qwen3 8B", - "family": "qwen", + "moonshotai/kimi-k2": { + "id": "moonshotai/kimi-k2", + "name": "Kimi K2", + "family": "kimi", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-07-11", + "last_updated": "2025-07-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.072, "output": 0.287, "reasoning": 0.717 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.55, + "output": 2.2 + } }, - "qwen-mt-plus": { - "id": "qwen-mt-plus", - "name": "Qwen-MT Plus", - "family": "qwen", + "moonshotai/kimi-k2-thinking": { + "id": "moonshotai/kimi-k2-thinking", + "name": "Kimi K2 Thinking", + "family": "kimi-thinking", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_details" + }, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-01", - "last_updated": "2025-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.259, "output": 0.775 }, - "limit": { "context": 16384, "output": 8192 } + "knowledge": "2024-08", + "release_date": "2025-11-06", + "last_updated": "2025-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 2.5, + "cache_read": 0.15 + } }, - "qwen3.5-plus": { - "id": "qwen3.5-plus", - "name": "Qwen3.5 Plus", - "family": "qwen", - "attachment": false, + "anthropic/claude-opus-4.1": { + "id": "anthropic/claude-opus-4.1", + "name": "Claude Opus 4.1", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-02-16", - "last_updated": "2026-02-16", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.573, "output": 3.44, "reasoning": 3.44 }, - "limit": { "context": 1000000, "output": 65536 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "deepseek-v3-2-exp": { - "id": "deepseek-v3-2-exp", - "name": "DeepSeek V3.2 Exp", - "family": "deepseek", - "attachment": false, - "reasoning": false, + "anthropic/claude-3.7-sonnet": { + "id": "anthropic/claude-3.7-sonnet", + "name": "Claude Sonnet 3.7", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-01", + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.287, "output": 0.431 }, - "limit": { "context": 131072, "output": 65536 } + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "deepseek-r1-distill-llama-70b": { - "id": "deepseek-r1-distill-llama-70b", - "name": "DeepSeek R1 Distill Llama 70B", - "family": "deepseek-thinking", - "attachment": false, + "anthropic/claude-opus-4.6": { + "id": "anthropic/claude-opus-4.6", + "name": "Claude Opus 4.6", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.287, "output": 0.861 }, - "limit": { "context": 32768, "output": 16384 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25, + "tiers": [ + { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5 + } + } }, - "qwen2-5-omni-7b": { - "id": "qwen2-5-omni-7b", - "name": "Qwen2.5-Omni 7B", - "family": "qwen", - "attachment": false, - "reasoning": false, + "anthropic/claude-opus-4.7": { + "id": "anthropic/claude-opus-4.7", + "name": "Claude Opus 4.7", + "family": "claude-opus", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-12", - "last_updated": "2024-12", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text", "audio"] }, - "open_weights": true, - "cost": { "input": 0.087, "output": 0.345, "input_audio": 5.448 }, - "limit": { "context": 32768, "output": 2048 } + "structured_output": true, + "temperature": false, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25, + "tiers": [ + { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5 + } + } }, - "qwen-plus-character": { - "id": "qwen-plus-character", - "name": "Qwen Plus Character", - "family": "qwen", - "attachment": false, - "reasoning": false, + "anthropic/claude-sonnet-4": { + "id": "anthropic/claude-sonnet-4", + "name": "Claude Sonnet 4", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-01", - "last_updated": "2024-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.115, "output": 0.287 }, - "limit": { "context": 32768, "output": 4096 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75, + "tiers": [ + { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5 + } + } }, - "qwen-turbo": { - "id": "qwen-turbo", - "name": "Qwen Turbo", - "family": "qwen", - "attachment": false, + "anthropic/claude-sonnet-4.5": { + "id": "anthropic/claude-sonnet-4.5", + "name": "Claude Sonnet 4.5", + "family": "claude-sonnet", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-11-01", - "last_updated": "2025-07-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.044, "output": 0.087, "reasoning": 0.431 }, - "limit": { "context": 1000000, "output": 16384 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75, + "tiers": [ + { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5 + } + } }, - "qwen-vl-max": { - "id": "qwen-vl-max", - "name": "Qwen-VL Max", - "family": "qwen", - "attachment": false, - "reasoning": false, + "anthropic/claude-opus-4.5": { + "id": "anthropic/claude-opus-4.5", + "name": "Claude Opus 4.5", + "family": "claude-opus", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-04-08", - "last_updated": "2025-08-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-05-30", + "release_date": "2025-11-24", + "last_updated": "2025-11-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.23, "output": 0.574 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "qwen-doc-turbo": { - "id": "qwen-doc-turbo", - "name": "Qwen Doc Turbo", - "family": "qwen", - "attachment": false, - "reasoning": false, + "anthropic/claude-haiku-4.5": { + "id": "anthropic/claude-haiku-4.5", + "name": "Claude Haiku 4.5", + "family": "claude-haiku", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-01", - "last_updated": "2024-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.087, "output": 0.144 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } }, - "qwq-32b": { - "id": "qwq-32b", - "name": "QwQ 32B", - "family": "qwen", - "attachment": false, + "anthropic/claude-sonnet-4.6": { + "id": "anthropic/claude-sonnet-4.6", + "name": "Claude Sonnet 4.6", + "family": "claude-sonnet", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-12", - "last_updated": "2024-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.287, "output": 0.861 }, - "limit": { "context": 131072, "output": 8192 } + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-02-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75, + "tiers": [ + { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5 + } + } }, - "kimi-k2-thinking": { - "id": "kimi-k2-thinking", - "name": "Moonshot Kimi K2 Thinking", - "family": "kimi", + "deepseek/deepseek-v4-flash": { + "id": "deepseek/deepseek-v4-flash", + "name": "DeepSeek V4 Flash", + "family": "deepseek-flash", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "release_date": "2025-11-06", - "last_updated": "2025-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.574, "output": 2.294 }, - "limit": { "context": 262144, "output": 16384 } + "limit": { + "context": 1048576, + "output": 393216 + }, + "cost": { + "input": 0.14, + "output": 0.28, + "cache_read": 0.028 + } }, - "qwen-omni-turbo-realtime": { - "id": "qwen-omni-turbo-realtime", - "name": "Qwen-Omni Turbo Realtime", - "family": "qwen", + "deepseek/deepseek-v4-pro": { + "id": "deepseek/deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-05-08", - "last_updated": "2025-05-08", - "modalities": { "input": ["text", "image", "audio"], "output": ["text", "audio"] }, - "open_weights": false, - "cost": { "input": 0.23, "output": 0.918, "input_audio": 3.584, "output_audio": 7.168 }, - "limit": { "context": 32768, "output": 2048 } + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 393216 + }, + "cost": { + "input": 1.74, + "output": 3.48, + "cache_read": 0.145 + } }, - "deepseek-r1": { - "id": "deepseek-r1", - "name": "DeepSeek R1", - "family": "deepseek-thinking", - "attachment": false, + "x-ai/grok-4.3": { + "id": "x-ai/grok-4.3", + "name": "Grok 4.3", + "family": "grok", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-05-01", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.574, "output": 2.294 }, - "limit": { "context": 131072, "output": 16384 } + "limit": { + "context": 1000000, + "output": 1000000 + }, + "cost": { + "input": 1.25, + "output": 2.5, + "cache_read": 0.2, + "context_over_200k": { + "input": 2.5, + "output": 5, + "cache_read": 0.4 + }, + "tiers": [ + { + "input": 2.5, + "output": 5, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ] + } }, - "qwen-vl-plus": { - "id": "qwen-vl-plus", - "name": "Qwen-VL Plus", - "family": "qwen", - "attachment": false, - "reasoning": false, + "openai/gpt-5.5": { + "id": "openai/gpt-5.5", + "name": "GPT-5.5", + "family": "gpt", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-01-25", - "last_updated": "2025-08-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-12-01", + "release_date": "2026-04-23", + "last_updated": "2026-04-23", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.115, "output": 0.287 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5, + "context_over_200k": { + "input": 10, + "output": 45, + "cache_read": 1 + }, + "tiers": [ + { + "input": 10, + "output": 45, + "cache_read": 1, + "tier": { + "type": "context", + "size": 272000 + } + } + ] + } }, - "qwen-max": { - "id": "qwen-max", - "name": "Qwen Max", - "family": "qwen", - "attachment": false, - "reasoning": false, + "openai/gpt-5.4": { + "id": "openai/gpt-5.4", + "name": "GPT-5.4", + "family": "gpt", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-04-03", - "last_updated": "2025-01-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.345, "output": 1.377 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 2.5, + "output": 15, + "cache_read": 0.25, + "context_over_200k": { + "input": 5, + "output": 22.5, + "cache_read": 0.5 + }, + "tiers": [ + { + "input": 5, + "output": 22.5, + "cache_read": 0.5, + "tier": { + "type": "context", + "size": 272000 + } + } + ] + } }, - "qvq-max": { - "id": "qvq-max", - "name": "QVQ Max", - "family": "qvq", - "attachment": false, + "openai/gpt-5.5-pro": { + "id": "openai/gpt-5.5-pro", + "name": "GPT-5.5 Pro", + "family": "gpt-pro", + "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-03-25", - "last_updated": "2025-03-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-12-01", + "release_date": "2026-04-23", + "last_updated": "2026-04-23", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.147, "output": 4.588 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 30, + "output": 180, + "context_over_200k": { + "input": 60, + "output": 270 + }, + "tiers": [ + { + "input": 60, + "output": 270, + "tier": { + "type": "context", + "size": 272000 + } + } + ] + } }, - "qwen-long": { - "id": "qwen-long", - "name": "Qwen Long", - "family": "qwen", - "attachment": false, - "reasoning": false, + "google/gemini-2.5-pro": { + "id": "google/gemini-2.5-pro", + "name": "Gemini 2.5 Pro", + "family": "gemini-pro", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-01-25", - "last_updated": "2025-01-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.072, "output": 0.287 }, - "limit": { "context": 10000000, "output": 8192 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125, + "context_over_200k": { + "input": 2.5, + "output": 15, + "cache_read": 0.25 + }, + "tiers": [ + { + "input": 2.5, + "output": 15, + "cache_read": 0.25, + "tier": { + "type": "context", + "size": 200000 + } + } + ] + } }, - "qwen-deep-research": { - "id": "qwen-deep-research", - "name": "Qwen Deep Research", - "family": "qwen", - "attachment": false, - "reasoning": false, + "anthropic/claude-opus-4": { + "id": "anthropic/claude-opus-4", + "name": "Claude Opus 4", + "family": "claude-opus", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-01", - "last_updated": "2024-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 7.742, "output": 23.367 }, - "limit": { "context": 1000000, "output": 32768 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "moonshot-kimi-k2-instruct": { - "id": "moonshot-kimi-k2-instruct", - "name": "Moonshot Kimi K2 Instruct", - "family": "kimi", - "attachment": false, + "anthropic/claude-3.5-haiku": { + "id": "anthropic/claude-3.5-haiku", + "name": "Claude Haiku 3.5", + "family": "claude-haiku", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": false, "temperature": true, - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.574, "output": 2.294 }, - "limit": { "context": 131072, "output": 8192 } + "knowledge": "2024-07-31", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 4, + "cache_read": 0.08, + "cache_write": 1 + } }, - "kimi-k2.5": { - "id": "kimi-k2.5", - "name": "Moonshot Kimi K2.5", - "family": "kimi", + "xiaomi/mimo-v2.5-pro": { + "id": "xiaomi/mimo-v2.5-pro", + "name": "Xiaomi: MiMo-V2.5-Pro", + "family": "mimo", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": false, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.574, "output": 2.411 }, - "limit": { "context": 262144, "output": 32768 } + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.2, + "context_over_200k": { + "input": 2, + "output": 6, + "cache_read": 0.4 + }, + "tiers": [ + { + "input": 2, + "output": 6, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 256000 + } + } + ] + } }, - "qwen2-5-coder-32b-instruct": { - "id": "qwen2-5-coder-32b-instruct", - "name": "Qwen2.5-Coder 32B Instruct", - "family": "qwen", - "attachment": false, - "reasoning": false, + "xiaomi/mimo-v2-omni": { + "id": "xiaomi/mimo-v2-omni", + "name": "Xiaomi: MiMo-V2-Omni", + "family": "mimo", + "attachment": true, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_details" + }, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-11", - "last_updated": "2024-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.287, "output": 0.861 }, - "limit": { "context": 131072, "output": 8192 } + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0.4, + "output": 2, + "cache_read": 0.08 + } }, - "deepseek-v3": { - "id": "deepseek-v3", - "name": "DeepSeek V3", - "family": "deepseek", - "attachment": false, - "reasoning": false, + "xiaomi/mimo-v2.5": { + "id": "xiaomi/mimo-v2.5", + "name": "Xiaomi: MiMo-V2.5", + "family": "mimo", + "attachment": true, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_details" + }, "temperature": true, - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.287, "output": 1.147 }, - "limit": { "context": 65536, "output": 8192 } + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 0.4, + "output": 2, + "cache_read": 0.08, + "context_over_200k": { + "input": 0.8, + "output": 4, + "cache_read": 0.16 + }, + "tiers": [ + { + "input": 0.8, + "output": 4, + "cache_read": 0.16, + "tier": { + "type": "context", + "size": 256000 + } + } + ] + } }, - "qwq-plus": { - "id": "qwq-plus", - "name": "QwQ Plus", - "family": "qwen", + "xiaomi/mimo-v2-pro": { + "id": "xiaomi/mimo-v2-pro", + "name": "Xiaomi: MiMo-V2-Pro", + "family": "mimo", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_details" + }, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-03-05", - "last_updated": "2025-03-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.23, "output": 0.574 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.2, + "context_over_200k": { + "input": 2, + "output": 6, + "cache_read": 0.4 + }, + "tiers": [ + { + "input": 2, + "output": 6, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 256000 + } + } + ] + } }, - "qwen3-omni-flash": { - "id": "qwen3-omni-flash", - "name": "Qwen3-Omni Flash", - "family": "qwen", + "xiaomi/mimo-v2-flash": { + "id": "xiaomi/mimo-v2-flash", + "name": "Xiaomi: MiMo-V2-Flash", + "family": "mimo", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_details" + }, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text", "audio"] }, - "open_weights": false, - "cost": { "input": 0.058, "output": 0.23, "input_audio": 3.584, "output_audio": 7.168 }, - "limit": { "context": 65536, "output": 16384 } - }, - "qwen2-5-coder-7b-instruct": { - "id": "qwen2-5-coder-7b-instruct", - "name": "Qwen2.5-Coder 7B Instruct", - "family": "qwen", + "knowledge": "2024-12-01", + "release_date": "2025-12-16", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.3, + "cache_read": 0.01 + } + } + } + }, + "fireworks-ai": { + "id": "fireworks-ai", + "env": ["FIREWORKS_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.fireworks.ai/inference/v1/", + "name": "Fireworks AI", + "doc": "https://fireworks.ai/docs/", + "models": { + "accounts/fireworks/models/glm-5p1": { + "id": "accounts/fireworks/models/glm-5p1", + "name": "GLM 5.1", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-11", - "last_updated": "2024-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-01", + "last_updated": "2026-04-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.144, "output": 0.287 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 202800, + "output": 131072 + }, + "cost": { + "input": 1.4, + "output": 4.4, + "cache_read": 0.26 + } }, - "deepseek-r1-distill-qwen-1-5b": { - "id": "deepseek-r1-distill-qwen-1-5b", - "name": "DeepSeek R1 Distill Qwen 1.5B", - "family": "qwen", + "accounts/fireworks/models/deepseek-v3p2": { + "id": "accounts/fireworks/models/deepseek-v3p2", + "name": "DeepSeek V3.2", + "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 32768, "output": 16384 } + "knowledge": "2025-09", + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 160000, + "output": 160000 + }, + "cost": { + "input": 0.56, + "output": 1.68, + "cache_read": 0.28 + } }, - "deepseek-r1-distill-qwen-14b": { - "id": "deepseek-r1-distill-qwen-14b", - "name": "DeepSeek R1 Distill Qwen 14B", - "family": "qwen", + "accounts/fireworks/models/minimax-m2p5": { + "id": "accounts/fireworks/models/minimax-m2p5", + "name": "MiniMax-M2.5", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.144, "output": 0.431 }, - "limit": { "context": 32768, "output": 16384 } + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 196608, + "output": 196608 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03 + } }, - "qwen3-14b": { - "id": "qwen3-14b", - "name": "Qwen3 14B", - "family": "qwen", + "accounts/fireworks/models/glm-4p5-air": { + "id": "accounts/fireworks/models/glm-4p5-air", + "name": "GLM 4.5 Air", + "family": "glm-air", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-08-01", + "last_updated": "2025-08-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.144, "output": 0.574, "reasoning": 1.434 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.22, + "output": 0.88 + } }, - "qwen-plus": { - "id": "qwen-plus", - "name": "Qwen Plus", - "family": "qwen", + "accounts/fireworks/models/glm-5": { + "id": "accounts/fireworks/models/glm-5", + "name": "GLM 5", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-01-25", - "last_updated": "2025-09-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.115, "output": 0.287, "reasoning": 1.147 }, - "limit": { "context": 1000000, "output": 32768 } + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3.2, + "cache_read": 0.5 + } }, - "deepseek-r1-distill-qwen-7b": { - "id": "deepseek-r1-distill-qwen-7b", - "name": "DeepSeek R1 Distill Qwen 7B", - "family": "qwen", + "accounts/fireworks/models/deepseek-v3p1": { + "id": "accounts/fireworks/models/deepseek-v3p1", + "name": "DeepSeek V3.1", + "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.072, "output": 0.144 }, - "limit": { "context": 32768, "output": 16384 } - }, - "qwen2-5-7b-instruct": { - "id": "qwen2-5-7b-instruct", - "name": "Qwen2.5 7B Instruct", - "family": "qwen", + "knowledge": "2025-07", + "release_date": "2025-08-21", + "last_updated": "2025-08-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 163840 + }, + "cost": { + "input": 0.56, + "output": 1.68 + } + }, + "accounts/fireworks/models/kimi-k2p6": { + "id": "accounts/fireworks/models/kimi-k2p6", + "name": "Kimi K2.6", + "family": "kimi-thinking", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-09", - "last_updated": "2024-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-17", + "last_updated": "2026-04-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.072, "output": 0.144 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.95, + "output": 4, + "cache_read": 0.16 + } }, - "qwen2-5-32b-instruct": { - "id": "qwen2-5-32b-instruct", - "name": "Qwen2.5 32B Instruct", - "family": "qwen", + "accounts/fireworks/models/kimi-k2-instruct": { + "id": "accounts/fireworks/models/kimi-k2-instruct", + "name": "Kimi K2 Instruct", + "family": "kimi", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-09", - "last_updated": "2024-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-07-11", + "last_updated": "2025-07-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.287, "output": 0.861 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1, + "output": 3 + } }, - "qwen3-omni-flash-realtime": { - "id": "qwen3-omni-flash-realtime", - "name": "Qwen3-Omni Flash Realtime", + "accounts/fireworks/models/qwen3p6-plus": { + "id": "accounts/fireworks/models/qwen3p6-plus", + "name": "Qwen 3.6 Plus", "family": "qwen", - "attachment": false, - "reasoning": false, + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image", "audio"], "output": ["text", "audio"] }, + "release_date": "2026-04-04", + "last_updated": "2026-04-04", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.23, "output": 0.918, "input_audio": 3.584, "output_audio": 7.168 }, - "limit": { "context": 65536, "output": 16384 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.1 + } }, - "qwen-math-turbo": { - "id": "qwen-math-turbo", - "name": "Qwen Math Turbo", - "family": "qwen", + "accounts/fireworks/models/minimax-m2p1": { + "id": "accounts/fireworks/models/minimax-m2p1", + "name": "MiniMax-M2.1", + "family": "minimax", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-09-19", - "last_updated": "2024-09-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.287, "output": 0.861 }, - "limit": { "context": 4096, "output": 3072 } + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 200000, + "output": 200000 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03 + } }, - "tongyi-intent-detect-v3": { - "id": "tongyi-intent-detect-v3", - "name": "Tongyi Intent Detect V3", - "family": "yi", + "accounts/fireworks/models/minimax-m2p7": { + "id": "accounts/fireworks/models/minimax-m2p7", + "name": "MiniMax-M2.7", + "family": "minimax", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-01", - "last_updated": "2024-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.058, "output": 0.144 }, - "limit": { "context": 8192, "output": 1024 } + "release_date": "2026-04-12", + "last_updated": "2026-04-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 196608, + "output": 196608 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03 + } }, - "qwen3-coder-480b-a35b-instruct": { - "id": "qwen3-coder-480b-a35b-instruct", - "name": "Qwen3-Coder 480B-A35B Instruct", - "family": "qwen", + "accounts/fireworks/models/glm-4p7": { + "id": "accounts/fireworks/models/glm-4p7", + "name": "GLM 4.7", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.861, "output": 3.441 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 198000, + "output": 198000 + }, + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.3 + } }, - "qwen3-next-80b-a3b-thinking": { - "id": "qwen3-next-80b-a3b-thinking", - "name": "Qwen3-Next 80B-A3B (Thinking)", - "family": "qwen", + "accounts/fireworks/models/glm-4p5": { + "id": "accounts/fireworks/models/glm-4p5", + "name": "GLM 4.5", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, "knowledge": "2025-04", - "release_date": "2025-09", - "last_updated": "2025-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-07-29", + "last_updated": "2025-07-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.144, "output": 1.434 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.55, + "output": 2.19 + } }, - "MiniMax-M2.5": { - "id": "MiniMax-M2.5", - "name": "MiniMax-M2.5", - "family": "minimax", + "accounts/fireworks/models/kimi-k2p5": { + "id": "accounts/fireworks/models/kimi-k2p5", + "name": "Kimi K2.5", + "family": "kimi-thinking", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.6, + "output": 3, + "cache_read": 0.1 + } }, - "qwen3-vl-30b-a3b": { - "id": "qwen3-vl-30b-a3b", - "name": "Qwen3-VL 30B-A3B", - "family": "qwen", + "accounts/fireworks/models/gpt-oss-20b": { + "id": "accounts/fireworks/models/gpt-oss-20b", + "name": "GPT OSS 20B", + "family": "gpt-oss", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.108, "output": 0.431, "reasoning": 1.076 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.05, + "output": 0.2 + } }, - "qwen3-next-80b-a3b-instruct": { - "id": "qwen3-next-80b-a3b-instruct", - "name": "Qwen3-Next 80B-A3B Instruct", - "family": "qwen", + "accounts/fireworks/models/gpt-oss-120b": { + "id": "accounts/fireworks/models/gpt-oss-120b", + "name": "GPT OSS 120B", + "family": "gpt-oss", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09", - "last_updated": "2025-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.144, "output": 0.574 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "deepseek-r1-distill-qwen-32b": { - "id": "deepseek-r1-distill-qwen-32b", - "name": "DeepSeek R1 Distill Qwen 32B", - "family": "qwen", + "accounts/fireworks/models/kimi-k2-thinking": { + "id": "accounts/fireworks/models/kimi-k2-thinking", + "name": "Kimi K2 Thinking", + "family": "kimi-thinking", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.287, "output": 0.861 }, - "limit": { "context": 32768, "output": 16384 } + "release_date": "2025-11-06", + "last_updated": "2025-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.6, + "output": 2.5, + "cache_read": 0.3 + } }, - "qwen-mt-turbo": { - "id": "qwen-mt-turbo", - "name": "Qwen-MT Turbo", - "family": "qwen", + "accounts/fireworks/routers/kimi-k2p5-turbo": { + "id": "accounts/fireworks/routers/kimi-k2p5-turbo", + "name": "Kimi K2.5 Turbo", + "family": "kimi-thinking", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-01", - "last_updated": "2025-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.101, "output": 0.28 }, - "limit": { "context": 16384, "output": 8192 } + "knowledge": "2025-01", + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } }, - "qwen3-vl-plus": { - "id": "qwen3-vl-plus", - "name": "Qwen3-VL Plus", - "family": "qwen", + "accounts/fireworks/models/deepseek-v4-pro": { + "id": "accounts/fireworks/models/deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-23", - "last_updated": "2025-09-23", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.143353, "output": 1.433525, "reasoning": 4.300576 }, - "limit": { "context": 262144, "output": 32768 } - }, - "deepseek-v3-1": { - "id": "deepseek-v3-1", - "name": "DeepSeek V3.1", - "family": "deepseek", + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 1.74, + "output": 3.48, + "cache_read": 0.15 + } + } + } + }, + "kimi-for-coding": { + "id": "kimi-for-coding", + "env": ["KIMI_API_KEY"], + "npm": "@ai-sdk/anthropic", + "api": "https://api.kimi.com/coding/v1", + "name": "Kimi For Coding", + "doc": "https://www.kimi.com/coding/docs/en/third-party-agents.html", + "models": { + "k2p6": { + "id": "k2p6", + "name": "Kimi K2.6", + "family": "kimi-thinking", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.574, "output": 1.721 }, - "limit": { "context": 131072, "output": 65536 } + "knowledge": "2025-01", + "release_date": "2026-04", + "last_updated": "2026-04", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "qwen3-235b-a22b": { - "id": "qwen3-235b-a22b", - "name": "Qwen3 235B-A22B", - "family": "qwen", + "k2p5": { + "id": "k2p5", + "name": "Kimi K2.5", + "family": "kimi-thinking", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.287, "output": 1.147, "reasoning": 2.868 }, - "limit": { "context": 131072, "output": 16384 } + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "qwen2-5-vl-7b-instruct": { - "id": "qwen2-5-vl-7b-instruct", - "name": "Qwen2.5-VL 7B Instruct", - "family": "qwen", + "kimi-k2-thinking": { + "id": "kimi-k2-thinking", + "name": "Kimi K2 Thinking", + "family": "kimi-thinking", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-09", - "last_updated": "2024-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-11", + "last_updated": "2025-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.287, "output": 0.717 }, - "limit": { "context": 131072, "output": 8192 } - }, - "glm-5": { - "id": "glm-5", - "name": "GLM-5", + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } + } + } + }, + "moark": { + "id": "moark", + "env": ["MOARK_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://moark.com/v1", + "name": "Moark", + "doc": "https://moark.com/docs/openapi/v1#tag/%E6%96%87%E6%9C%AC%E7%94%9F%E6%88%90", + "models": { + "GLM-4.7": { + "id": "GLM-4.7", + "name": "GLM-4.7", "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.86, "output": 3.15 }, - "limit": { "context": 202752, "output": 16384 } - }, - "qwen-vl-ocr": { - "id": "qwen-vl-ocr", - "name": "Qwen-VL OCR", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": false, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-10-28", - "last_updated": "2025-04-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.717, "output": 0.717 }, - "limit": { "context": 34096, "output": 4096 } + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 3.5, + "output": 14 + } }, - "qwen-omni-turbo": { - "id": "qwen-omni-turbo", - "name": "Qwen-Omni Turbo", - "family": "qwen", + "MiniMax-M2.1": { + "id": "MiniMax-M2.1", + "name": "MiniMax-M2.1", + "family": "minimax", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-01-19", - "last_updated": "2025-03-26", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text", "audio"] }, - "open_weights": false, - "cost": { "input": 0.058, "output": 0.23, "input_audio": 3.584, "output_audio": 7.168 }, - "limit": { "context": 32768, "output": 2048 } - }, - "qwen3.5-397b-a17b": { - "id": "qwen3.5-397b-a17b", - "name": "Qwen3.5 397B-A17B", - "family": "qwen", + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 2.1, + "output": 8.4 + } + } + } + }, + "opencode-go": { + "id": "opencode-go", + "env": ["OPENCODE_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://opencode.ai/zen/go/v1", + "name": "OpenCode Go", + "doc": "https://opencode.ai/docs/zen", + "models": { + "minimax-m2.7": { + "id": "minimax-m2.7", + "name": "MiniMax M2.7", + "family": "minimax-m2.7", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-02-16", - "last_updated": "2026-02-16", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.43, "output": 2.58, "reasoning": 2.58 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 204800, + "output": 131072 + }, + "provider": { + "npm": "@ai-sdk/anthropic" + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06 + } }, - "qwen-flash": { - "id": "qwen-flash", - "name": "Qwen Flash", - "family": "qwen", - "attachment": false, + "kimi-k2.5": { + "id": "kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi-k2.5", + "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.022, "output": 0.216 }, - "limit": { "context": 1000000, "output": 32768 } + "knowledge": "2024-10", + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.6, + "output": 3, + "cache_read": 0.1 + } }, - "qwen2-5-math-7b-instruct": { - "id": "qwen2-5-math-7b-instruct", - "name": "Qwen2.5-Math 7B Instruct", - "family": "qwen", - "attachment": false, - "reasoning": false, + "mimo-v2.5-pro": { + "id": "mimo-v2.5-pro", + "name": "MiMo V2.5 Pro", + "family": "mimo-v2.5-pro", + "attachment": true, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-09", - "last_updated": "2024-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.144, "output": 0.287 }, - "limit": { "context": 4096, "output": 3072 } + "limit": { + "context": 1048576, + "output": 128000 + }, + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.2, + "tiers": [ + { + "input": 2, + "output": 6, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 256000 + } + } + ], + "context_over_200k": { + "input": 2, + "output": 6, + "cache_read": 0.4 + } + } }, - "deepseek-r1-0528": { - "id": "deepseek-r1-0528", - "name": "DeepSeek R1 0528", - "family": "deepseek-thinking", + "glm-5": { + "id": "glm-5", + "name": "GLM-5", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-05-28", - "last_updated": "2025-05-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.574, "output": 2.294 }, - "limit": { "context": 131072, "output": 16384 } + "knowledge": "2025-04", + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 32768 + }, + "cost": { + "input": 1, + "output": 3.2, + "cache_read": 0.2 + } }, - "qwen3-max": { - "id": "qwen3-max", - "name": "Qwen3 Max", - "family": "qwen", - "attachment": false, - "reasoning": false, + "mimo-v2-omni": { + "id": "mimo-v2-omni", + "name": "MiMo V2 Omni", + "family": "mimo-v2-omni", + "attachment": true, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-23", - "last_updated": "2025-09-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.861, "output": 3.441 }, - "limit": { "context": 262144, "output": 65536 } - }, - "qwen2-5-vl-72b-instruct": { - "id": "qwen2-5-vl-72b-instruct", - "name": "Qwen2.5-VL 72B Instruct", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-09", - "last_updated": "2024-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text", "image", "audio", "pdf"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 2.294, "output": 6.881 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 262144, + "output": 128000 + }, + "status": "deprecated", + "cost": { + "input": 0.4, + "output": 2, + "cache_read": 0.08 + } }, - "qwen3-32b": { - "id": "qwen3-32b", - "name": "Qwen3 32B", - "family": "qwen", - "attachment": false, + "mimo-v2.5": { + "id": "mimo-v2.5", + "name": "MiMo V2.5", + "family": "mimo-v2.5", + "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.287, "output": 1.147, "reasoning": 2.868 }, - "limit": { "context": 131072, "output": 16384 } - }, - "qwen2-5-math-72b-instruct": { - "id": "qwen2-5-math-72b-instruct", - "name": "Qwen2.5-Math 72B Instruct", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-09", - "last_updated": "2024-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.574, "output": 1.721 }, - "limit": { "context": 4096, "output": 3072 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 0.4, + "output": 2, + "cache_read": 0.08, + "tiers": [ + { + "input": 0.8, + "output": 4, + "cache_read": 0.16, + "tier": { + "type": "context", + "size": 256000 + } + } + ], + "context_over_200k": { + "input": 0.8, + "output": 4, + "cache_read": 0.16 + } + } }, - "deepseek-r1-distill-llama-8b": { - "id": "deepseek-r1-distill-llama-8b", - "name": "DeepSeek R1 Distill Llama 8B", - "family": "deepseek-thinking", - "attachment": false, + "qwen3.6-plus": { + "id": "qwen3.6-plus", + "name": "Qwen3.6 Plus", + "family": "qwen3.6", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 32768, "output": 16384 } - }, - "qwen3-asr-flash": { - "id": "qwen3-asr-flash", - "name": "Qwen3-ASR Flash", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "knowledge": "2024-04", - "release_date": "2025-09-08", - "last_updated": "2025-09-08", - "modalities": { "input": ["audio"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.032, "output": 0.032 }, - "limit": { "context": 53248, "output": 4096 } - }, - "qwen3-coder-flash": { - "id": "qwen3-coder-flash", - "name": "Qwen3 Coder Flash", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.144, "output": 0.574 }, - "limit": { "context": 1000000, "output": 65536 } + "limit": { + "context": 262144, + "output": 65536 + }, + "provider": { + "npm": "@ai-sdk/anthropic" + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.05, + "cache_write": 0.625 + } }, - "qwen2-5-14b-instruct": { - "id": "qwen2-5-14b-instruct", - "name": "Qwen2.5 14B Instruct", - "family": "qwen", + "glm-5.1": { + "id": "glm-5.1", + "name": "GLM-5.1", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-09", - "last_updated": "2024-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2026-04-07", + "last_updated": "2026-04-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.144, "output": 0.431 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 202752, + "output": 32768 + }, + "cost": { + "input": 1.4, + "output": 4.4, + "cache_read": 0.26 + } }, - "qwen3-vl-235b-a22b": { - "id": "qwen3-vl-235b-a22b", - "name": "Qwen3-VL 235B-A22B", - "family": "qwen", + "deepseek-v4-flash": { + "id": "deepseek-v4-flash", + "name": "DeepSeek V4 Flash", + "family": "deepseek-flash", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.286705, "output": 1.14682, "reasoning": 2.867051 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 0.14, + "output": 0.28, + "cache_read": 0.0028 + } }, - "qwen3.5-flash": { - "id": "qwen3.5-flash", - "name": "Qwen3.5 Flash", - "family": "qwen", + "kimi-k2.6": { + "id": "kimi-k2.6", + "name": "Kimi K2.6", + "family": "kimi-k2.6", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-02-23", - "last_updated": "2026-02-23", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.172, "output": 1.72, "reasoning": 1.72 }, - "limit": { "context": 1000000, "output": 65536 } + "knowledge": "2024-10", + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.95, + "output": 4, + "cache_read": 0.16 + } }, - "qwen3-coder-plus": { - "id": "qwen3-coder-plus", - "name": "Qwen3 Coder Plus", - "family": "qwen", + "deepseek-v4-pro": { + "id": "deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1, "output": 5 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 1.74, + "output": 3.48, + "cache_read": 0.0145 + } }, - "MiniMax/MiniMax-M2.5": { - "id": "MiniMax/MiniMax-M2.5", + "minimax-m2.5": { + "id": "minimax-m2.5", "name": "MiniMax M2.5", - "family": "minimax", + "family": "minimax-m2.5", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, + "knowledge": "2025-01", "release_date": "2026-02-12", "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.301, "output": 1.205 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 204800, + "output": 65536 + }, + "provider": { + "npm": "@ai-sdk/anthropic" + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03 + } }, - "kimi/kimi-k2.5": { - "id": "kimi/kimi-k2.5", - "name": "kimi/kimi-k2.5", - "family": "kimi", - "attachment": false, + "mimo-v2-pro": { + "id": "mimo-v2-pro", + "name": "MiMo V2 Pro", + "family": "mimo-v2-pro", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, - "temperature": false, - "knowledge": "2025-01", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 3, "cache_read": 0.1 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 1048576, + "output": 128000 + }, + "status": "deprecated", + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.2, + "tiers": [ + { + "input": 2, + "output": 6, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 256000 + } + } + ], + "context_over_200k": { + "input": 2, + "output": 6, + "cache_read": 0.4 + } + } }, - "siliconflow/deepseek-v3.1-terminus": { - "id": "siliconflow/deepseek-v3.1-terminus", - "name": "siliconflow/deepseek-v3.1-terminus", - "family": "deepseek", - "attachment": false, + "qwen3.5-plus": { + "id": "qwen3.5-plus", + "name": "Qwen3.5 Plus", + "family": "qwen3.5", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-09-29", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.27, "output": 1 }, - "limit": { "context": 163840, "output": 65536 } - }, - "siliconflow/deepseek-v3-0324": { - "id": "siliconflow/deepseek-v3-0324", - "name": "siliconflow/deepseek-v3-0324", - "family": "deepseek", - "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-12-26", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2026-02-16", + "last_updated": "2026-02-16", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 1 }, - "limit": { "context": 163840, "output": 163840 } - }, - "siliconflow/deepseek-r1-0528": { - "id": "siliconflow/deepseek-r1-0528", - "name": "siliconflow/deepseek-r1-0528", - "family": "deepseek-thinking", + "limit": { + "context": 262144, + "output": 65536 + }, + "provider": { + "npm": "@ai-sdk/anthropic" + }, + "cost": { + "input": 0.2, + "output": 1.2, + "cache_read": 0.02, + "cache_write": 0.25 + } + } + } + }, + "databricks": { + "id": "databricks", + "env": ["DATABRICKS_HOST", "DATABRICKS_TOKEN"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://${DATABRICKS_HOST}/ai-gateway/mlflow/v1", + "name": "Databricks", + "doc": "https://docs.databricks.com/aws/en/machine-learning/foundation-models/", + "models": { + "databricks-gpt-oss-120b": { + "id": "databricks-gpt-oss-120b", + "name": "GPT OSS 120B", + "family": "gpt-oss", "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-05-28", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 2.18 }, - "limit": { "context": 163840, "output": 32768 } + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.072, + "output": 0.28 + } }, - "siliconflow/deepseek-v3.2": { - "id": "siliconflow/deepseek-v3.2", - "name": "siliconflow/deepseek-v3.2", - "family": "deepseek", + "databricks-gpt-oss-20b": { + "id": "databricks-gpt-oss-20b", + "name": "GPT OSS 20B", + "family": "gpt-oss", "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-12-03", - "last_updated": "2025-12-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.27, "output": 0.42 }, - "limit": { "context": 163840, "output": 65536 } - } - } - }, - "requesty": { - "id": "requesty", - "env": ["REQUESTY_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://router.requesty.ai/v1", - "name": "Requesty", - "doc": "https://requesty.ai/solution/llm-routing/models", - "models": { - "openai/gpt-5.2-codex": { - "id": "openai/gpt-5.2-codex", - "name": "GPT-5.2-Codex", - "family": "gpt-codex", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-08-31", - "release_date": "2026-01-14", - "last_updated": "2026-01-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "output": 128000 } + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.05, + "output": 0.2 + } }, - "openai/gpt-5.1-codex-mini": { - "id": "openai/gpt-5.1-codex-mini", - "name": "GPT-5.1-Codex-Mini", - "family": "gpt-codex", + "databricks-claude-haiku-4-5": { + "id": "databricks-claude-haiku-4-5", + "name": "Claude Haiku 4.5 (latest)", + "family": "claude-haiku", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.025 }, - "limit": { "context": 400000, "output": 100000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } }, - "openai/gpt-5.4-pro": { - "id": "openai/gpt-5.4-pro", - "name": "GPT-5.4 Pro", - "family": "gpt-pro", + "databricks-claude-sonnet-4-6": { + "id": "databricks-claude-sonnet-4-6", + "name": "Claude Sonnet 4.6", + "family": "claude-sonnet", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": false, - "temperature": false, + "temperature": true, "knowledge": "2025-08-31", - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-02-17", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 30, "output": 180, "cache_read": 30 }, - "limit": { "context": 1050000, "input": 922000, "output": 128000 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "openai/gpt-5-pro": { - "id": "openai/gpt-5-pro", - "name": "GPT-5 Pro", - "family": "gpt-pro", + "databricks-gemini-3-pro": { + "id": "databricks-gemini-3-pro", + "name": "Gemini 3 Pro Preview", + "family": "gemini-pro", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-10-06", - "last_updated": "2025-10-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 120 }, - "limit": { "context": 400000, "output": 272000 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2, + "context_over_200k": { + "input": 4, + "output": 18, + "cache_read": 0.4 + }, + "tiers": [ + { + "input": 4, + "output": 18, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ] + } }, - "openai/gpt-5": { - "id": "openai/gpt-5", - "name": "GPT-5", - "family": "gpt", + "databricks-gemini-3-1-pro": { + "id": "databricks-gemini-3-1-pro", + "name": "Gemini 3.1 Pro Preview Custom Tools", + "family": "gemini-pro", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "audio", "image", "video"], "output": ["text", "audio", "image"] }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-02-19", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.13 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2, + "context_over_200k": { + "input": 4, + "output": 18, + "cache_read": 0.4 + }, + "tiers": [ + { + "input": 4, + "output": 18, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ] + } }, - "openai/gpt-5.3-codex": { - "id": "openai/gpt-5.3-codex", - "name": "GPT-5.3-Codex", - "family": "gpt-codex", + "databricks-gpt-5-4-mini": { + "id": "databricks-gpt-5-4-mini", + "name": "GPT-5.4 mini", + "family": "gpt-mini", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, "knowledge": "2025-08-31", - "release_date": "2026-02-24", - "last_updated": "2026-02-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "experimental": { + "modes": { + "fast": { + "cost": { + "input": 1.5, + "output": 9, + "cache_read": 0.15 + }, + "provider": { + "body": { + "service_tier": "priority" + } + } + } + } + }, + "cost": { + "input": 0.75, + "output": 4.5, + "cache_read": 0.075 + } }, - "openai/gpt-5-mini": { - "id": "openai/gpt-5-mini", - "name": "GPT-5 Mini", - "family": "gpt-mini", + "databricks-claude-opus-4-6": { + "id": "databricks-claude-opus-4-6", + "name": "Claude Opus 4.6", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.03 }, - "limit": { "context": 128000, "output": 32000 } - }, - "openai/gpt-4o-mini": { - "id": "openai/gpt-4o-mini", - "name": "GPT-4o Mini", - "family": "gpt-mini", - "attachment": true, - "reasoning": false, - "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.6, "cache_read": 0.08 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "experimental": { + "modes": { + "fast": { + "cost": { + "input": 30, + "output": 150, + "cache_read": 3, + "cache_write": 37.5 + }, + "provider": { + "body": { + "speed": "fast" + }, + "headers": { + "anthropic-beta": "fast-mode-2026-02-01" + } + } + } + } + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "openai/gpt-5.1-codex-max": { - "id": "openai/gpt-5.1-codex-max", - "name": "GPT-5.1-Codex-Max", - "family": "gpt-codex", + "databricks-gemini-2-5-pro": { + "id": "databricks-gemini-2-5-pro", + "name": "Gemini 2.5 Pro", + "family": "gemini-pro", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.1, "output": 9, "cache_read": 0.11 }, - "limit": { "context": 400000, "output": 128000 } - }, - "openai/gpt-4.1": { - "id": "openai/gpt-4.1", - "name": "GPT-4.1", - "family": "gpt", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125, + "context_over_200k": { + "input": 2.5, + "output": 15, + "cache_read": 0.25 + }, + "tiers": [ + { + "input": 2.5, + "output": 15, + "cache_read": 0.25, + "tier": { + "type": "context", + "size": 200000 + } + } + ] + } }, - "openai/gpt-5.1-chat": { - "id": "openai/gpt-5.1-chat", - "name": "GPT-5.1 Chat", - "family": "gpt-codex", + "databricks-gpt-5-nano": { + "id": "databricks-gpt-5-nano", + "name": "GPT-5 Nano", + "family": "gpt-nano", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.05, + "output": 0.4, + "cache_read": 0.005 + } }, - "openai/gpt-5.4": { - "id": "openai/gpt-5.4", + "databricks-gpt-5-4": { + "id": "databricks-gpt-5-4", "name": "GPT-5.4", "family": "gpt", "attachment": true, @@ -19319,187 +28842,141 @@ "knowledge": "2025-08-31", "release_date": "2026-03-05", "last_updated": "2026-03-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "experimental": { + "modes": { + "fast": { + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5 + }, + "provider": { + "body": { + "service_tier": "priority" + } + } + } + } + }, "cost": { "input": 2.5, "output": 15, "cache_read": 0.25, - "context_over_200k": { "input": 5, "output": 22.5, "cache_read": 0.5 } - }, - "limit": { "context": 1050000, "input": 922000, "output": 128000 } + "context_over_200k": { + "input": 5, + "output": 22.5, + "cache_read": 0.5 + }, + "tiers": [ + { + "input": 5, + "output": 22.5, + "cache_read": 0.5, + "tier": { + "type": "context", + "size": 272000 + } + } + ] + } }, - "openai/gpt-5-chat": { - "id": "openai/gpt-5-chat", - "name": "GPT-5 Chat (latest)", - "family": "gpt-codex", + "databricks-gemini-3-1-flash-lite": { + "id": "databricks-gemini-3-1-flash-lite", + "name": "Gemini 3.1 Flash Lite Preview", + "family": "gemini-flash-lite", "attachment": true, "reasoning": true, - "tool_call": false, + "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2024-09-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 400000, "output": 128000 } - }, - "openai/gpt-5.2-chat": { - "id": "openai/gpt-5.2-chat", - "name": "GPT-5.2 Chat", - "family": "gpt-codex", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-03-03", + "last_updated": "2026-03-03", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.25, + "output": 1.5, + "cache_read": 0.025, + "input_audio": 0.5 + } }, - "openai/gpt-5.2": { - "id": "openai/gpt-5.2", - "name": "GPT-5.2", + "databricks-gpt-5": { + "id": "databricks-gpt-5", + "name": "GPT-5", "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "output": 128000 } - }, - "openai/gpt-5.1": { - "id": "openai/gpt-5.1", - "name": "GPT-5.1", - "family": "gpt", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } - }, - "openai/gpt-4.1-mini": { - "id": "openai/gpt-4.1-mini", - "name": "GPT-4.1 Mini", - "family": "gpt-mini", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 1.6, "cache_read": 0.1 }, - "limit": { "context": 1047576, "output": 32768 } - }, - "openai/gpt-5-nano": { - "id": "openai/gpt-5-nano", - "name": "GPT-5 Nano", - "family": "gpt-nano", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2024-05-30", "release_date": "2025-08-07", "last_updated": "2025-08-07", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.05, "output": 0.4, "cache_read": 0.01 }, - "limit": { "context": 16000, "output": 4000 } - }, - "openai/gpt-5-image": { - "id": "openai/gpt-5-image", - "name": "GPT-5 Image", - "family": "gpt", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2024-10-01", - "release_date": "2025-10-14", - "last_updated": "2025-10-14", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text", "image"] }, - "open_weights": false, - "cost": { "input": 5, "output": 10, "cache_read": 1.25 }, - "limit": { "context": 400000, "output": 128000 } - }, - "openai/gpt-5-codex": { - "id": "openai/gpt-5-codex", - "name": "GPT-5 Codex", - "family": "gpt-codex", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2024-10-01", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } - }, - "openai/o4-mini": { - "id": "openai/o4-mini", - "name": "o4 Mini", - "family": "o-mini", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-06", - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.28 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "openai/gpt-5.1-codex": { - "id": "openai/gpt-5.1-codex", - "name": "GPT-5.1-Codex", - "family": "gpt-codex", + "databricks-claude-opus-4-1": { + "id": "databricks-claude-opus-4-1", + "name": "Claude Opus 4.1 (latest)", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "openai/gpt-5.2-pro": { - "id": "openai/gpt-5.2-pro", - "name": "GPT-5.2 Pro", - "family": "gpt-pro", + "databricks-gpt-5-2": { + "id": "databricks-gpt-5-2", + "name": "GPT-5.2", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, @@ -19508,106 +28985,167 @@ "knowledge": "2025-08-31", "release_date": "2025-12-11", "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 21, "output": 168 }, - "limit": { "context": 400000, "output": 128000 } - }, - "anthropic/claude-opus-4-5": { - "id": "anthropic/claude-opus-4-5", - "name": "Claude Opus 4.5", - "family": "claude-opus", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-11-24", - "last_updated": "2025-11-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "anthropic/claude-opus-4-6": { - "id": "anthropic/claude-opus-4-6", - "name": "Claude Opus 4.6", - "family": "claude-opus", + "databricks-gpt-5-5": { + "id": "databricks-gpt-5-5", + "name": "GPT-5.5", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "knowledge": "2025-05-30", - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-12-01", + "release_date": "2026-04-23", + "last_updated": "2026-04-23", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "experimental": { + "modes": { + "fast": { + "cost": { + "input": 12.5, + "output": 75, + "cache_read": 1.25 + }, + "provider": { + "body": { + "service_tier": "priority" + } + } + } + } + }, "cost": { "input": 5, - "output": 25, + "output": 30, "cache_read": 0.5, - "cache_write": 6.25, - "context_over_200k": { "input": 10, "output": 37.5, "cache_read": 1, "cache_write": 12.5 } - }, - "limit": { "context": 1000000, "output": 128000 } + "context_over_200k": { + "input": 10, + "output": 45, + "cache_read": 1 + }, + "tiers": [ + { + "input": 10, + "output": 45, + "cache_read": 1, + "tier": { + "type": "context", + "size": 272000 + } + } + ] + } }, - "anthropic/claude-sonnet-4-6": { - "id": "anthropic/claude-sonnet-4-6", - "name": "Claude Sonnet 4.6", - "family": "claude-sonnet", + "databricks-gemini-3-flash": { + "id": "databricks-gemini-3-flash", + "name": "Gemini 3 Flash Preview", + "family": "gemini-flash", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2026-02-17", - "last_updated": "2026-02-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75, - "context_over_200k": { "input": 6, "output": 22.5, "cache_read": 0.6, "cache_write": 7.5 } + "limit": { + "context": 1048576, + "output": 65536 }, - "limit": { "context": 1000000, "output": 128000 } + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.05, + "input_audio": 1 + } }, - "anthropic/claude-opus-4-1": { - "id": "anthropic/claude-opus-4-1", - "name": "Claude Opus 4.1", + "databricks-claude-opus-4-5": { + "id": "databricks-claude-opus-4-5", + "name": "Claude Opus 4.5 (latest)", "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, "knowledge": "2025-03-31", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-11-24", + "last_updated": "2025-11-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "anthropic/claude-sonnet-4": { - "id": "anthropic/claude-sonnet-4", - "name": "Claude Sonnet 4", - "family": "claude-sonnet", + "databricks-gpt-5-mini": { + "id": "databricks-gpt-5-mini", + "name": "GPT-5 Mini", + "family": "gpt-mini", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.025 + } }, - "anthropic/claude-sonnet-4-5": { - "id": "anthropic/claude-sonnet-4-5", + "databricks-claude-sonnet-4": { + "id": "databricks-claude-sonnet-4", "name": "Claude Sonnet 4.5", "family": "claude-sonnet", "attachment": true, @@ -19617,20471 +29155,47731 @@ "knowledge": "2025-07-31", "release_date": "2025-09-29", "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 1000000, "output": 64000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "anthropic/claude-haiku-4-5": { - "id": "anthropic/claude-haiku-4-5", - "name": "Claude Haiku 4.5", - "family": "claude-haiku", + "databricks-gpt-5-1": { + "id": "databricks-gpt-5-1", + "name": "GPT-5.1", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-02-01", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 62000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.13 + } }, - "anthropic/claude-opus-4": { - "id": "anthropic/claude-opus-4", - "name": "Claude Opus 4", + "databricks-claude-opus-4-7": { + "id": "databricks-claude-opus-4-7", + "name": "Claude Opus 4.7", "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } - }, - "anthropic/claude-3-7-sonnet": { - "id": "anthropic/claude-3-7-sonnet", - "name": "Claude Sonnet 3.7", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-01", - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } - }, - "xai/grok-4-fast": { - "id": "xai/grok-4-fast", - "name": "Grok 4 Fast", - "family": "grok", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-19", - "last_updated": "2025-09-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05, "cache_write": 0.2 }, - "limit": { "context": 2000000, "output": 64000 } - }, - "xai/grok-4": { - "id": "xai/grok-4", - "name": "Grok 4", - "family": "grok", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-09", - "last_updated": "2025-09-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.75, "cache_write": 3 }, - "limit": { "context": 256000, "output": 64000 } - }, - "google/gemini-3-pro-preview": { - "id": "google/gemini-3-pro-preview", - "name": "Gemini 3 Pro", - "family": "gemini-pro", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "temperature": false, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 12, "cache_read": 0.2, "cache_write": 4.5 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "experimental": { + "modes": { + "fast": { + "cost": { + "input": 30, + "output": 150, + "cache_read": 3, + "cache_write": 37.5 + }, + "provider": { + "body": { + "speed": "fast" + }, + "headers": { + "anthropic-beta": "fast-mode-2026-02-01" + } + } + } + } + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "google/gemini-3-flash-preview": { - "id": "google/gemini-3-flash-preview", - "name": "Gemini 3 Flash", + "databricks-gemini-2-5-flash": { + "id": "databricks-gemini-2-5-flash", + "name": "Gemini 2.5 Flash", + "family": "gemini-flash", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, "knowledge": "2025-01", - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "release_date": "2025-03-20", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 3, "cache_read": 0.05, "cache_write": 1 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.03, + "input_audio": 1 + } }, - "google/gemini-2.5-pro": { - "id": "google/gemini-2.5-pro", - "name": "Gemini 2.5 Pro", - "family": "gemini-pro", + "databricks-gpt-5-4-nano": { + "id": "databricks-gpt-5-4-nano", + "name": "GPT-5.4 nano", + "family": "gpt-nano", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.31, "cache_write": 2.375 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.2, + "output": 1.25, + "cache_read": 0.02 + } }, - "google/gemini-2.5-flash": { - "id": "google/gemini-2.5-flash", - "name": "Gemini 2.5 Flash", - "family": "gemini-flash", + "databricks-claude-sonnet-4-5": { + "id": "databricks-claude-sonnet-4-5", + "name": "Claude Sonnet 4.5 (latest)", + "family": "claude-sonnet", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 2.5, "cache_read": 0.075, "cache_write": 0.55 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } } } }, - "friendli": { - "id": "friendli", - "env": ["FRIENDLI_TOKEN"], + "io-net": { + "id": "io-net", + "env": ["IOINTELLIGENCE_API_KEY"], "npm": "@ai-sdk/openai-compatible", - "api": "https://api.friendli.ai/serverless/v1", - "name": "Friendli", - "doc": "https://friendli.ai/docs/guides/serverless_endpoints/introduction", + "api": "https://api.intelligence.io.solutions/api/v1", + "name": "IO.NET", + "doc": "https://io.net/docs/guides/intelligence/io-intelligence", "models": { - "zai-org/GLM-4.7": { - "id": "zai-org/GLM-4.7", - "name": "GLM 4.7", - "family": "glm", + "Intel/Qwen3-Coder-480B-A35B-Instruct-int4-mixed-ar": { + "id": "Intel/Qwen3-Coder-480B-A35B-Instruct-int4-mixed-ar", + "name": "Qwen 3 Coder 480B", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "release_date": "2025-12-22", - "last_updated": "2026-01-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2025-01-15", + "last_updated": "2025-01-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "limit": { "context": 202752, "output": 202752 } + "limit": { + "context": 106000, + "output": 4096 + }, + "cost": { + "input": 0.22, + "output": 0.95, + "cache_read": 0.11, + "cache_write": 0.44 + } }, - "zai-org/GLM-5": { - "id": "zai-org/GLM-5", - "name": "GLM 5", - "family": "glm", + "Qwen/Qwen3-Next-80B-A3B-Instruct": { + "id": "Qwen/Qwen3-Next-80B-A3B-Instruct", + "name": "Qwen 3 Next 80B Instruct", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2025-01-10", + "last_updated": "2025-01-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1, "output": 3.2 }, - "limit": { "context": 202752, "output": 202752 } + "limit": { + "context": 262144, + "output": 4096 + }, + "cost": { + "input": 0.1, + "output": 0.8, + "cache_read": 0.05, + "cache_write": 0.2 + } }, - "meta-llama/Llama-3.1-8B-Instruct": { - "id": "meta-llama/Llama-3.1-8B-Instruct", - "name": "Llama 3.1 8B Instruct", - "family": "llama", + "Qwen/Qwen3-235B-A22B-Thinking-2507": { + "id": "Qwen/Qwen3-235B-A22B-Thinking-2507", + "name": "Qwen 3 235B Thinking", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-08-01", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2025-07-01", + "last_updated": "2025-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.1 }, - "limit": { "context": 131072, "output": 8000 } + "limit": { + "context": 262144, + "output": 4096 + }, + "cost": { + "input": 0.11, + "output": 0.6, + "cache_read": 0.055, + "cache_write": 0.22 + } }, - "meta-llama/Llama-3.3-70B-Instruct": { - "id": "meta-llama/Llama-3.3-70B-Instruct", - "name": "Llama 3.3 70B Instruct", - "family": "llama", + "Qwen/Qwen2.5-VL-32B-Instruct": { + "id": "Qwen/Qwen2.5-VL-32B-Instruct", + "name": "Qwen 2.5 VL 32B Instruct", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-08-01", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-09", + "release_date": "2024-11-01", + "last_updated": "2024-11-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 0.6 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 32000, + "output": 4096 + }, + "cost": { + "input": 0.05, + "output": 0.22, + "cache_read": 0.025, + "cache_write": 0.1 + } }, - "MiniMaxAI/MiniMax-M2.1": { - "id": "MiniMaxAI/MiniMax-M2.1", - "name": "MiniMax M2.1", + "zai-org/GLM-4.6": { + "id": "zai-org/GLM-4.6", + "name": "GLM 4.6", + "family": "glm", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "release_date": "2026-01-13", - "last_updated": "2026-01-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 196608, "output": 196608 } + "knowledge": "2024-10", + "release_date": "2024-11-15", + "last_updated": "2024-11-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 4096 + }, + "cost": { + "input": 0.4, + "output": 1.75, + "cache_read": 0.2, + "cache_write": 0.8 + } }, - "MiniMaxAI/MiniMax-M2.5": { - "id": "MiniMaxAI/MiniMax-M2.5", - "name": "MiniMax M2.5", - "family": "minimax", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, - "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 196608, "output": 196608 } - }, - "Qwen/Qwen3-235B-A22B-Instruct-2507": { - "id": "Qwen/Qwen3-235B-A22B-Instruct-2507", - "name": "Qwen3 235B A22B Instruct 2507", - "family": "qwen", + "mistralai/Magistral-Small-2506": { + "id": "mistralai/Magistral-Small-2506", + "name": "Magistral Small 2506", + "family": "magistral-small", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-29", - "last_updated": "2026-01-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 262144, "output": 262144 } - } - } - }, - "302ai": { - "id": "302ai", - "env": ["302AI_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.302.ai/v1", - "name": "302.AI", - "doc": "https://doc.302.ai", - "models": { - "qwen3-235b-a22b-instruct-2507": { - "id": "qwen3-235b-a22b-instruct-2507", - "name": "qwen3-235b-a22b-instruct-2507", + "knowledge": "2025-01", + "release_date": "2025-06-01", + "last_updated": "2025-06-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.5, + "output": 1.5, + "cache_read": 0.25, + "cache_write": 1 + } + }, + "mistralai/Mistral-Large-Instruct-2411": { + "id": "mistralai/Mistral-Large-Instruct-2411", + "name": "Mistral Large Instruct 2411", + "family": "mistral-large", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-30", - "last_updated": "2025-07-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2024-11-01", + "last_updated": "2024-11-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.29, "output": 1.143 }, - "limit": { "context": 128000, "output": 65536 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 2, + "output": 6, + "cache_read": 1, + "cache_write": 4 + } }, - "claude-opus-4-5-20251101": { - "id": "claude-opus-4-5-20251101", - "name": "claude-opus-4-5-20251101", - "attachment": true, + "mistralai/Mistral-Nemo-Instruct-2407": { + "id": "mistralai/Mistral-Nemo-Instruct-2407", + "name": "Mistral Nemo Instruct 2407", + "family": "mistral-nemo", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-11-25", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5, "output": 25 }, - "limit": { "context": 200000, "output": 64000 } + "knowledge": "2024-05", + "release_date": "2024-07-01", + "last_updated": "2024-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.02, + "output": 0.04, + "cache_read": 0.01, + "cache_write": 0.04 + } }, - "deepseek-v3.2-thinking": { - "id": "deepseek-v3.2-thinking", - "name": "DeepSeek-V3.2-Thinking", + "mistralai/Devstral-Small-2505": { + "id": "mistralai/Devstral-Small-2505", + "name": "Devstral Small 2505", + "family": "devstral", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, "knowledge": "2024-12", - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-05-01", + "last_updated": "2025-05-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.29, "output": 0.43 }, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.05, + "output": 0.22, + "cache_read": 0.025, + "cache_write": 0.1 + } }, - "gpt-5-pro": { - "id": "gpt-5-pro", - "name": "gpt-5-pro", - "attachment": true, + "meta-llama/Llama-3.3-70B-Instruct": { + "id": "meta-llama/Llama-3.3-70B-Instruct", + "name": "Llama 3.3 70B Instruct", + "family": "llama", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-10-08", - "last_updated": "2025-10-08", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 120 }, - "limit": { "context": 400000, "output": 272000 } + "knowledge": "2023-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.13, + "output": 0.38, + "cache_read": 0.065, + "cache_write": 0.26 + } }, - "gemini-2.5-flash-lite-preview-09-2025": { - "id": "gemini-2.5-flash-lite-preview-09-2025", - "name": "gemini-2.5-flash-lite-preview-09-2025", - "attachment": true, + "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8": { + "id": "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8", + "name": "Llama 4 Maverick 17B 128E Instruct", + "family": "llama", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-26", - "last_updated": "2025-09-26", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 1000000, "output": 65536 } + "knowledge": "2024-12", + "release_date": "2025-01-15", + "last_updated": "2025-01-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 430000, + "output": 4096 + }, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.075, + "cache_write": 0.3 + } }, - "kimi-k2-thinking": { - "id": "kimi-k2-thinking", - "name": "kimi-k2-thinking", + "meta-llama/Llama-3.2-90B-Vision-Instruct": { + "id": "meta-llama/Llama-3.2-90B-Vision-Instruct", + "name": "Llama 3.2 90B Vision Instruct", + "family": "llama", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.575, "output": 2.3 }, - "limit": { "context": 262144, "output": 262144 } + "knowledge": "2023-12", + "release_date": "2024-09-25", + "last_updated": "2024-09-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 16000, + "output": 4096 + }, + "cost": { + "input": 0.35, + "output": 0.4, + "cache_read": 0.175, + "cache_write": 0.7 + } }, - "qwen-max-latest": { - "id": "qwen-max-latest", - "name": "Qwen-Max-Latest", - "family": "qwen", + "deepseek-ai/DeepSeek-R1-0528": { + "id": "deepseek-ai/DeepSeek-R1-0528", + "name": "DeepSeek R1", + "family": "deepseek-thinking", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-11", - "release_date": "2024-04-03", - "last_updated": "2025-01-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.343, "output": 1.372 }, - "limit": { "context": 131072, "output": 8192 } + "knowledge": "2024-07", + "release_date": "2025-01-20", + "last_updated": "2025-05-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 2, + "output": 8.75, + "cache_read": 1, + "cache_write": 4 + } }, - "gpt-5.2-chat-latest": { - "id": "gpt-5.2-chat-latest", - "name": "gpt-5.2-chat-latest", - "attachment": true, + "openai/gpt-oss-20b": { + "id": "openai/gpt-oss-20b", + "name": "GPT-OSS 20B", + "family": "gpt-oss", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, "knowledge": "2024-10", - "release_date": "2025-12-12", - "last_updated": "2025-12-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14 }, - "limit": { "context": 128000, "output": 16384 } + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 64000, + "output": 4096 + }, + "cost": { + "input": 0.03, + "output": 0.14, + "cache_read": 0.015, + "cache_write": 0.06 + } }, - "gpt-5": { - "id": "gpt-5", - "name": "gpt-5", - "attachment": true, + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "name": "GPT-OSS 120B", + "family": "gpt-oss", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, "knowledge": "2024-10", - "release_date": "2025-08-08", - "last_updated": "2025-08-08", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 400000, "output": 128000 } + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 4096 + }, + "cost": { + "input": 0.04, + "output": 0.4, + "cache_read": 0.02, + "cache_write": 0.08 + } }, - "grok-4.1": { - "id": "grok-4.1", - "name": "grok-4.1", - "attachment": true, - "reasoning": false, + "moonshotai/Kimi-K2-Thinking": { + "id": "moonshotai/Kimi-K2-Thinking", + "name": "Kimi K2 Thinking", + "family": "kimi-thinking", + "attachment": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 10 }, - "limit": { "context": 200000, "output": 64000 } - }, - "chatgpt-4o-latest": { - "id": "chatgpt-4o-latest", - "name": "chatgpt-4o-latest", - "family": "gpt", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2023-09", - "release_date": "2024-08-08", - "last_updated": "2024-08-08", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-08", + "release_date": "2024-11-01", + "last_updated": "2024-11-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 15 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 32768, + "output": 4096 + }, + "cost": { + "input": 0.55, + "output": 2.25, + "cache_read": 0.275, + "cache_write": 1.1 + } }, - "gemini-3-pro-preview": { - "id": "gemini-3-pro-preview", - "name": "gemini-3-pro-preview", - "attachment": true, + "moonshotai/Kimi-K2-Instruct-0905": { + "id": "moonshotai/Kimi-K2-Instruct-0905", + "name": "Kimi K2 Instruct", + "family": "kimi", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-11-19", - "last_updated": "2025-11-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-08", + "release_date": "2024-09-05", + "last_updated": "2024-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 12 }, - "limit": { "context": 1000000, "output": 64000 } - }, - "MiniMax-M1": { - "id": "MiniMax-M1", - "name": "MiniMax-M1", - "family": "minimax", + "limit": { + "context": 32768, + "output": 4096 + }, + "cost": { + "input": 0.39, + "output": 1.9, + "cache_read": 0.195, + "cache_write": 0.78 + } + } + } + }, + "alibaba-cn": { + "id": "alibaba-cn", + "env": ["DASHSCOPE_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://dashscope.aliyuncs.com/compatible-mode/v1", + "name": "Alibaba (China)", + "doc": "https://www.alibabacloud.com/help/en/model-studio/models", + "models": { + "qwen3-235b-a22b": { + "id": "qwen3-235b-a22b", + "name": "Qwen3 235B-A22B", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-06-16", - "last_updated": "2025-06-16", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.132, "output": 1.254 }, - "limit": { "context": 1000000, "output": 128000 } + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.287, + "output": 1.147, + "reasoning": 2.868 + } }, - "gpt-4o": { - "id": "gpt-4o", - "name": "gpt-4o", - "family": "gpt", - "attachment": true, + "qwen-plus-character": { + "id": "qwen-plus-character", + "name": "Qwen Plus Character", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-09", - "release_date": "2024-05-13", - "last_updated": "2024-05-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-01", + "last_updated": "2024-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 32768, + "output": 4096 + }, + "cost": { + "input": 0.115, + "output": 0.287 + } }, - "grok-4-fast-non-reasoning": { - "id": "grok-4-fast-non-reasoning", - "name": "grok-4-fast-non-reasoning", - "attachment": true, + "qwen2-5-math-7b-instruct": { + "id": "qwen2-5-math-7b-instruct", + "name": "Qwen2.5-Math 7B Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-09-23", - "last_updated": "2025-09-23", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.5 }, - "limit": { "context": 2000000, "output": 30000 } + "knowledge": "2024-04", + "release_date": "2024-09", + "last_updated": "2024-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 4096, + "output": 3072 + }, + "cost": { + "input": 0.144, + "output": 0.287 + } }, - "kimi-k2-thinking-turbo": { - "id": "kimi-k2-thinking-turbo", - "name": "kimi-k2-thinking-turbo", + "kimi-k2.5": { + "id": "kimi-k2.5", + "name": "Moonshot Kimi K2.5", + "family": "kimi", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": false, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.265, "output": 9.119 }, - "limit": { "context": 262144, "output": 262144 } + "knowledge": "2025-01", + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0.574, + "output": 2.411 + } }, - "kimi-k2-0905-preview": { - "id": "kimi-k2-0905-preview", - "name": "kimi-k2-0905-preview", + "qwen-doc-turbo": { + "id": "qwen-doc-turbo", + "name": "Qwen Doc Turbo", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.632, "output": 2.53 }, - "limit": { "context": 262144, "output": 262144 } - }, - "ministral-14b-2512": { - "id": "ministral-14b-2512", - "name": "ministral-14b-2512", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-12-16", - "last_updated": "2025-12-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.33, "output": 0.33 }, - "limit": { "context": 128000, "output": 128000 } - }, - "gpt-5-mini": { - "id": "gpt-5-mini", - "name": "gpt-5-mini", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-08-08", - "last_updated": "2025-08-08", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-01", + "last_updated": "2024-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 2 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.087, + "output": 0.144 + } }, - "deepseek-chat": { - "id": "deepseek-chat", - "name": "Deepseek-Chat", - "family": "deepseek", + "qwen-vl-ocr": { + "id": "qwen-vl-ocr", + "name": "Qwen-VL OCR", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2024-07", - "release_date": "2024-11-29", - "last_updated": "2024-11-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-10-28", + "last_updated": "2025-04-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.29, "output": 0.43 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 34096, + "output": 4096 + }, + "cost": { + "input": 0.717, + "output": 0.717 + } }, - "claude-sonnet-4-5-20250929": { - "id": "claude-sonnet-4-5-20250929", - "name": "claude-sonnet-4-5-20250929", - "attachment": true, + "qwen-omni-turbo-realtime": { + "id": "qwen-omni-turbo-realtime", + "name": "Qwen-Omni Turbo Realtime", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-05-08", + "last_updated": "2025-05-08", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text", "audio"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 32768, + "output": 2048 + }, + "cost": { + "input": 0.23, + "output": 0.918, + "input_audio": 3.584, + "output_audio": 7.168 + } }, - "doubao-seed-1-6-thinking-250715": { - "id": "doubao-seed-1-6-thinking-250715", - "name": "doubao-seed-1-6-thinking-250715", - "attachment": true, + "qwen3-8b": { + "id": "qwen3-8b", + "name": "Qwen3 8B", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-07-15", - "last_updated": "2025-07-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.121, "output": 1.21 }, - "limit": { "context": 256000, "output": 16000 } + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.072, + "output": 0.287, + "reasoning": 0.717 + } }, - "qwen3-30b-a3b": { - "id": "qwen3-30b-a3b", - "name": "Qwen3-30B-A3B", + "qwen3.5-397b-a17b": { + "id": "qwen3.5-397b-a17b", + "name": "Qwen3.5 397B-A17B", "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, "knowledge": "2025-04", - "release_date": "2025-04-29", - "last_updated": "2025-04-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.11, "output": 1.08 }, - "limit": { "context": 128000, "output": 8192 } + "release_date": "2026-02-16", + "last_updated": "2026-02-16", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.43, + "output": 2.58, + "reasoning": 2.58 + } }, - "qwen-plus": { - "id": "qwen-plus", - "name": "Qwen-Plus", + "qwen-math-turbo": { + "id": "qwen-math-turbo", + "name": "Qwen Math Turbo", "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-09-19", + "last_updated": "2024-09-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.12, "output": 1.2 }, - "limit": { "context": 1000000, "output": 32768 } + "limit": { + "context": 4096, + "output": 3072 + }, + "cost": { + "input": 0.287, + "output": 0.861 + } }, - "gpt-4.1": { - "id": "gpt-4.1", - "name": "gpt-4.1", - "family": "gpt", - "attachment": true, - "reasoning": false, + "qwq-plus": { + "id": "qwq-plus", + "name": "QwQ Plus", + "family": "qwen", + "attachment": false, + "reasoning": true, "tool_call": true, "temperature": true, "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-03-05", + "last_updated": "2025-03-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8 }, - "limit": { "context": 1000000, "output": 32768 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.23, + "output": 0.574 + } }, - "qwen3-max-2025-09-23": { - "id": "qwen3-max-2025-09-23", - "name": "qwen3-max-2025-09-23", + "qwen-vl-plus": { + "id": "qwen-vl-plus", + "name": "Qwen-VL Plus", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-24", - "last_updated": "2025-09-24", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-01-25", + "last_updated": "2025-08-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.86, "output": 3.43 }, - "limit": { "context": 258048, "output": 65536 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.115, + "output": 0.287 + } }, - "gpt-5.1-chat-latest": { - "id": "gpt-5.1-chat-latest", - "name": "gpt-5.1-chat-latest", - "attachment": true, - "reasoning": false, + "glm-5": { + "id": "glm-5", + "name": "GLM-5", + "family": "glm", + "attachment": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 128000, "output": 16384 } - }, - "gemini-2.0-flash-lite": { - "id": "gemini-2.0-flash-lite", - "name": "gemini-2.0-flash-lite", - "family": "gemini-flash-lite", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-06-16", - "last_updated": "2025-06-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.075, "output": 0.3 }, - "limit": { "context": 2000000, "output": 8192 } + "limit": { + "context": 202752, + "output": 16384 + }, + "cost": { + "input": 0.86, + "output": 3.15 + } }, - "claude-sonnet-4-5-20250929-thinking": { - "id": "claude-sonnet-4-5-20250929-thinking", - "name": "claude-sonnet-4-5-20250929-thinking", - "attachment": true, + "deepseek-r1-distill-llama-70b": { + "id": "deepseek-r1-distill-llama-70b", + "name": "DeepSeek R1 Distill Llama 70B", + "family": "deepseek-thinking", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 32768, + "output": 16384 + }, + "cost": { + "input": 0.287, + "output": 0.861 + } }, - "grok-4-1-fast-non-reasoning": { - "id": "grok-4-1-fast-non-reasoning", - "name": "grok-4-1-fast-non-reasoning", - "attachment": true, - "reasoning": false, + "qwen3-32b": { + "id": "qwen3-32b", + "name": "Qwen3 32B", + "family": "qwen", + "attachment": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-11-20", - "last_updated": "2025-11-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.5 }, - "limit": { "context": 2000000, "output": 30000 } + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.287, + "output": 1.147, + "reasoning": 2.868 + } }, - "gemini-2.5-flash-nothink": { - "id": "gemini-2.5-flash-nothink", - "name": "gemini-2.5-flash-nothink", - "family": "gemini-flash", - "attachment": true, - "reasoning": false, + "MiniMax-M2.5": { + "id": "MiniMax-M2.5", + "name": "MiniMax-M2.5", + "family": "minimax", + "attachment": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-24", - "last_updated": "2025-06-24", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.5 }, - "limit": { "context": 1000000, "output": 65536 } + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "glm-4.5": { - "id": "glm-4.5", - "name": "GLM-4.5", + "qwen-max": { + "id": "qwen-max", + "name": "Qwen Max", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-07-29", - "last_updated": "2025-07-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.286, "output": 1.142 }, - "limit": { "context": 128000, "output": 98304 } - }, - "gpt-5-thinking": { - "id": "gpt-5-thinking", - "name": "gpt-5-thinking", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-08-08", - "last_updated": "2025-08-08", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-04-03", + "last_updated": "2025-01-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.345, + "output": 1.377 + } }, - "deepseek-reasoner": { - "id": "deepseek-reasoner", - "name": "Deepseek-Reasoner", - "family": "deepseek-thinking", + "qwen-plus": { + "id": "qwen-plus", + "name": "Qwen Plus", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-01-25", + "last_updated": "2025-09-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.29, "output": 0.43 }, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 1000000, + "output": 32768 + }, + "cost": { + "input": 0.115, + "output": 0.287, + "reasoning": 1.147 + } }, - "MiniMax-M2.1": { - "id": "MiniMax-M2.1", - "name": "MiniMax-M2.1", + "qwen-omni-turbo": { + "id": "qwen-omni-turbo", + "name": "Qwen-Omni Turbo", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2025-12-19", - "last_updated": "2025-12-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-01-19", + "last_updated": "2025-03-26", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text", "audio"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 1000000, "output": 131072 } + "limit": { + "context": 32768, + "output": 2048 + }, + "cost": { + "input": 0.058, + "output": 0.23, + "input_audio": 3.584, + "output_audio": 7.168 + } }, - "glm-4.6": { - "id": "glm-4.6", - "name": "glm-4.6", + "qwen-flash": { + "id": "qwen-flash", + "name": "Qwen Flash", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.286, "output": 1.142 }, - "limit": { "context": 200000, "output": 131072 } + "limit": { + "context": 1000000, + "output": 32768 + }, + "cost": { + "input": 0.022, + "output": 0.216 + } }, - "gemini-3-flash-preview": { - "id": "gemini-3-flash-preview", - "name": "gemini-3-flash-preview", - "attachment": true, + "qwen2-5-vl-7b-instruct": { + "id": "qwen2-5-vl-7b-instruct", + "name": "Qwen2.5-VL 7B Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-12-18", - "last_updated": "2025-12-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 3 }, - "limit": { "context": 1000000, "output": 65536 } + "knowledge": "2024-04", + "release_date": "2024-09", + "last_updated": "2024-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.287, + "output": 0.717 + } }, - "glm-4.6v": { - "id": "glm-4.6v", - "name": "GLM-4.6V", - "attachment": true, - "reasoning": false, + "deepseek-r1": { + "id": "deepseek-r1", + "name": "DeepSeek R1", + "family": "deepseek-thinking", + "attachment": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-12-08", - "last_updated": "2025-12-08", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.145, "output": 0.43 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.574, + "output": 2.294 + } }, - "claude-opus-4-5-20251101-thinking": { - "id": "claude-opus-4-5-20251101-thinking", - "name": "claude-opus-4-5-20251101-thinking", + "qwen3.5-flash": { + "id": "qwen3.5-flash", + "name": "Qwen3.5 Flash", + "family": "qwen", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-11-25", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2026-02-23", + "last_updated": "2026-02-23", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.172, + "output": 1.72, + "reasoning": 1.72 + } }, - "qwen3-coder-480b-a35b-instruct": { - "id": "qwen3-coder-480b-a35b-instruct", - "name": "qwen3-coder-480b-a35b-instruct", + "qwen3.6-plus": { + "id": "qwen3.6-plus", + "name": "Qwen3.6 Plus", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.86, "output": 3.43 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.05, + "cache_write": 0.625, + "tiers": [ + { + "input": 2, + "output": 6, + "cache_read": 0.2, + "cache_write": 2.5, + "tier": { + "type": "context", + "size": 256000 + } + } + ], + "context_over_200k": { + "input": 2, + "output": 6, + "cache_read": 0.2, + "cache_write": 2.5 + } + } }, - "doubao-seed-code-preview-251028": { - "id": "doubao-seed-code-preview-251028", - "name": "doubao-seed-code-preview-251028", - "attachment": true, + "qwen3-max": { + "id": "qwen3-max", + "name": "Qwen3 Max", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2025-11-11", - "last_updated": "2025-11-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-09-23", + "last_updated": "2025-09-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.17, "output": 1.14 }, - "limit": { "context": 256000, "output": 32000 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.861, + "output": 3.441 + } }, - "gemini-2.5-pro": { - "id": "gemini-2.5-pro", - "name": "gemini-2.5-pro", - "family": "gemini-pro", - "attachment": true, - "reasoning": false, + "glm-5.1": { + "id": "glm-5.1", + "name": "GLM-5.1", + "family": "glm", + "attachment": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 1000000, "output": 65536 } + "release_date": "2026-04-14", + "last_updated": "2026-04-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 128000 + }, + "cost": { + "input": 0.87, + "output": 3.48, + "cache_read": 0.17 + } }, - "claude-opus-4-1-20250805": { - "id": "claude-opus-4-1-20250805", - "name": "claude-opus-4-1-20250805", - "attachment": true, - "reasoning": false, + "qwen3-omni-flash": { + "id": "qwen3-omni-flash", + "name": "Qwen3-Omni Flash", + "family": "qwen", + "attachment": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-09-15", + "last_updated": "2025-09-15", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text", "audio"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 65536, + "output": 16384 + }, + "cost": { + "input": 0.058, + "output": 0.23, + "input_audio": 3.584, + "output_audio": 7.168 + } }, - "gemini-2.5-flash": { - "id": "gemini-2.5-flash", - "name": "gemini-2.5-flash", - "family": "gemini-flash", - "attachment": true, + "deepseek-v3-1": { + "id": "deepseek-v3-1", + "name": "DeepSeek V3.1", + "family": "deepseek", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 2.5 }, - "limit": { "context": 1000000, "output": 65536 } + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.574, + "output": 1.721 + } }, - "gpt-5.2": { - "id": "gpt-5.2", - "name": "gpt-5.2", - "attachment": true, + "qwen2-5-72b-instruct": { + "id": "qwen2-5-72b-instruct", + "name": "Qwen2.5 72B Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-12-12", - "last_updated": "2025-12-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14 }, - "limit": { "context": 400000, "output": 128000 } + "knowledge": "2024-04", + "release_date": "2024-09", + "last_updated": "2024-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.574, + "output": 1.721 + } }, - "grok-4-1-fast-reasoning": { - "id": "grok-4-1-fast-reasoning", - "name": "grok-4-1-fast-reasoning", - "attachment": true, + "qwen3-vl-235b-a22b": { + "id": "qwen3-vl-235b-a22b", + "name": "Qwen3-VL 235B-A22B", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-11-20", - "last_updated": "2025-11-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.5 }, - "limit": { "context": 2000000, "output": 30000 } + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.286705, + "output": 1.14682, + "reasoning": 2.867051 + } }, - "gpt-5.1": { - "id": "gpt-5.1", - "name": "gpt-5.1", - "attachment": true, + "qwen-math-plus": { + "id": "qwen-math-plus", + "name": "Qwen Math Plus", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-08-16", + "last_updated": "2024-09-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 4096, + "output": 3072 + }, + "cost": { + "input": 0.574, + "output": 1.721 + } }, - "gemini-2.5-flash-preview-09-2025": { - "id": "gemini-2.5-flash-preview-09-2025", - "name": "gemini-2.5-flash-preview-09-2025", - "attachment": true, + "qwen2-5-coder-32b-instruct": { + "id": "qwen2-5-coder-32b-instruct", + "name": "Qwen2.5-Coder 32B Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-26", - "last_updated": "2025-09-26", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.5 }, - "limit": { "context": 1000000, "output": 65536 } + "knowledge": "2024-04", + "release_date": "2024-11", + "last_updated": "2024-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.287, + "output": 0.861 + } }, - "doubao-seed-1-8-251215": { - "id": "doubao-seed-1-8-251215", - "name": "doubao-seed-1-8-251215", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2025-12-18", - "last_updated": "2025-12-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.114, "output": 0.286 }, - "limit": { "context": 224000, "output": 64000 } - }, - "gpt-4.1-mini": { - "id": "gpt-4.1-mini", - "name": "gpt-4.1-mini", - "family": "gpt-mini", - "attachment": true, + "qwen3-asr-flash": { + "id": "qwen3-asr-flash", + "name": "Qwen3-ASR Flash", + "family": "qwen", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, + "tool_call": false, + "temperature": false, "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-09-08", + "last_updated": "2025-09-08", + "modalities": { + "input": ["audio"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 1.6 }, - "limit": { "context": 1000000, "output": 32768 } + "limit": { + "context": 53248, + "output": 4096 + }, + "cost": { + "input": 0.032, + "output": 0.032 + } }, - "qwen3-235b-a22b": { - "id": "qwen3-235b-a22b", - "name": "Qwen3-235B-A22B", + "qwen-deep-research": { + "id": "qwen-deep-research", + "name": "Qwen Deep Research", "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04-29", - "last_updated": "2025-04-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-01", + "last_updated": "2024-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.29, "output": 2.86 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 1000000, + "output": 32768 + }, + "cost": { + "input": 7.742, + "output": 23.367 + } }, - "grok-4-fast-reasoning": { - "id": "grok-4-fast-reasoning", - "name": "grok-4-fast-reasoning", - "attachment": true, + "qwen3-next-80b-a3b-thinking": { + "id": "qwen3-next-80b-a3b-thinking", + "name": "Qwen3-Next 80B-A3B (Thinking)", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-09-23", - "last_updated": "2025-09-23", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.5 }, - "limit": { "context": 2000000, "output": 30000 } + "knowledge": "2025-04", + "release_date": "2025-09", + "last_updated": "2025-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.144, + "output": 1.434 + } }, - "MiniMax-M2": { - "id": "MiniMax-M2", - "name": "MiniMax-M2", + "qwen-mt-plus": { + "id": "qwen-mt-plus", + "name": "Qwen-MT Plus", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2025-10-26", - "last_updated": "2025-10-26", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.33, "output": 1.32 }, - "limit": { "context": 1000000, "output": 128000 } - }, - "gemini-2.5-flash-image": { - "id": "gemini-2.5-flash-image", - "name": "gemini-2.5-flash-image", - "attachment": true, - "reasoning": false, "tool_call": false, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-10-08", - "last_updated": "2025-10-08", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-01", + "last_updated": "2025-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 30 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 16384, + "output": 8192 + }, + "cost": { + "input": 0.259, + "output": 0.775 + } }, - "glm-4.5v": { - "id": "glm-4.5v", - "name": "GLM-4.5V", - "attachment": true, - "reasoning": false, + "deepseek-r1-distill-qwen-32b": { + "id": "deepseek-r1-distill-qwen-32b", + "name": "DeepSeek R1 Distill Qwen 32B", + "family": "qwen", + "attachment": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-07-29", - "last_updated": "2025-07-29", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.29, "output": 0.86 }, - "limit": { "context": 64000, "output": 16384 } + "limit": { + "context": 32768, + "output": 16384 + }, + "cost": { + "input": 0.287, + "output": 0.861 + } }, - "claude-haiku-4-5-20251001": { - "id": "claude-haiku-4-5-20251001", - "name": "claude-haiku-4-5-20251001", - "attachment": true, + "qwen-vl-max": { + "id": "qwen-vl-max", + "name": "Qwen-VL Max", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-10-16", - "last_updated": "2025-10-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-04-08", + "last_updated": "2025-08-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 5 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.23, + "output": 0.574 + } }, - "glm-4.7": { - "id": "glm-4.7", - "name": "glm-4.7", + "qwen3-coder-flash": { + "id": "qwen3-coder-flash", + "name": "Qwen3 Coder Flash", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.286, "output": 1.142 }, - "limit": { "context": 200000, "output": 131072 } + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.144, + "output": 0.574 + } }, - "qwen-flash": { - "id": "qwen-flash", - "name": "Qwen-Flash", + "deepseek-r1-distill-qwen-7b": { + "id": "deepseek-r1-distill-qwen-7b", + "name": "DeepSeek R1 Distill Qwen 7B", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.022, "output": 0.22 }, - "limit": { "context": 1000000, "output": 32768 } + "limit": { + "context": 32768, + "output": 16384 + }, + "cost": { + "input": 0.072, + "output": 0.144 + } }, - "gpt-4.1-nano": { - "id": "gpt-4.1-nano", - "name": "gpt-4.1-nano", - "family": "gpt-nano", - "attachment": true, + "qwen2-5-7b-instruct": { + "id": "qwen2-5-7b-instruct", + "name": "Qwen2.5 7B Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 1000000, "output": 32768 } + "release_date": "2024-09", + "last_updated": "2024-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.072, + "output": 0.144 + } }, - "deepseek-v3.2": { - "id": "deepseek-v3.2", - "name": "deepseek-v3.2", + "qwen2-5-14b-instruct": { + "id": "qwen2-5-14b-instruct", + "name": "Qwen2.5 14B Instruct", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.29, "output": 0.43 }, - "limit": { "context": 128000, "output": 8192 } + "knowledge": "2024-04", + "release_date": "2024-09", + "last_updated": "2024-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.144, + "output": 0.431 + } }, - "doubao-seed-1-6-vision-250815": { - "id": "doubao-seed-1-6-vision-250815", - "name": "doubao-seed-1-6-vision-250815", - "attachment": true, + "tongyi-intent-detect-v3": { + "id": "tongyi-intent-detect-v3", + "name": "Tongyi Intent Detect V3", + "family": "yi", + "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "temperature": true, - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-01", + "last_updated": "2024-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.114, "output": 1.143 }, - "limit": { "context": 256000, "output": 32000 } + "limit": { + "context": 8192, + "output": 1024 + }, + "cost": { + "input": 0.058, + "output": 0.144 + } }, - "claude-opus-4-1-20250805-thinking": { - "id": "claude-opus-4-1-20250805-thinking", - "name": "claude-opus-4-1-20250805-thinking", - "attachment": true, + "qwq-32b": { + "id": "qwq-32b", + "name": "QwQ 32B", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-05-27", - "last_updated": "2025-05-27", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 75 }, - "limit": { "context": 200000, "output": 32000 } + "knowledge": "2024-04", + "release_date": "2024-12", + "last_updated": "2024-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.287, + "output": 0.861 + } }, - "gemini-3-pro-image-preview": { - "id": "gemini-3-pro-image-preview", - "name": "gemini-3-pro-image-preview", - "attachment": true, + "moonshot-kimi-k2-instruct": { + "id": "moonshot-kimi-k2-instruct", + "name": "Moonshot Kimi K2 Instruct", + "family": "kimi", + "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-11-20", - "last_updated": "2025-11-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 120 }, - "limit": { "context": 32768, "output": 64000 } + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.574, + "output": 2.294 + } }, - "mistral-large-2512": { - "id": "mistral-large-2512", - "name": "mistral-large-2512", - "attachment": true, + "qwen2-5-32b-instruct": { + "id": "qwen2-5-32b-instruct", + "name": "Qwen2.5 32B Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-12-16", - "last_updated": "2025-12-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.1, "output": 3.3 }, - "limit": { "context": 128000, "output": 262144 } - } - } - }, - "novita-ai": { - "id": "novita-ai", - "env": ["NOVITA_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.novita.ai/openai", - "name": "NovitaAI", - "doc": "https://novita.ai/docs/guides/introduction", - "models": { - "paddlepaddle/paddleocr-vl": { - "id": "paddlepaddle/paddleocr-vl", - "name": "PaddleOCR-VL", - "attachment": true, + "knowledge": "2024-04", + "release_date": "2024-09", + "last_updated": "2024-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.287, + "output": 0.861 + } + }, + "qwen3-next-80b-a3b-instruct": { + "id": "qwen3-next-80b-a3b-instruct", + "name": "Qwen3-Next 80B-A3B Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2025-10-22", - "last_updated": "2025-10-22", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-09", + "last_updated": "2025-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.02, "output": 0.02 }, - "limit": { "context": 16384, "output": 16384 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.144, + "output": 0.574 + } }, - "openai/gpt-oss-120b": { - "id": "openai/gpt-oss-120b", - "name": "OpenAI GPT OSS 120B", - "attachment": true, - "reasoning": true, + "qwen3-omni-flash-realtime": { + "id": "qwen3-omni-flash-realtime", + "name": "Qwen3-Omni Flash Realtime", + "family": "qwen", + "attachment": false, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-08-06", - "last_updated": "2025-08-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.05, "output": 0.25 }, - "limit": { "context": 131072, "output": 32768 } + "knowledge": "2024-04", + "release_date": "2025-09-15", + "last_updated": "2025-09-15", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text", "audio"] + }, + "open_weights": false, + "limit": { + "context": 65536, + "output": 16384 + }, + "cost": { + "input": 0.23, + "output": 0.918, + "input_audio": 3.584, + "output_audio": 7.168 + } }, - "openai/gpt-oss-20b": { - "id": "openai/gpt-oss-20b", - "name": "OpenAI: GPT OSS 20B", + "kimi-k2.6": { + "id": "kimi-k2.6", + "name": "Moonshot Kimi K2.6", + "family": "kimi", "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": false, "temperature": true, - "release_date": "2025-08-06", - "last_updated": "2025-08-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.04, "output": 0.15 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 262144, + "output": 16384 + }, + "cost": { + "input": 0.929, + "output": 3.858 + } }, - "microsoft/wizardlm-2-8x22b": { - "id": "microsoft/wizardlm-2-8x22b", - "name": "Wizardlm 2 8x22B", + "qwen3-vl-30b-a3b": { + "id": "qwen3-vl-30b-a3b", + "name": "Qwen3-VL 30B-A3B", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2024-04-24", - "last_updated": "2024-04-24", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.62, "output": 0.62 }, - "limit": { "context": 65535, "output": 8000 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.108, + "output": 0.431, + "reasoning": 1.076 + } }, - "kwaipilot/kat-coder": { - "id": "kwaipilot/kat-coder", - "name": "KAT-Coder-Pro V1(Free)", + "qwen3-vl-plus": { + "id": "qwen3-vl-plus", + "name": "Qwen3-VL Plus", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 32000 } + "knowledge": "2025-04", + "release_date": "2025-09-23", + "last_updated": "2025-09-23", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0.143353, + "output": 1.433525, + "reasoning": 4.300576 + } }, - "kwaipilot/kat-coder-pro": { - "id": "kwaipilot/kat-coder-pro", - "name": "Kat Coder Pro", + "deepseek-v3-2-exp": { + "id": "deepseek-v3-2-exp", + "name": "DeepSeek V3.2 Exp", + "family": "deepseek", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-01-05", - "last_updated": "2026-01-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.06 }, - "limit": { "context": 256000, "output": 128000 } + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.287, + "output": 0.431 + } }, - "sao10k/l3-8b-lunaris": { - "id": "sao10k/l3-8b-lunaris", - "name": "Sao10k L3 8B Lunaris\t", + "qwen3-coder-480b-a35b-instruct": { + "id": "qwen3-coder-480b-a35b-instruct", + "name": "Qwen3-Coder 480B-A35B Instruct", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": true, + "tool_call": true, "temperature": true, - "release_date": "2024-11-28", - "last_updated": "2024-11-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.05, "output": 0.05 }, - "limit": { "context": 8192, "output": 8192 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.861, + "output": 3.441 + } }, - "sao10k/l3-70b-euryale-v2.1": { - "id": "sao10k/l3-70b-euryale-v2.1", - "name": "L3 70B Euryale V2.1\t", + "deepseek-r1-distill-qwen-1-5b": { + "id": "deepseek-r1-distill-qwen-1-5b", + "name": "DeepSeek R1 Distill Qwen 1.5B", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-06-18", - "last_updated": "2024-06-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1.48, "output": 1.48 }, - "limit": { "context": 8192, "output": 8192 } + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "sao10k/l31-70b-euryale-v2.2": { - "id": "sao10k/l31-70b-euryale-v2.2", - "name": "L31 70B Euryale V2.2", + "qwen3-coder-30b-a3b-instruct": { + "id": "qwen3-coder-30b-a3b-instruct", + "name": "Qwen3-Coder 30B-A3B Instruct", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2024-09-19", - "last_updated": "2024-09-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1.48, "output": 1.48 }, - "limit": { "context": 8192, "output": 8192 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.216, + "output": 0.861 + } }, - "sao10k/L3-8B-Stheno-v3.2": { - "id": "sao10k/L3-8B-Stheno-v3.2", - "name": "L3 8B Stheno V3.2", + "qwen2-5-coder-7b-instruct": { + "id": "qwen2-5-coder-7b-instruct", + "name": "Qwen2.5-Coder 7B Instruct", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2024-11-29", - "last_updated": "2024-11-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-11", + "last_updated": "2024-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.05, "output": 0.05 }, - "limit": { "context": 8192, "output": 32000 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.144, + "output": 0.287 + } }, - "deepseek/deepseek-r1-turbo": { - "id": "deepseek/deepseek-r1-turbo", - "name": "DeepSeek R1 (Turbo)\t", + "qwen-turbo": { + "id": "qwen-turbo", + "name": "Qwen Turbo", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-03-05", - "last_updated": "2025-03-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.7, "output": 2.5 }, - "limit": { "context": 64000, "output": 16000 } + "knowledge": "2024-04", + "release_date": "2024-11-01", + "last_updated": "2025-07-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 16384 + }, + "cost": { + "input": 0.044, + "output": 0.087, + "reasoning": 0.431 + } }, - "deepseek/deepseek-v3.1-terminus": { - "id": "deepseek/deepseek-v3.1-terminus", - "name": "Deepseek V3.1 Terminus", - "family": "deepseek", + "qwen-mt-turbo": { + "id": "qwen-mt-turbo", + "name": "Qwen-MT Turbo", + "family": "qwen", "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "release_date": "2025-09-22", - "last_updated": "2025-09-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.27, "output": 1, "cache_read": 0.135 }, - "limit": { "context": 131072, "output": 32768 } + "knowledge": "2024-04", + "release_date": "2025-01", + "last_updated": "2025-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16384, + "output": 8192 + }, + "cost": { + "input": 0.101, + "output": 0.28 + } }, - "deepseek/deepseek-r1-distill-llama-70b": { - "id": "deepseek/deepseek-r1-distill-llama-70b", - "name": "DeepSeek R1 Distill LLama 70B", - "family": "deepseek-thinking", + "qwen2-5-math-72b-instruct": { + "id": "qwen2-5-math-72b-instruct", + "name": "Qwen2.5-Math 72B Instruct", + "family": "qwen", "attachment": false, - "reasoning": true, - "tool_call": false, - "structured_output": true, + "reasoning": false, + "tool_call": true, "temperature": true, - "release_date": "2025-01-27", - "last_updated": "2025-01-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-09", + "last_updated": "2024-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.8, "output": 0.8 }, - "limit": { "context": 8192, "output": 8192 } + "limit": { + "context": 4096, + "output": 3072 + }, + "cost": { + "input": 0.574, + "output": 1.721 + } }, - "deepseek/deepseek-v3-0324": { - "id": "deepseek/deepseek-v3-0324", - "name": "DeepSeek V3 0324", - "family": "deepseek", + "qwen3.6-max-preview": { + "id": "qwen3.6-max-preview", + "name": "Qwen3.6 Max Preview", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-03-25", - "last_updated": "2025-03-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.27, "output": 1.12, "cache_read": 0.135 }, - "limit": { "context": 163840, "output": 163840 } + "release_date": "2026-04-20", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 245800, + "output": 65536 + }, + "cost": { + "input": 1.32, + "output": 7.9, + "cache_read": 0.132 + } }, - "deepseek/deepseek-prover-v2-671b": { - "id": "deepseek/deepseek-prover-v2-671b", - "name": "Deepseek Prover V2 671B", + "qwen2-5-omni-7b": { + "id": "qwen2-5-omni-7b", + "name": "Qwen2.5-Omni 7B", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2025-04-30", - "last_updated": "2025-04-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-12", + "last_updated": "2024-12", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text", "audio"] + }, "open_weights": true, - "cost": { "input": 0.7, "output": 2.5 }, - "limit": { "context": 160000, "output": 160000 } + "limit": { + "context": 32768, + "output": 2048 + }, + "cost": { + "input": 0.087, + "output": 0.345, + "input_audio": 5.448 + } }, - "deepseek/deepseek-r1-0528-qwen3-8b": { - "id": "deepseek/deepseek-r1-0528-qwen3-8b", - "name": "DeepSeek R1 0528 Qwen3 8B", + "qwen3.5-plus": { + "id": "qwen3.5-plus", + "name": "Qwen3.5 Plus", + "family": "qwen", "attachment": false, "reasoning": true, - "tool_call": false, - "temperature": true, - "release_date": "2025-05-29", - "last_updated": "2025-05-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.06, "output": 0.09 }, - "limit": { "context": 128000, "output": 32000 } - }, - "deepseek/deepseek-ocr-2": { - "id": "deepseek/deepseek-ocr-2", - "name": "deepseek/deepseek-ocr-2", - "attachment": true, - "reasoning": false, - "tool_call": false, - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.03, "output": 0.03 }, - "limit": { "context": 8192, "output": 8192 } - }, - "deepseek/deepseek-v3-turbo": { - "id": "deepseek/deepseek-v3-turbo", - "name": "DeepSeek V3 (Turbo)\t", - "attachment": false, - "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2025-03-05", - "last_updated": "2025-03-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.4, "output": 1.3 }, - "limit": { "context": 64000, "output": 16000 } + "knowledge": "2025-04", + "release_date": "2026-02-16", + "last_updated": "2026-02-16", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.573, + "output": 3.44, + "reasoning": 3.44 + } }, - "deepseek/deepseek-v3.2-exp": { - "id": "deepseek/deepseek-v3.2-exp", - "name": "Deepseek V3.2 Exp", + "deepseek-r1-distill-qwen-14b": { + "id": "deepseek-r1-distill-qwen-14b", + "name": "DeepSeek R1 Distill Qwen 14B", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.27, "output": 0.41 }, - "limit": { "context": 163840, "output": 65536 } + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "output": 16384 + }, + "cost": { + "input": 0.144, + "output": 0.431 + } }, - "deepseek/deepseek-ocr": { - "id": "deepseek/deepseek-ocr", - "name": "DeepSeek-OCR", - "attachment": true, + "qwen2-5-vl-72b-instruct": { + "id": "qwen2-5-vl-72b-instruct", + "name": "Qwen2.5-VL 72B Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": true, + "tool_call": true, "temperature": true, - "release_date": "2025-10-24", - "last_updated": "2025-10-24", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-09", + "last_updated": "2024-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.03, "output": 0.03 }, - "limit": { "context": 8192, "output": 8192 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 2.294, + "output": 6.881 + } }, - "deepseek/deepseek-v3.1": { - "id": "deepseek/deepseek-v3.1", - "name": "DeepSeek V3.1", + "deepseek-v3": { + "id": "deepseek-v3", + "name": "DeepSeek V3", "family": "deepseek", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-08-21", - "last_updated": "2025-08-21", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.27, "output": 1, "cache_read": 0.135 }, - "limit": { "context": 131072, "output": 32768 } + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 65536, + "output": 8192 + }, + "cost": { + "input": 0.287, + "output": 1.147 + } }, - "deepseek/deepseek-r1-0528": { - "id": "deepseek/deepseek-r1-0528", + "deepseek-r1-0528": { + "id": "deepseek-r1-0528", "name": "DeepSeek R1 0528", "family": "deepseek-thinking", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2024-07", "release_date": "2025-05-28", "last_updated": "2025-05-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.7, "output": 2.5, "cache_read": 0.35 }, - "limit": { "context": 163840, "output": 32768 } + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.574, + "output": 2.294 + } }, - "deepseek/deepseek-v3.2": { - "id": "deepseek/deepseek-v3.2", - "name": "Deepseek V3.2", - "family": "deepseek", + "qvq-max": { + "id": "qvq-max", + "name": "QVQ Max", + "family": "qvq", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.269, "output": 0.4, "cache_read": 0.1345 }, - "limit": { "context": 163840, "output": 65536 } + "knowledge": "2024-04", + "release_date": "2025-03-25", + "last_updated": "2025-03-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 1.147, + "output": 4.588 + } }, - "zai-org/glm-4.7-flash": { - "id": "zai-org/glm-4.7-flash", - "name": "GLM-4.7-Flash", - "family": "glm", + "kimi-k2-thinking": { + "id": "kimi-k2-thinking", + "name": "Moonshot Kimi K2 Thinking", + "family": "kimi", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-01-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.07, "output": 0.4, "cache_read": 0.01 }, - "limit": { "context": 200000, "output": 128000 } - }, - "zai-org/autoglm-phone-9b-multilingual": { - "id": "zai-org/autoglm-phone-9b-multilingual", - "name": "AutoGLM-Phone-9B-Multilingual", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2025-12-10", - "last_updated": "2025-12-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-11-06", + "last_updated": "2025-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.035, "output": 0.138 }, - "limit": { "context": 65536, "output": 65536 } + "limit": { + "context": 262144, + "output": 16384 + }, + "cost": { + "input": 0.574, + "output": 2.294 + } }, - "zai-org/glm-4.5": { - "id": "zai-org/glm-4.5", - "name": "GLM-4.5", - "family": "glm", + "qwen3-14b": { + "id": "qwen3-14b", + "name": "Qwen3 14B", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.2, "cache_read": 0.11 }, - "limit": { "context": 131072, "output": 98304 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.144, + "output": 0.574, + "reasoning": 1.434 + } }, - "zai-org/glm-4.6": { - "id": "zai-org/glm-4.6", - "name": "GLM 4.6", - "family": "glm", + "deepseek-r1-distill-llama-8b": { + "id": "deepseek-r1-distill-llama-8b", + "name": "DeepSeek R1 Distill Llama 8B", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.55, "output": 2.2, "cache_read": 0.11 }, - "limit": { "context": 204800, "output": 131072 } + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "zai-org/glm-4.6v": { - "id": "zai-org/glm-4.6v", - "name": "GLM 4.6V", - "family": "glmv", - "attachment": true, - "reasoning": true, + "qwen-long": { + "id": "qwen-long", + "name": "Qwen Long", + "family": "qwen", + "attachment": false, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-08", - "last_updated": "2025-12-08", - "modalities": { "input": ["text", "video", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 0.9, "cache_read": 0.055 }, - "limit": { "context": 131072, "output": 32768 } + "knowledge": "2024-04", + "release_date": "2025-01-25", + "last_updated": "2025-01-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 10000000, + "output": 8192 + }, + "cost": { + "input": 0.072, + "output": 0.287 + } }, - "zai-org/glm-5": { - "id": "zai-org/glm-5", - "name": "GLM-5", - "family": "glm", + "kimi/kimi-k2.5": { + "id": "kimi/kimi-k2.5", + "name": "kimi/kimi-k2.5", + "family": "kimi", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, - "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1, "output": 3.2, "cache_read": 0.2 }, - "limit": { "context": 202800, "output": 131072 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 3, + "cache_read": 0.1 + } }, - "zai-org/glm-4.5-air": { - "id": "zai-org/glm-4.5-air", - "name": "GLM 4.5 Air", - "family": "glm-air", + "MiniMax/MiniMax-M2.7": { + "id": "MiniMax/MiniMax-M2.7", + "name": "MiniMax-M2.7", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-10-13", - "last_updated": "2025-10-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.13, "output": 0.85 }, - "limit": { "context": 131072, "output": 98304 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06, + "cache_write": 0.375 + } }, - "zai-org/glm-4.5v": { - "id": "zai-org/glm-4.5v", - "name": "GLM 4.5V", - "family": "glmv", - "attachment": true, - "reasoning": true, + "siliconflow/deepseek-v3-0324": { + "id": "siliconflow/deepseek-v3-0324", + "name": "siliconflow/deepseek-v3-0324", + "family": "deepseek", + "attachment": false, + "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-08-11", - "last_updated": "2025-08-11", - "modalities": { "input": ["text", "video", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 1.8, "cache_read": 0.11 }, - "limit": { "context": 65536, "output": 16384 } + "release_date": "2024-12-26", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 163840, + "output": 163840 + }, + "cost": { + "input": 0.25, + "output": 1 + } }, - "zai-org/glm-4.7": { - "id": "zai-org/glm-4.7", - "name": "GLM-4.7", - "family": "glm", + "siliconflow/deepseek-v3.2": { + "id": "siliconflow/deepseek-v3.2", + "name": "siliconflow/deepseek-v3.2", + "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.2, "cache_read": 0.11 }, - "limit": { "context": 204800, "output": 131072 } + "release_date": "2025-12-03", + "last_updated": "2025-12-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 163840, + "output": 65536 + }, + "cost": { + "input": 0.27, + "output": 0.42 + } }, - "gryphe/mythomax-l2-13b": { - "id": "gryphe/mythomax-l2-13b", - "name": "Mythomax L2 13B", + "siliconflow/deepseek-r1-0528": { + "id": "siliconflow/deepseek-r1-0528", + "name": "siliconflow/deepseek-r1-0528", + "family": "deepseek-thinking", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-04-25", - "last_updated": "2024-04-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.09, "output": 0.09 }, - "limit": { "context": 4096, "output": 3200 } + "release_date": "2025-05-28", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 163840, + "output": 32768 + }, + "cost": { + "input": 0.5, + "output": 2.18 + } }, - "google/gemma-3-27b-it": { - "id": "google/gemma-3-27b-it", - "name": "Gemma 3 27B", - "family": "gemma", - "attachment": true, - "reasoning": false, - "tool_call": false, + "siliconflow/deepseek-v3.1-terminus": { + "id": "siliconflow/deepseek-v3.1-terminus", + "name": "siliconflow/deepseek-v3.1-terminus", + "family": "deepseek", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-03-25", - "last_updated": "2025-03-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.119, "output": 0.2 }, - "limit": { "context": 98304, "output": 16384 } + "release_date": "2025-09-29", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 163840, + "output": 65536 + }, + "cost": { + "input": 0.27, + "output": 1 + } }, - "skywork/r1v4-lite": { - "id": "skywork/r1v4-lite", - "name": "Skywork R1V4-Lite", - "family": "skywork", - "attachment": true, + "qwen3-coder-plus": { + "id": "qwen3-coder-plus", + "name": "Qwen3 Coder Plus", + "family": "qwen", + "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": true, + "tool_call": true, "temperature": true, - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 0.6 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1, + "output": 5 + } }, - "baidu/ernie-4.5-21B-a3b-thinking": { - "id": "baidu/ernie-4.5-21B-a3b-thinking", - "name": "ERNIE-4.5-21B-A3B-Thinking", - "family": "ernie", + "deepseek-v4-flash": { + "id": "deepseek-v4-flash", + "name": "DeepSeek V4 Flash", + "family": "deepseek-flash", "attachment": false, "reasoning": true, - "tool_call": false, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-09-19", - "last_updated": "2025-09-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.07, "output": 0.28 }, - "limit": { "context": 131072, "output": 65536 } + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 0.14, + "output": 0.28, + "cache_read": 0.028 + } }, - "baidu/ernie-4.5-vl-28b-a3b-thinking": { - "id": "baidu/ernie-4.5-vl-28b-a3b-thinking", - "name": "ERNIE-4.5-VL-28B-A3B-Thinking", - "attachment": true, + "deepseek-v4-pro": { + "id": "deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "release_date": "2025-11-26", - "last_updated": "2025-11-26", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.39, "output": 0.39 }, - "limit": { "context": 131072, "output": 65536 } - }, - "baidu/ernie-4.5-vl-28b-a3b": { - "id": "baidu/ernie-4.5-vl-28b-a3b", - "name": "ERNIE 4.5 VL 28B A3B", - "attachment": true, + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 1.74, + "output": 3.48, + "cache_read": 0.145 + } + } + } + }, + "firepass": { + "id": "firepass", + "env": ["FIREPASS_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.fireworks.ai/inference/v1/", + "name": "Fireworks (Firepass)", + "doc": "https://docs.fireworks.ai/firepass", + "models": { + "accounts/fireworks/routers/kimi-k2p6-turbo": { + "id": "accounts/fireworks/routers/kimi-k2p6-turbo", + "name": "Kimi K2.6 Turbo", + "family": "kimi-thinking", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-06-30", - "last_updated": "2025-06-30", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-04-17", + "last_updated": "2026-04-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1.4, "output": 5.6 }, - "limit": { "context": 30000, "output": 8000 } - }, - "baidu/ernie-4.5-21B-a3b": { - "id": "baidu/ernie-4.5-21B-a3b", - "name": "ERNIE 4.5 21B A3B", - "family": "ernie", + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + } + } + }, + "minimax-cn-coding-plan": { + "id": "minimax-cn-coding-plan", + "env": ["MINIMAX_API_KEY"], + "npm": "@ai-sdk/anthropic", + "api": "https://api.minimaxi.com/anthropic/v1", + "name": "MiniMax Coding Plan (minimaxi.com)", + "doc": "https://platform.minimaxi.com/docs/coding-plan/intro", + "models": { + "MiniMax-M2": { + "id": "MiniMax-M2", + "name": "MiniMax-M2", + "family": "minimax", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-06-30", - "last_updated": "2025-06-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-10-27", + "last_updated": "2025-10-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.07, "output": 0.28 }, - "limit": { "context": 120000, "output": 8000 } + "limit": { + "context": 196608, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "baidu/ernie-4.5-300b-a47b-paddle": { - "id": "baidu/ernie-4.5-300b-a47b-paddle", - "name": "ERNIE 4.5 300B A47B", + "MiniMax-M2.5": { + "id": "MiniMax-M2.5", + "name": "MiniMax-M2.5", + "family": "minimax", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": true, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-06-30", - "last_updated": "2025-06-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.28, "output": 1.1 }, - "limit": { "context": 123000, "output": 12000 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "baidu/ernie-4.5-vl-424b-a47b": { - "id": "baidu/ernie-4.5-vl-424b-a47b", - "name": "ERNIE 4.5 VL 424B A47B", - "attachment": true, + "MiniMax-M2.7": { + "id": "MiniMax-M2.7", + "name": "MiniMax-M2.7", + "family": "minimax", + "attachment": false, "reasoning": true, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2025-06-30", - "last_updated": "2025-06-30", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.42, "output": 1.25 }, - "limit": { "context": 123000, "output": 16000 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "minimaxai/minimax-m1-80k": { - "id": "minimaxai/minimax-m1-80k", - "name": "MiniMax M1", + "MiniMax-M2.7-highspeed": { + "id": "MiniMax-M2.7-highspeed", + "name": "MiniMax-M2.7-highspeed", "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.55, "output": 2.2 }, - "limit": { "context": 1000000, "output": 40000 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "meta-llama/llama-3.3-70b-instruct": { - "id": "meta-llama/llama-3.3-70b-instruct", - "name": "Llama 3.3 70B Instruct", - "family": "llama", + "MiniMax-M2.1": { + "id": "MiniMax-M2.1", + "name": "MiniMax-M2.1", + "family": "minimax", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-12-07", - "last_updated": "2024-12-07", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.135, "output": 0.4 }, - "limit": { "context": 131072, "output": 120000 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "meta-llama/llama-3.1-8b-instruct": { - "id": "meta-llama/llama-3.1-8b-instruct", - "name": "Llama 3.1 8B Instruct", - "family": "llama", + "MiniMax-M2.5-highspeed": { + "id": "MiniMax-M2.5-highspeed", + "name": "MiniMax-M2.5-highspeed", + "family": "minimax", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2024-07-24", - "last_updated": "2024-07-24", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-13", + "last_updated": "2026-02-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.02, "output": 0.05 }, - "limit": { "context": 16384, "output": 16384 } - }, - "meta-llama/llama-4-scout-17b-16e-instruct": { - "id": "meta-llama/llama-4-scout-17b-16e-instruct", - "name": "Llama 4 Scout Instruct", + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } + } + } + }, + "jiekou": { + "id": "jiekou", + "env": ["JIEKOU_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.jiekou.ai/openai", + "name": "Jiekou.AI", + "doc": "https://docs.jiekou.ai/docs/support/quickstart?utm_source=github_models.dev", + "models": { + "gpt-5.1-codex-max": { + "id": "gpt-5.1-codex-max", + "name": "gpt-5.1-codex-max", + "family": "gpt-codex", "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2025-04-06", - "last_updated": "2025-04-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.18, "output": 0.59 }, - "limit": { "context": 131072, "output": 131072 } - }, - "meta-llama/llama-3-8b-instruct": { - "id": "meta-llama/llama-3-8b-instruct", - "name": "Llama 3 8B Instruct", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-04-25", - "last_updated": "2024-04-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.04, "output": 0.04 }, - "limit": { "context": 8192, "output": 8192 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.125, + "output": 9 + } }, - "meta-llama/llama-3-70b-instruct": { - "id": "meta-llama/llama-3-70b-instruct", - "name": "Llama3 70B Instruct", - "family": "llama", - "attachment": false, + "grok-4-1-fast-reasoning": { + "id": "grok-4-1-fast-reasoning", + "name": "grok-4-1-fast-reasoning", + "family": "grok", + "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2024-04-25", - "last_updated": "2024-04-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.51, "output": 0.74 }, - "limit": { "context": 8192, "output": 8000 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 2000000 + }, + "cost": { + "input": 0.18, + "output": 0.45 + } }, - "meta-llama/llama-4-maverick-17b-128e-instruct-fp8": { - "id": "meta-llama/llama-4-maverick-17b-128e-instruct-fp8", - "name": "Llama 4 Maverick Instruct", + "claude-opus-4-5-20251101": { + "id": "claude-opus-4-5-20251101", + "name": "claude-opus-4-5-20251101", + "family": "claude-opus", "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-04-06", - "last_updated": "2025-04-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.27, "output": 0.85 }, - "limit": { "context": 1048576, "output": 8192 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 65536 + }, + "cost": { + "input": 4.5, + "output": 22.5 + } }, - "nousresearch/hermes-2-pro-llama-3-8b": { - "id": "nousresearch/hermes-2-pro-llama-3-8b", - "name": "Hermes 2 Pro Llama 3 8B", - "attachment": false, - "reasoning": false, - "tool_call": false, + "gemini-2.5-flash-lite-preview-09-2025": { + "id": "gemini-2.5-flash-lite-preview-09-2025", + "name": "gemini-2.5-flash-lite-preview-09-2025", + "family": "gemini-flash-lite", + "attachment": true, + "reasoning": true, + "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2024-06-27", - "last_updated": "2024-06-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.14, "output": 0.14 }, - "limit": { "context": 8192, "output": 8192 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.09, + "output": 0.36 + } }, - "minimax/minimax-m2": { - "id": "minimax/minimax-m2", - "name": "MiniMax-M2", - "family": "minimax", - "attachment": false, + "gpt-5.2-pro": { + "id": "gpt-5.2-pro", + "name": "gpt-5.2-pro", + "family": "gpt-pro", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "structured_output": true, "temperature": true, - "release_date": "2025-10-27", - "last_updated": "2025-10-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.03 }, - "limit": { "context": 204800, "output": 131072 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 18.9, + "output": 151.2 + } }, - "minimax/minimax-m2.1": { - "id": "minimax/minimax-m2.1", - "name": "Minimax M2.1", - "family": "minimax", - "attachment": false, + "gemini-3-flash-preview": { + "id": "gemini-3-flash-preview", + "name": "gemini-3-flash-preview", + "family": "gemini-flash", + "attachment": true, "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.03 }, - "limit": { "context": 204800, "output": 131072 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 3 + } }, - "minimax/minimax-m2.5": { - "id": "minimax/minimax-m2.5", - "name": "MiniMax M2.5", - "family": "minimax", - "attachment": false, + "gpt-5-mini": { + "id": "gpt-5-mini", + "name": "gpt-5-mini", + "family": "gpt-mini", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.03 }, - "limit": { "context": 204800, "output": 131100 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.225, + "output": 1.8 + } }, - "qwen/qwen3-coder-30b-a3b-instruct": { - "id": "qwen/qwen3-coder-30b-a3b-instruct", - "name": "Qwen3 Coder 30b A3B Instruct", - "attachment": false, + "gpt-5-nano": { + "id": "gpt-5-nano", + "name": "gpt-5-nano", + "family": "gpt-nano", + "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-10-09", - "last_updated": "2025-10-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.07, "output": 0.27 }, - "limit": { "context": 160000, "output": 32768 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.045, + "output": 0.36 + } }, - "qwen/qwen3-235b-a22b-instruct-2507": { - "id": "qwen/qwen3-235b-a22b-instruct-2507", - "name": "Qwen3 235B A22B Instruct 2507", - "family": "qwen", - "attachment": false, + "gemini-3-pro-preview": { + "id": "gemini-3-pro-preview", + "name": "gemini-3-pro-preview", + "family": "gemini-pro", + "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-22", - "last_updated": "2025-07-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.09, "output": 0.58 }, - "limit": { "context": 131072, "output": 16384 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.8, + "output": 10.8 + } }, - "qwen/qwen-mt-plus": { - "id": "qwen/qwen-mt-plus", - "name": "Qwen MT Plus", - "attachment": false, + "gemini-2.5-flash-preview-05-20": { + "id": "gemini-2.5-flash-preview-05-20", + "name": "gemini-2.5-flash-preview-05-20", + "family": "gemini-flash", + "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-09-03", - "last_updated": "2025-09-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.25, "output": 0.75 }, - "limit": { "context": 16384, "output": 8192 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 200000 + }, + "cost": { + "input": 0.135, + "output": 3.15 + } }, - "qwen/qwen3-vl-235b-a22b-instruct": { - "id": "qwen/qwen3-vl-235b-a22b-instruct", - "name": "Qwen3 VL 235B A22B Instruct", + "claude-sonnet-4-5-20250929": { + "id": "claude-sonnet-4-5-20250929", + "name": "claude-sonnet-4-5-20250929", + "family": "claude-sonnet", "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-09-24", - "last_updated": "2025-09-24", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.5 }, - "limit": { "context": 131072, "output": 32768 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 2.7, + "output": 13.5 + } }, - "qwen/qwen3-coder-next": { - "id": "qwen/qwen3-coder-next", - "name": "Qwen3 Coder Next", - "family": "qwen", - "attachment": false, + "gemini-2.5-pro": { + "id": "gemini-2.5-pro", + "name": "gemini-2.5-pro", + "family": "gemini-pro", + "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2026-02-03", - "last_updated": "2026-02-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 1.5 }, - "limit": { "context": 262144, "output": 65536 } - }, - "qwen/qwen3-8b-fp8": { - "id": "qwen/qwen3-8b-fp8", - "name": "Qwen3 8B", - "attachment": false, - "reasoning": true, - "tool_call": false, - "temperature": true, - "release_date": "2025-04-29", - "last_updated": "2025-04-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.035, "output": 0.138 }, - "limit": { "context": 128000, "output": 20000 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65535 + }, + "cost": { + "input": 1.125, + "output": 9 + } }, - "qwen/qwen3-omni-30b-a3b-instruct": { - "id": "qwen/qwen3-omni-30b-a3b-instruct", - "name": "Qwen3 Omni 30B A3B Instruct", - "family": "qwen", + "grok-4-1-fast-non-reasoning": { + "id": "grok-4-1-fast-non-reasoning", + "name": "grok-4-1-fast-non-reasoning", + "family": "grok", "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-09-24", - "last_updated": "2025-09-24", - "modalities": { "input": ["text", "video", "audio", "image"], "output": ["text", "audio"] }, - "open_weights": true, - "cost": { "input": 0.25, "output": 0.97, "input_audio": 2.2, "output_audio": 1.788 }, - "limit": { "context": 65536, "output": 16384 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 2000000 + }, + "cost": { + "input": 0.18, + "output": 0.45 + } }, - "qwen/qwen2.5-7b-instruct": { - "id": "qwen/qwen2.5-7b-instruct", - "name": "Qwen2.5 7B Instruct", - "attachment": false, + "gpt-5.2": { + "id": "gpt-5.2", + "name": "gpt-5.2", + "family": "gpt", + "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.07, "output": 0.07 }, - "limit": { "context": 32000, "output": 32000 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.575, + "output": 12.6 + } }, - "qwen/qwen3-30b-a3b-fp8": { - "id": "qwen/qwen3-30b-a3b-fp8", - "name": "Qwen3 30B A3B", - "attachment": false, - "reasoning": true, - "tool_call": false, + "o4-mini": { + "id": "o4-mini", + "name": "o4-mini", + "family": "o", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-04-29", - "last_updated": "2025-04-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.09, "output": 0.45 }, - "limit": { "context": 40960, "output": 20000 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4 + } }, - "qwen/qwen3-235b-a22b-fp8": { - "id": "qwen/qwen3-235b-a22b-fp8", - "name": "Qwen3 235B A22B", - "attachment": false, - "reasoning": true, - "tool_call": false, + "gemini-2.5-pro-preview-06-05": { + "id": "gemini-2.5-pro-preview-06-05", + "name": "gemini-2.5-pro-preview-06-05", + "family": "gemini-pro", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-04-29", - "last_updated": "2025-04-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 40960, "output": 20000 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 200000 + }, + "cost": { + "input": 1.125, + "output": 9 + } }, - "qwen/qwen2.5-vl-72b-instruct": { - "id": "qwen/qwen2.5-vl-72b-instruct", - "name": "Qwen2.5 VL 72B Instruct", - "family": "qwen", + "gemini-2.5-flash-lite-preview-06-17": { + "id": "gemini-2.5-flash-lite-preview-06-17", + "name": "gemini-2.5-flash-lite-preview-06-17", + "family": "gemini-flash-lite", "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-03-25", - "last_updated": "2025-03-25", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.8, "output": 0.8 }, - "limit": { "context": 32768, "output": 32768 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "video", "image", "audio"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65535 + }, + "cost": { + "input": 0.09, + "output": 0.36 + } }, - "qwen/qwen3-vl-8b-instruct": { - "id": "qwen/qwen3-vl-8b-instruct", - "name": "qwen/qwen3-vl-8b-instruct", + "gpt-5.2-codex": { + "id": "gpt-5.2-codex", + "name": "gpt-5.2-codex", + "family": "gpt-codex", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-10-17", - "last_updated": "2025-10-17", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.08, "output": 0.5 }, - "limit": { "context": 131072, "output": 32768 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14 + } }, - "qwen/qwen3-coder-480b-a35b-instruct": { - "id": "qwen/qwen3-coder-480b-a35b-instruct", - "name": "Qwen3 Coder 480B A35B Instruct", - "family": "qwen", - "attachment": false, + "gemini-2.5-flash": { + "id": "gemini-2.5-flash", + "name": "gemini-2.5-flash", + "family": "gemini-flash", + "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.3 }, - "limit": { "context": 262144, "output": 65536 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65535 + }, + "cost": { + "input": 0.27, + "output": 2.25 + } }, - "qwen/qwen3-next-80b-a3b-thinking": { - "id": "qwen/qwen3-next-80b-a3b-thinking", - "name": "Qwen3 Next 80B A3B Thinking", - "attachment": false, + "gpt-5.1-codex-mini": { + "id": "gpt-5.1-codex-mini", + "name": "gpt-5.1-codex-mini", + "family": "gpt-codex", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-09-10", - "last_updated": "2025-09-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 1.5 }, - "limit": { "context": 131072, "output": 32768 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.225, + "output": 1.8 + } }, - "qwen/qwen3-vl-30b-a3b-instruct": { - "id": "qwen/qwen3-vl-30b-a3b-instruct", - "name": "qwen/qwen3-vl-30b-a3b-instruct", + "grok-code-fast-1": { + "id": "grok-code-fast-1", + "name": "grok-code-fast-1", + "family": "grok", "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-10-11", - "last_updated": "2025-10-11", - "modalities": { "input": ["text", "video", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.7 }, - "limit": { "context": 131072, "output": 32768 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.18, + "output": 1.35 + } }, - "qwen/qwen3-235b-a22b-thinking-2507": { - "id": "qwen/qwen3-235b-a22b-thinking-2507", - "name": "Qwen3 235B A22b Thinking 2507", - "family": "qwen", - "attachment": false, + "gpt-5.1": { + "id": "gpt-5.1", + "name": "gpt-5.1", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-25", - "last_updated": "2025-07-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 3 }, - "limit": { "context": 131072, "output": 32768 } + "release_date": "2026-02", + "last_updated": "2026-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.125, + "output": 9 + } }, - "qwen/qwen3-next-80b-a3b-instruct": { - "id": "qwen/qwen3-next-80b-a3b-instruct", - "name": "Qwen3 Next 80B A3B Instruct", - "attachment": false, + "grok-4-fast-reasoning": { + "id": "grok-4-fast-reasoning", + "name": "grok-4-fast-reasoning", + "family": "grok", + "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-09-10", - "last_updated": "2025-09-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 1.5 }, - "limit": { "context": 131072, "output": 32768 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 2000000 + }, + "cost": { + "input": 0.18, + "output": 0.45 + } }, - "qwen/qwen3-omni-30b-a3b-thinking": { - "id": "qwen/qwen3-omni-30b-a3b-thinking", - "name": "Qwen3 Omni 30B A3B Thinking", + "o3-mini": { + "id": "o3-mini", + "name": "o3-mini", + "family": "o", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-09-24", - "last_updated": "2025-09-24", - "modalities": { "input": ["text", "audio", "video", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.25, "output": 0.97, "input_audio": 2.2, "output_audio": 1.788 }, - "limit": { "context": 65536, "output": 16384 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 1.1, + "output": 4.4 + } }, - "qwen/qwen3-vl-30b-a3b-thinking": { - "id": "qwen/qwen3-vl-30b-a3b-thinking", - "name": "qwen/qwen3-vl-30b-a3b-thinking", + "grok-4-0709": { + "id": "grok-4-0709", + "name": "grok-4-0709", + "family": "grok", "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-10-11", - "last_updated": "2025-10-11", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 1 }, - "limit": { "context": 131072, "output": 32768 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 8192 + }, + "cost": { + "input": 2.7, + "output": 13.5 + } }, - "qwen/qwen3-32b-fp8": { - "id": "qwen/qwen3-32b-fp8", - "name": "Qwen3 32B", - "attachment": false, - "reasoning": true, - "tool_call": false, + "gpt-5-codex": { + "id": "gpt-5-codex", + "name": "gpt-5-codex", + "family": "gpt-codex", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-04-29", - "last_updated": "2025-04-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.45 }, - "limit": { "context": 40960, "output": 20000 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.125, + "output": 9 + } }, - "qwen/qwen3.5-397b-a17b": { - "id": "qwen/qwen3.5-397b-a17b", - "name": "Qwen3.5-397B-A17B", - "family": "qwen", + "claude-opus-4-1-20250805": { + "id": "claude-opus-4-1-20250805", + "name": "claude-opus-4-1-20250805", + "family": "claude-opus", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2026-02-17", - "last_updated": "2026-02-17", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 3.6 }, - "limit": { "context": 262144, "output": 64000 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 13.5, + "output": 67.5 + } }, - "qwen/qwen3-max": { - "id": "qwen/qwen3-max", - "name": "Qwen3 Max", - "family": "qwen", - "attachment": false, + "claude-haiku-4-5-20251001": { + "id": "claude-haiku-4-5-20251001", + "name": "claude-haiku-4-5-20251001", + "family": "claude-haiku", + "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-24", - "last_updated": "2025-09-24", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.11, "output": 8.45 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 20000, + "output": 64000 + }, + "cost": { + "input": 0.9, + "output": 4.5 + } }, - "qwen/qwen3-4b-fp8": { - "id": "qwen/qwen3-4b-fp8", - "name": "Qwen3 4B", - "attachment": false, - "reasoning": true, - "tool_call": false, + "claude-sonnet-4-20250514": { + "id": "claude-sonnet-4-20250514", + "name": "claude-sonnet-4-20250514", + "family": "claude-sonnet", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-04-29", - "last_updated": "2025-04-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.03, "output": 0.03 }, - "limit": { "context": 128000, "output": 20000 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 2.7, + "output": 13.5 + } }, - "qwen/qwen3-vl-235b-a22b-thinking": { - "id": "qwen/qwen3-vl-235b-a22b-thinking", - "name": "Qwen3 VL 235B A22B Thinking", + "claude-opus-4-6": { + "id": "claude-opus-4-6", + "name": "claude-opus-4-6", + "family": "claude-opus", "attachment": true, "reasoning": true, - "tool_call": false, - "temperature": true, - "release_date": "2025-09-24", - "last_updated": "2025-09-24", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.98, "output": 3.95 }, - "limit": { "context": 131072, "output": 32768 } - }, - "qwen/qwen-2.5-72b-instruct": { - "id": "qwen/qwen-2.5-72b-instruct", - "name": "Qwen 2.5 72B Instruct", - "family": "qwen", - "attachment": false, - "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-10-15", - "last_updated": "2024-10-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.38, "output": 0.4 }, - "limit": { "context": 32000, "output": 8192 } + "knowledge": "2025-05-31", + "release_date": "2026-02", + "last_updated": "2026-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25 + } }, - "baichuan/baichuan-m2-32b": { - "id": "baichuan/baichuan-m2-32b", - "name": "baichuan-m2-32b", - "family": "baichuan", - "attachment": false, + "o3": { + "id": "o3", + "name": "o3", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-08-13", - "last_updated": "2025-08-13", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.07, "output": 0.07 }, - "limit": { "context": 131072, "output": 131072 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 10, + "output": 40 + } }, - "moonshotai/kimi-k2-thinking": { - "id": "moonshotai/kimi-k2-thinking", - "name": "Kimi K2 Thinking", - "family": "kimi", - "attachment": false, + "gpt-5-pro": { + "id": "gpt-5-pro", + "name": "gpt-5-pro", + "family": "gpt-pro", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "release_date": "2025-11-07", - "last_updated": "2025-11-07", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.5 }, - "limit": { "context": 262144, "output": 262144 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 272000 + }, + "cost": { + "input": 13.5, + "output": 108 + } }, - "moonshotai/kimi-k2.5": { - "id": "moonshotai/kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi", + "gemini-2.5-flash-lite": { + "id": "gemini-2.5-flash-lite", + "name": "gemini-2.5-flash-lite", + "family": "gemini-flash-lite", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 3, "cache_read": 0.1 }, - "limit": { "context": 262144, "output": 262144 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65535 + }, + "cost": { + "input": 0.09, + "output": 0.36 + } }, - "moonshotai/kimi-k2-0905": { - "id": "moonshotai/kimi-k2-0905", - "name": "Kimi K2 0905", - "family": "kimi", - "attachment": false, + "gpt-5-chat-latest": { + "id": "gpt-5-chat-latest", + "name": "gpt-5-chat-latest", + "family": "gpt", + "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.5 }, - "limit": { "context": 262144, "output": 262144 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.125, + "output": 9 + } }, - "moonshotai/kimi-k2-instruct": { - "id": "moonshotai/kimi-k2-instruct", - "name": "Kimi K2 Instruct", - "attachment": false, + "claude-opus-4-20250514": { + "id": "claude-opus-4-20250514", + "name": "claude-opus-4-20250514", + "family": "claude-opus", + "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-07-11", - "last_updated": "2025-07-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.57, "output": 2.3 }, - "limit": { "context": 131072, "output": 131072 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 13.5, + "output": 67.5 + } }, - "xiaomimimo/mimo-v2-flash": { - "id": "xiaomimimo/mimo-v2-flash", - "name": "XiaomiMiMo/MiMo-V2-Flash", - "family": "mimo", - "attachment": false, + "gpt-5.1-codex": { + "id": "gpt-5.1-codex", + "name": "gpt-5.1-codex", + "family": "gpt-codex", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-12-19", - "last_updated": "2025-12-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.3, "cache_read": 0.3 }, - "limit": { "context": 262144, "output": 32000 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.125, + "output": 9 + } }, - "mistralai/mistral-nemo": { - "id": "mistralai/mistral-nemo", - "name": "Mistral Nemo", - "family": "mistral-nemo", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": true, - "temperature": true, - "release_date": "2024-07-30", - "last_updated": "2024-07-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.04, "output": 0.17 }, - "limit": { "context": 60288, "output": 16000 } - } - } - }, - "cortecs": { - "id": "cortecs", - "env": ["CORTECS_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.cortecs.ai/v1", - "name": "Cortecs", - "doc": "https://api.cortecs.ai/v1/models", - "models": { - "claude-4-6-sonnet": { - "id": "claude-4-6-sonnet", - "name": "Claude Sonnet 4.6", - "family": "claude-sonnet", + "grok-4-fast-non-reasoning": { + "id": "grok-4-fast-non-reasoning", + "name": "grok-4-fast-non-reasoning", + "family": "grok", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-08", - "release_date": "2026-02-17", - "last_updated": "2026-03-13", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3.59, "output": 17.92 }, - "limit": { "context": 1000000, "output": 1000000 } + "limit": { + "context": 2000000, + "output": 2000000 + }, + "cost": { + "input": 0.18, + "output": 0.45 + } }, - "deepseek-v3-0324": { - "id": "deepseek-v3-0324", + "deepseek/deepseek-v3-0324": { + "id": "deepseek/deepseek-v3-0324", "name": "DeepSeek V3 0324", "family": "deepseek", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-03-24", - "last_updated": "2025-03-24", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.551, "output": 1.654 }, - "limit": { "context": 128000, "output": 128000 } - }, - "kimi-k2-thinking": { - "id": "kimi-k2-thinking", - "name": "Kimi K2 Thinking", - "attachment": true, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2025-12", - "release_date": "2025-12-08", - "last_updated": "2025-12-08", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.656, "output": 2.731 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 163840, + "output": 163840 + }, + "cost": { + "input": 0.28, + "output": 1.14 + } }, - "devstral-2512": { - "id": "devstral-2512", - "name": "Devstral 2 2512", + "deepseek/deepseek-v3.1": { + "id": "deepseek/deepseek-v3.1", + "name": "DeepSeek V3.1", + "family": "deepseek", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-12", - "release_date": "2025-12-09", - "last_updated": "2025-12-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 163840, + "output": 32768 + }, + "cost": { + "input": 0.27, + "output": 1 + } }, - "kimi-k2.5": { - "id": "kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi-thinking", + "deepseek/deepseek-r1-0528": { + "id": "deepseek/deepseek-r1-0528", + "name": "DeepSeek R1 0528", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.55, "output": 2.76 }, - "limit": { "context": 256000, "output": 256000 } + "limit": { + "context": 163840, + "output": 32768 + }, + "cost": { + "input": 0.7, + "output": 2.5 + } }, - "llama-3.1-405b-instruct": { - "id": "llama-3.1-405b-instruct", - "name": "Llama 3.1 405B Instruct", - "family": "llama", + "zai-org/glm-4.7": { + "id": "zai-org/glm-4.7", + "name": "GLM-4.7", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 2.2 + } }, - "glm-4.7-flash": { - "id": "glm-4.7-flash", - "name": "GLM-4.7-Flash", + "zai-org/glm-4.5": { + "id": "zai-org/glm-4.5", + "name": "GLM-4.5", "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-08-08", - "last_updated": "2025-08-08", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.09, "output": 0.53 }, - "limit": { "context": 203000, "output": 203000 } + "limit": { + "context": 131072, + "output": 98304 + }, + "cost": { + "input": 0.6, + "output": 2.2 + } }, - "gpt-4.1": { - "id": "gpt-4.1", - "name": "GPT 4.1", - "family": "gpt", - "attachment": false, - "reasoning": false, + "zai-org/glm-4.5v": { + "id": "zai-org/glm-4.5v", + "name": "GLM 4.5V", + "family": "glmv", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-06", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.354, "output": 9.417 }, - "limit": { "context": 1047576, "output": 32768 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 65536, + "output": 16384 + }, + "cost": { + "input": 0.6, + "output": 1.8 + } }, - "minimax-m2": { - "id": "minimax-m2", - "name": "MiniMax-M2", - "family": "minimax", + "zai-org/glm-4.7-flash": { + "id": "zai-org/glm-4.7-flash", + "name": "GLM-4.7-Flash", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "structured_output": true, "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-10-27", - "last_updated": "2025-10-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.39, "output": 1.57 }, - "limit": { "context": 400000, "output": 400000 } + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 0.07, + "output": 0.4 + } }, - "gpt-oss-120b": { - "id": "gpt-oss-120b", - "name": "GPT Oss 120b", - "family": "gpt-oss", + "minimaxai/minimax-m1-80k": { + "id": "minimaxai/minimax-m1-80k", + "name": "MiniMax M1", + "family": "minimax", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-01", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 1000000, + "output": 40000 + }, + "cost": { + "input": 0.55, + "output": 2.2 + } }, - "glm-4.5": { - "id": "glm-4.5", - "name": "GLM 4.5", - "family": "glm", + "xiaomimimo/mimo-v2-flash": { + "id": "xiaomimimo/mimo-v2-flash", + "name": "XiaomiMiMo/MiMo-V2-Flash", + "family": "mimo", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-29", - "last_updated": "2025-07-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.67, "output": 2.46 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "minimax-m2.1": { - "id": "minimax-m2.1", - "name": "MiniMax-M2.1", - "family": "minimax", - "attachment": false, + "baidu/ernie-4.5-vl-424b-a47b": { + "id": "baidu/ernie-4.5-vl-424b-a47b", + "name": "ERNIE 4.5 VL 424B A47B", + "family": "ernie", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "structured_output": false, "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.34, "output": 1.34 }, - "limit": { "context": 196000, "output": 196000 } + "limit": { + "context": 123000, + "output": 16000 + }, + "cost": { + "input": 0.42, + "output": 1.25 + } }, - "qwen3-coder-480b-a35b-instruct": { - "id": "qwen3-coder-480b-a35b-instruct", - "name": "Qwen3 Coder 480B A35B Instruct", - "family": "qwen", + "baidu/ernie-4.5-300b-a47b-paddle": { + "id": "baidu/ernie-4.5-300b-a47b-paddle", + "name": "ERNIE 4.5 300B A47B", + "family": "ernie", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-07-25", - "last_updated": "2025-07-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.441, "output": 1.984 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 123000, + "output": 12000 + }, + "cost": { + "input": 0.28, + "output": 1.1 + } }, - "qwen3-next-80b-a3b-thinking": { - "id": "qwen3-next-80b-a3b-thinking", - "name": "Qwen3 Next 80B A3B Thinking", + "minimax/minimax-m2.1": { + "id": "minimax/minimax-m2.1", + "name": "Minimax M2.1", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-11", - "last_updated": "2025-09-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.164, "output": 1.311 }, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "gemini-2.5-pro": { - "id": "gemini-2.5-pro", - "name": "Gemini 2.5 Pro", - "family": "gemini-pro", + "qwen/qwen3-235b-a22b-instruct-2507": { + "id": "qwen/qwen3-235b-a22b-instruct-2507", + "name": "Qwen3 235B A22B Instruct 2507", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-03-20", - "last_updated": "2025-06-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.654, "output": 11.024 }, - "limit": { "context": 1048576, "output": 65535 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.8 + } }, - "claude-opus4-5": { - "id": "claude-opus4-5", - "name": "Claude Opus 4.5", - "family": "claude-opus", - "attachment": true, + "qwen/qwen3-32b-fp8": { + "id": "qwen/qwen3-32b-fp8", + "name": "Qwen3 32B", + "family": "qwen", + "attachment": false, "reasoning": true, - "tool_call": true, + "tool_call": false, + "structured_output": false, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-11-24", - "last_updated": "2025-11-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5.98, "output": 29.89 }, - "limit": { "context": 200000, "output": 200000 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 40960, + "output": 20000 + }, + "cost": { + "input": 0.1, + "output": 0.45 + } }, - "nova-pro-v1": { - "id": "nova-pro-v1", - "name": "Nova Pro 1.0", - "family": "nova-pro", + "qwen/qwen3-235b-a22b-thinking-2507": { + "id": "qwen/qwen3-235b-a22b-thinking-2507", + "name": "Qwen3 235B A22b Thinking 2507", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-12-03", - "last_updated": "2024-12-03", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.016, "output": 4.061 }, - "limit": { "context": 300000, "output": 5000 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 3 + } }, - "claude-sonnet-4": { - "id": "claude-sonnet-4", - "name": "Claude Sonnet 4", - "family": "claude-sonnet", + "qwen/qwen3-next-80b-a3b-thinking": { + "id": "qwen/qwen3-next-80b-a3b-thinking", + "name": "Qwen3 Next 80B A3B Thinking", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3.307, "output": 16.536 }, - "limit": { "context": 200000, "output": 64000 } - }, - "intellect-3": { - "id": "intellect-3", - "name": "INTELLECT 3", - "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-11", - "release_date": "2025-11-26", - "last_updated": "2025-11-26", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.219, "output": 1.202 }, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 65536, + "output": 65536 + }, + "cost": { + "input": 0.15, + "output": 1.5 + } }, - "glm-4.5-air": { - "id": "glm-4.5-air", - "name": "GLM 4.5 Air", - "family": "glm-air", + "qwen/qwen3-next-80b-a3b-instruct": { + "id": "qwen/qwen3-next-80b-a3b-instruct", + "name": "Qwen3 Next 80B A3B Instruct", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-08-01", - "last_updated": "2025-08-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.22, "output": 1.34 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 65536, + "output": 65536 + }, + "cost": { + "input": 0.15, + "output": 1.5 + } }, - "glm-4.7": { - "id": "glm-4.7", - "name": "GLM 4.7", - "family": "glm", + "qwen/qwen3-30b-a3b-fp8": { + "id": "qwen/qwen3-30b-a3b-fp8", + "name": "Qwen3 30B A3B", + "family": "qwen", "attachment": false, "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "tool_call": false, + "structured_output": false, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.45, "output": 2.23 }, - "limit": { "context": 198000, "output": 198000 } + "limit": { + "context": 40960, + "output": 20000 + }, + "cost": { + "input": 0.09, + "output": 0.45 + } }, - "devstral-small-2512": { - "id": "devstral-small-2512", - "name": "Devstral Small 2 2512", + "qwen/qwen3-coder-next": { + "id": "qwen/qwen3-coder-next", + "name": "qwen/qwen3-coder-next", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-12", - "release_date": "2025-12-09", - "last_updated": "2025-12-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-02", + "last_updated": "2026-02", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.2, + "output": 1.5 + } }, - "qwen3-32b": { - "id": "qwen3-32b", - "name": "Qwen3 32B", + "qwen/qwen3-coder-480b-a35b-instruct": { + "id": "qwen/qwen3-coder-480b-a35b-instruct", + "name": "Qwen3 Coder 480B A35B Instruct", "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-04-29", - "last_updated": "2025-04-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.099, "output": 0.33 }, - "limit": { "context": 16384, "output": 16384 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.29, + "output": 1.2 + } }, - "claude-opus4-6": { - "id": "claude-opus4-6", - "name": "Claude Opus 4.6", - "family": "claude-opus", - "attachment": true, + "qwen/qwen3-235b-a22b-fp8": { + "id": "qwen/qwen3-235b-a22b-fp8", + "name": "Qwen3 235B A22B", + "family": "qwen", + "attachment": false, "reasoning": true, - "tool_call": true, + "tool_call": false, + "structured_output": false, "temperature": true, - "knowledge": "2025-05", - "release_date": "2026-02-05", - "last_updated": "2026-03-13", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5.98, "output": 29.89 }, - "limit": { "context": 1000000, "output": 1000000 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 40960, + "output": 20000 + }, + "cost": { + "input": 0.2, + "output": 0.8 + } }, - "claude-haiku-4-5": { - "id": "claude-haiku-4-5", - "name": "Claude Haiku 4.5", - "family": "claude-haiku", + "moonshotai/kimi-k2.5": { + "id": "moonshotai/kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-02-28", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.09, "output": 5.43 }, - "limit": { "context": 200000, "output": 200000 } - }, - "minimax-m2.5": { - "id": "minimax-m2.5", - "name": "MiniMax-M2.5", - "family": "minimax", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.32, "output": 1.18 }, - "limit": { "context": 196608, "output": 196608 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 3 + } }, - "kimi-k2-instruct": { - "id": "kimi-k2-instruct", + "moonshotai/kimi-k2-instruct": { + "id": "moonshotai/kimi-k2-instruct", "name": "Kimi K2 Instruct", "family": "kimi", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-07-11", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.551, "output": 2.646 }, - "limit": { "context": 131000, "output": 131000 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.57, + "output": 2.3 + } }, - "claude-4-5-sonnet": { - "id": "claude-4-5-sonnet", - "name": "Claude 4.5 Sonnet", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, + "moonshotai/kimi-k2-0905": { + "id": "moonshotai/kimi-k2-0905", + "name": "Kimi K2 0905", + "family": "kimi", + "attachment": false, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3.259, "output": 16.296 }, - "limit": { "context": 200000, "output": 200000 } + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 2.5 + } } } }, - "siliconflow-cn": { - "id": "siliconflow-cn", - "env": ["SILICONFLOW_CN_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.siliconflow.cn/v1", - "name": "SiliconFlow (China)", - "doc": "https://cloud.siliconflow.com/models", + "bailing": { + "id": "bailing", + "env": ["BAILING_API_TOKEN"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.tbox.cn/api/llm/v1/chat/completions", + "name": "Bailing", + "doc": "https://alipaytbox.yuque.com/sxs0ba/ling/intro", "models": { - "THUDM/GLM-4-9B-0414": { - "id": "THUDM/GLM-4-9B-0414", - "name": "THUDM/GLM-4-9B-0414", - "family": "glm", + "Ring-1T": { + "id": "Ring-1T", + "name": "Ring-1T", + "family": "ring", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "knowledge": "2024-06", + "release_date": "2025-10", + "last_updated": "2025-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32000 + }, + "cost": { + "input": 0.57, + "output": 2.29 + } + }, + "Ling-1T": { + "id": "Ling-1T", + "name": "Ling-1T", + "family": "ling", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-04-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.086, "output": 0.086 }, - "limit": { "context": 33000, "output": 33000 } - }, - "THUDM/GLM-4-32B-0414": { - "id": "THUDM/GLM-4-32B-0414", - "name": "THUDM/GLM-4-32B-0414", - "family": "glm", + "knowledge": "2024-06", + "release_date": "2025-10", + "last_updated": "2025-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32000 + }, + "cost": { + "input": 0.57, + "output": 2.29 + } + } + } + }, + "iflowcn": { + "id": "iflowcn", + "env": ["IFLOW_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://apis.iflow.cn/v1", + "name": "iFlow", + "doc": "https://platform.iflow.cn/en/docs", + "models": { + "qwen3-coder-plus": { + "id": "qwen3-coder-plus", + "name": "Qwen3-Coder-Plus", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-04-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.27, "output": 0.27 }, - "limit": { "context": 33000, "output": 33000 } + "knowledge": "2025-04", + "release_date": "2025-07-01", + "last_updated": "2025-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "THUDM/GLM-Z1-9B-0414": { - "id": "THUDM/GLM-Z1-9B-0414", - "name": "THUDM/GLM-Z1-9B-0414", - "family": "glm-z", + "qwen3-32b": { + "id": "qwen3-32b", + "name": "Qwen3-32B", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-04-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.086, "output": 0.086 }, - "limit": { "context": 131000, "output": 131000 } + "knowledge": "2024-10", + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "THUDM/GLM-Z1-32B-0414": { - "id": "THUDM/GLM-Z1-32B-0414", - "name": "THUDM/GLM-Z1-32B-0414", - "family": "glm-z", + "deepseek-r1": { + "id": "deepseek-r1", + "name": "DeepSeek-R1", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-04-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.14, "output": 0.57 }, - "limit": { "context": 131000, "output": 131000 } + "knowledge": "2024-12", + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "zai-org/GLM-4.6": { - "id": "zai-org/GLM-4.6", - "name": "zai-org/GLM-4.6", - "family": "glm", + "qwen3-max": { + "id": "qwen3-max", + "name": "Qwen3-Max", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-04", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 1.9 }, - "limit": { "context": 205000, "output": 205000 } + "limit": { + "context": 256000, + "output": 32000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "zai-org/GLM-4.5V": { - "id": "zai-org/GLM-4.5V", - "name": "zai-org/GLM-4.5V", - "family": "glm", - "attachment": true, + "qwen3-235b-a22b-instruct": { + "id": "qwen3-235b-a22b-instruct", + "name": "Qwen3-235B-A22B-Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-08-13", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.14, "output": 0.86 }, - "limit": { "context": 66000, "output": 66000 } + "knowledge": "2025-04", + "release_date": "2025-07-01", + "last_updated": "2025-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "zai-org/GLM-4.6V": { - "id": "zai-org/GLM-4.6V", - "name": "zai-org/GLM-4.6V", - "family": "glm", - "attachment": true, + "qwen3-235b-a22b-thinking-2507": { + "id": "qwen3-235b-a22b-thinking-2507", + "name": "Qwen3-235B-A22B-Thinking", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": false, "temperature": true, - "release_date": "2025-12-07", - "last_updated": "2025-12-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 0.9 }, - "limit": { "context": 131000, "output": 131000 } + "knowledge": "2025-04", + "release_date": "2025-07-01", + "last_updated": "2025-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "zai-org/GLM-4.5-Air": { - "id": "zai-org/GLM-4.5-Air", - "name": "zai-org/GLM-4.5-Air", - "family": "glm-air", + "kimi-k2-0905": { + "id": "kimi-k2-0905", + "name": "Kimi-K2-0905", + "family": "kimi", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-28", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.14, "output": 0.86 }, - "limit": { "context": 131000, "output": 131000 } + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "stepfun-ai/Step-3.5-Flash": { - "id": "stepfun-ai/Step-3.5-Flash", - "name": "stepfun-ai/Step-3.5-Flash", - "family": "step", + "glm-4.6": { + "id": "glm-4.6", + "name": "GLM-4.6", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2024-12-01", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "Pro/zai-org/GLM-4.7": { - "id": "Pro/zai-org/GLM-4.7", - "name": "Pro/zai-org/GLM-4.7", - "family": "glm", - "attachment": false, - "reasoning": true, + "qwen3-vl-plus": { + "id": "qwen3-vl-plus", + "name": "Qwen3-VL-Plus", + "family": "qwen", + "attachment": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 2.2 }, - "limit": { "context": 205000, "output": 205000 } + "limit": { + "context": 256000, + "output": 32000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "Pro/zai-org/GLM-5": { - "id": "Pro/zai-org/GLM-5", - "name": "Pro/zai-org/GLM-5", - "family": "glm", + "deepseek-v3.2": { + "id": "deepseek-v3.2", + "name": "DeepSeek-V3.2-Exp", + "family": "deepseek", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1, "output": 3.2 }, - "limit": { "context": 205000, "output": 205000 } + "limit": { + "context": 128000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "Pro/MiniMaxAI/MiniMax-M2.1": { - "id": "Pro/MiniMaxAI/MiniMax-M2.1", - "name": "Pro/MiniMaxAI/MiniMax-M2.1", - "family": "minimax", + "qwen3-235b": { + "id": "qwen3-235b", + "name": "Qwen3-235B-A22B", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 197000, "output": 131000 } + "knowledge": "2024-10", + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "Pro/MiniMaxAI/MiniMax-M2.5": { - "id": "Pro/MiniMaxAI/MiniMax-M2.5", - "name": "Pro/MiniMaxAI/MiniMax-M2.5", - "family": "minimax", + "kimi-k2": { + "id": "kimi-k2", + "name": "Kimi-K2", + "family": "kimi", "attachment": false, "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "release_date": "2026-02-13", - "last_updated": "2026-02-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.22 }, - "limit": { "context": 192000, "output": 131000 } + "limit": { + "context": 128000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "Pro/deepseek-ai/DeepSeek-R1": { - "id": "Pro/deepseek-ai/DeepSeek-R1", - "name": "Pro/deepseek-ai/DeepSeek-R1", - "family": "deepseek-thinking", + "qwen3-max-preview": { + "id": "qwen3-max-preview", + "name": "Qwen3-Max-Preview", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-05-28", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 2.18 }, - "limit": { "context": 164000, "output": 164000 } + "limit": { + "context": 256000, + "output": 32000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "Pro/deepseek-ai/DeepSeek-V3.1-Terminus": { - "id": "Pro/deepseek-ai/DeepSeek-V3.1-Terminus", - "name": "Pro/deepseek-ai/DeepSeek-V3.1-Terminus", + "deepseek-v3": { + "id": "deepseek-v3", + "name": "DeepSeek-V3", "family": "deepseek", "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2024-12-26", + "last_updated": "2024-12-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32000 + }, + "cost": { + "input": 0, + "output": 0 + } + } + } + }, + "v0": { + "id": "v0", + "env": ["V0_API_KEY"], + "npm": "@ai-sdk/vercel", + "name": "v0", + "doc": "https://sdk.vercel.ai/providers/ai-sdk-providers/vercel", + "models": { + "v0-1.5-lg": { + "id": "v0-1.5-lg", + "name": "v0-1.5-lg", + "family": "v0", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-09-29", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-06-09", + "last_updated": "2025-06-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.27, "output": 1 }, - "limit": { "context": 164000, "output": 164000 } + "limit": { + "context": 512000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75 + } }, - "Pro/deepseek-ai/DeepSeek-V3.2": { - "id": "Pro/deepseek-ai/DeepSeek-V3.2", - "name": "Pro/deepseek-ai/DeepSeek-V3.2", - "family": "deepseek", - "attachment": false, + "v0-1.0-md": { + "id": "v0-1.0-md", + "name": "v0-1.0-md", + "family": "v0", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-12-03", - "last_updated": "2025-12-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.27, "output": 0.42 }, - "limit": { "context": 164000, "output": 164000 } + "limit": { + "context": 128000, + "output": 32000 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "Pro/deepseek-ai/DeepSeek-V3": { - "id": "Pro/deepseek-ai/DeepSeek-V3", - "name": "Pro/deepseek-ai/DeepSeek-V3", - "family": "deepseek", - "attachment": false, - "reasoning": false, + "v0-1.5-md": { + "id": "v0-1.5-md", + "name": "v0-1.5-md", + "family": "v0", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-12-26", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-06-09", + "last_updated": "2025-06-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 1 }, - "limit": { "context": 164000, "output": 164000 } - }, - "Pro/moonshotai/Kimi-K2.5": { - "id": "Pro/moonshotai/Kimi-K2.5", - "name": "Pro/moonshotai/Kimi-K2.5", - "family": "kimi", - "attachment": false, + "limit": { + "context": 128000, + "output": 32000 + }, + "cost": { + "input": 3, + "output": 15 + } + } + } + }, + "huggingface": { + "id": "huggingface", + "env": ["HF_TOKEN"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://router.huggingface.co/v1", + "name": "Hugging Face", + "doc": "https://huggingface.co/docs/inference-providers", + "models": { + "Qwen/Qwen3.5-397B-A17B": { + "id": "Qwen/Qwen3.5-397B-A17B", + "name": "Qwen3.5-397B-A17B", + "family": "qwen", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.55, "output": 3 }, - "limit": { "context": 262000, "output": 262000 } + "knowledge": "2025-04", + "release_date": "2026-02-01", + "last_updated": "2026-02-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0.6, + "output": 3.6 + } }, - "Pro/moonshotai/Kimi-K2-Instruct-0905": { - "id": "Pro/moonshotai/Kimi-K2-Instruct-0905", - "name": "Pro/moonshotai/Kimi-K2-Instruct-0905", - "family": "kimi", + "Qwen/Qwen3-Coder-Next": { + "id": "Qwen/Qwen3-Coder-Next", + "name": "Qwen3-Coder-Next", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-09-08", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 262000, "output": 262000 } + "knowledge": "2025-04", + "release_date": "2026-02-03", + "last_updated": "2026-02-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.2, + "output": 1.5 + } }, - "Pro/moonshotai/Kimi-K2-Thinking": { - "id": "Pro/moonshotai/Kimi-K2-Thinking", - "name": "Pro/moonshotai/Kimi-K2-Thinking", - "family": "kimi-thinking", + "Qwen/Qwen3-Next-80B-A3B-Instruct": { + "id": "Qwen/Qwen3-Next-80B-A3B-Instruct", + "name": "Qwen3-Next-80B-A3B-Instruct", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-11-07", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.55, "output": 2.5 }, - "limit": { "context": 262000, "output": 262000 } + "knowledge": "2025-04", + "release_date": "2025-09-11", + "last_updated": "2025-09-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 66536 + }, + "cost": { + "input": 0.25, + "output": 1 + } }, - "baidu/ERNIE-4.5-300B-A47B": { - "id": "baidu/ERNIE-4.5-300B-A47B", - "name": "baidu/ERNIE-4.5-300B-A47B", - "family": "ernie", + "Qwen/Qwen3-Embedding-8B": { + "id": "Qwen/Qwen3-Embedding-8B", + "name": "Qwen 3 Embedding 8B", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-07-02", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.28, "output": 1.1 }, - "limit": { "context": 131000, "output": 131000 } - }, - "PaddlePaddle/PaddleOCR-VL-1.5": { - "id": "PaddlePaddle/PaddleOCR-VL-1.5", - "name": "PaddlePaddle/PaddleOCR-VL-1.5", - "attachment": true, - "reasoning": false, "tool_call": false, + "temperature": false, + "knowledge": "2024-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32000, + "output": 4096 + }, + "cost": { + "input": 0.01, + "output": 0 + } + }, + "Qwen/Qwen3-235B-A22B-Thinking-2507": { + "id": "Qwen/Qwen3-235B-A22B-Thinking-2507", + "name": "Qwen3-235B-A22B-Thinking-2507", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2026-01-29", - "last_updated": "2026-01-29", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-25", + "last_updated": "2025-07-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 16384, "output": 16384 } + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 3 + } }, - "PaddlePaddle/PaddleOCR-VL": { - "id": "PaddlePaddle/PaddleOCR-VL", - "name": "PaddlePaddle/PaddleOCR-VL", - "attachment": true, + "Qwen/Qwen3-Next-80B-A3B-Thinking": { + "id": "Qwen/Qwen3-Next-80B-A3B-Thinking", + "name": "Qwen3-Next-80B-A3B-Thinking", + "family": "qwen", + "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2025-10-16", - "last_updated": "2025-10-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-09-11", + "last_updated": "2025-09-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 16384, "output": 16384 } + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 2 + } }, - "deepseek-ai/DeepSeek-OCR": { - "id": "deepseek-ai/DeepSeek-OCR", - "name": "deepseek-ai/DeepSeek-OCR", - "attachment": true, + "Qwen/Qwen3-Embedding-4B": { + "id": "Qwen/Qwen3-Embedding-4B", + "name": "Qwen 3 Embedding 4B", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-10-20", - "last_updated": "2025-10-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 8192, "output": 8192 } + "limit": { + "context": 32000, + "output": 2048 + }, + "cost": { + "input": 0.01, + "output": 0 + } }, - "deepseek-ai/DeepSeek-V3": { - "id": "deepseek-ai/DeepSeek-V3", - "name": "deepseek-ai/DeepSeek-V3", - "family": "deepseek", + "Qwen/Qwen3-Coder-480B-A35B-Instruct": { + "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct", + "name": "Qwen3-Coder-480B-A35B-Instruct", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-12-26", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 1 }, - "limit": { "context": 164000, "output": 164000 } + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 66536 + }, + "cost": { + "input": 2, + "output": 2 + } }, - "deepseek-ai/DeepSeek-V3.2": { - "id": "deepseek-ai/DeepSeek-V3.2", - "name": "deepseek-ai/DeepSeek-V3.2", - "family": "deepseek", + "zai-org/GLM-4.7-Flash": { + "id": "zai-org/GLM-4.7-Flash", + "name": "GLM-4.7-Flash", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-12-03", - "last_updated": "2025-12-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.27, "output": 0.42 }, - "limit": { "context": 164000, "output": 164000 } + "knowledge": "2025-04", + "release_date": "2025-08-08", + "last_updated": "2025-08-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B": { - "id": "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B", - "name": "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B", - "family": "qwen", + "zai-org/GLM-4.7": { + "id": "zai-org/GLM-4.7", + "name": "GLM-4.7", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-01-20", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.1 }, - "limit": { "context": 131000, "output": 131000 } + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.11 + } }, - "deepseek-ai/DeepSeek-V3.1-Terminus": { - "id": "deepseek-ai/DeepSeek-V3.1-Terminus", - "name": "deepseek-ai/DeepSeek-V3.1-Terminus", - "family": "deepseek", + "zai-org/GLM-5.1": { + "id": "zai-org/GLM-5.1", + "name": "GLM-5.1", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-09-29", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.27, "output": 1 }, - "limit": { "context": 164000, "output": 164000 } + "release_date": "2026-04-03", + "last_updated": "2026-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3.2, + "cache_read": 0.2 + } }, - "deepseek-ai/DeepSeek-R1": { - "id": "deepseek-ai/DeepSeek-R1", - "name": "deepseek-ai/DeepSeek-R1", + "zai-org/GLM-5": { + "id": "zai-org/GLM-5", + "name": "GLM-5", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3.2, + "cache_read": 0.2 + } + }, + "XiaomiMiMo/MiMo-V2-Flash": { + "id": "XiaomiMiMo/MiMo-V2-Flash", + "name": "MiMo-V2-Flash", + "family": "mimo", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2025-12-16", + "last_updated": "2025-12-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 4096 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } + }, + "deepseek-ai/DeepSeek-R1-0528": { + "id": "deepseek-ai/DeepSeek-R1-0528", + "name": "DeepSeek-R1-0528", "family": "deepseek-thinking", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, + "knowledge": "2025-05", "release_date": "2025-05-28", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 2.18 }, - "limit": { "context": 164000, "output": 164000 } + "last_updated": "2025-05-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 163840 + }, + "cost": { + "input": 3, + "output": 5 + } }, - "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B": { - "id": "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", - "name": "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", - "family": "qwen", + "deepseek-ai/DeepSeek-V3.2": { + "id": "deepseek-ai/DeepSeek-V3.2", + "name": "DeepSeek-V3.2", + "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-01-20", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.18, "output": 0.18 }, - "limit": { "context": 131000, "output": 131000 } + "knowledge": "2024-07", + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 65536 + }, + "cost": { + "input": 0.28, + "output": 0.4 + } }, - "deepseek-ai/deepseek-vl2": { - "id": "deepseek-ai/deepseek-vl2", - "name": "deepseek-ai/deepseek-vl2", - "family": "deepseek", + "moonshotai/Kimi-K2-Thinking": { + "id": "moonshotai/Kimi-K2-Thinking", + "name": "Kimi-K2-Thinking", + "family": "kimi-thinking", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-08", + "release_date": "2025-11-06", + "last_updated": "2025-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 2.5, + "cache_read": 0.15 + } + }, + "moonshotai/Kimi-K2.6": { + "id": "moonshotai/Kimi-K2.6", + "name": "Kimi-K2.6", + "family": "kimi", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2024-12-13", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.15 }, - "limit": { "context": 4000, "output": 4000 } + "knowledge": "2025-01", + "release_date": "2026-04-20", + "last_updated": "2026-04-20", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.95, + "output": 4, + "cache_read": 0.16 + } }, - "ByteDance-Seed/Seed-OSS-36B-Instruct": { - "id": "ByteDance-Seed/Seed-OSS-36B-Instruct", - "name": "ByteDance-Seed/Seed-OSS-36B-Instruct", - "family": "seed", + "moonshotai/Kimi-K2-Instruct": { + "id": "moonshotai/Kimi-K2-Instruct", + "name": "Kimi-K2-Instruct", + "family": "kimi", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-09-04", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.21, "output": 0.57 }, - "limit": { "context": 262000, "output": 262000 } + "knowledge": "2024-10", + "release_date": "2025-07-14", + "last_updated": "2025-07-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 1, + "output": 3 + } }, - "Qwen/Qwen3.5-35B-A3B": { - "id": "Qwen/Qwen3.5-35B-A3B", - "name": "Qwen/Qwen3.5-35B-A3B", - "family": "qwen", + "moonshotai/Kimi-K2-Instruct-0905": { + "id": "moonshotai/Kimi-K2-Instruct-0905", + "name": "Kimi-K2-Instruct-0905", + "family": "kimi", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-02-25", - "last_updated": "2026-02-25", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-09-04", + "last_updated": "2025-09-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.23, "output": 1.86 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 262144, + "output": 16384 + }, + "cost": { + "input": 1, + "output": 3 + } }, - "Qwen/Qwen3.5-397B-A17B": { - "id": "Qwen/Qwen3.5-397B-A17B", - "name": "Qwen/Qwen3.5-397B-A17B", - "family": "qwen", - "attachment": false, + "moonshotai/Kimi-K2.5": { + "id": "moonshotai/Kimi-K2.5", + "name": "Kimi-K2.5", + "family": "kimi", + "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-02-16", - "last_updated": "2026-02-16", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-01-01", + "last_updated": "2026-01-01", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.29, "output": 1.74 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 3, + "cache_read": 0.1 + } }, - "Qwen/Qwen3.5-122B-A10B": { - "id": "Qwen/Qwen3.5-122B-A10B", - "name": "Qwen/Qwen3.5-122B-A10B", - "family": "qwen", + "MiniMaxAI/MiniMax-M2.5": { + "id": "MiniMaxAI/MiniMax-M2.5", + "name": "MiniMax-M2.5", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-02-26", - "last_updated": "2026-02-26", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.29, "output": 2.32 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03 + } }, - "Qwen/Qwen3.5-9B": { - "id": "Qwen/Qwen3.5-9B", - "name": "Qwen/Qwen3.5-9B", - "family": "qwen", + "MiniMaxAI/MiniMax-M2.7": { + "id": "MiniMaxAI/MiniMax-M2.7", + "name": "MiniMax-M2.7", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-03-03", - "last_updated": "2026-03-03", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.22, "output": 1.74 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06 + } }, - "Qwen/Qwen3.5-27B": { - "id": "Qwen/Qwen3.5-27B", - "name": "Qwen/Qwen3.5-27B", - "family": "qwen", + "MiniMaxAI/MiniMax-M2.1": { + "id": "MiniMaxAI/MiniMax-M2.1", + "name": "MiniMax-M2.1", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-02-25", - "last_updated": "2026-02-25", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2025-10", + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.26, "output": 2.09 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "Qwen/Qwen3.5-4B": { - "id": "Qwen/Qwen3.5-4B", - "name": "Qwen/Qwen3.5-4B", - "family": "qwen", + "deepseek-ai/DeepSeek-V4-Pro": { + "id": "deepseek-ai/DeepSeek-V4-Pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-03-03", - "last_updated": "2026-03-03", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 65536 } - }, - "Qwen/Qwen3-235B-A22B-Instruct-2507": { - "id": "Qwen/Qwen3-235B-A22B-Instruct-2507", - "name": "Qwen/Qwen3-235B-A22B-Instruct-2507", - "family": "qwen", + "limit": { + "context": 1048576, + "output": 393216 + }, + "cost": { + "input": 1.74, + "output": 3.48, + "cache_read": 0.145 + } + } + } + }, + "zenmux": { + "id": "zenmux", + "env": ["ZENMUX_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://zenmux.ai/api/v1", + "name": "ZenMux", + "doc": "https://docs.zenmux.ai", + "models": { + "deepseek/deepseek-chat": { + "id": "deepseek/deepseek-chat", + "name": "DeepSeek-V3.2 (Non-thinking Mode)", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-23", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.09, "output": 0.6 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 128000, + "output": 64000 + }, + "cost": { + "input": 0.28, + "output": 0.42, + "cache_read": 0.03 + } }, - "Qwen/Qwen2.5-32B-Instruct": { - "id": "Qwen/Qwen2.5-32B-Instruct", - "name": "Qwen/Qwen2.5-32B-Instruct", - "family": "qwen", + "deepseek/deepseek-v3.2-exp": { + "id": "deepseek/deepseek-v3.2-exp", + "name": "DeepSeek-V3.2-Exp", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-09-19", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.18, "output": 0.18 }, - "limit": { "context": 33000, "output": 4000 } + "limit": { + "context": 163000, + "output": 64000 + }, + "cost": { + "input": 0.22, + "output": 0.33 + } }, - "Qwen/Qwen3-VL-32B-Thinking": { - "id": "Qwen/Qwen3-VL-32B-Thinking", - "name": "Qwen/Qwen3-VL-32B-Thinking", - "family": "qwen", - "attachment": true, + "deepseek/deepseek-v3.2": { + "id": "deepseek/deepseek-v3.2", + "name": "DeepSeek V3.2", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-21", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-12-05", + "last_updated": "2025-12-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 1.5 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 128000, + "output": 64000 + }, + "cost": { + "input": 0.28, + "output": 0.43 + } }, - "Qwen/Qwen2.5-72B-Instruct": { - "id": "Qwen/Qwen2.5-72B-Instruct", - "name": "Qwen/Qwen2.5-72B-Instruct", - "family": "qwen", + "inclusionai/ring-1t": { + "id": "inclusionai/ring-1t", + "name": "Ring-1T", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-09-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-10-12", + "last_updated": "2025-10-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.59, "output": 0.59 }, - "limit": { "context": 33000, "output": 4000 } + "limit": { + "context": 128000, + "output": 64000 + }, + "cost": { + "input": 0.56, + "output": 2.24, + "cache_read": 0.11 + } }, - "Qwen/Qwen3-VL-235B-A22B-Instruct": { - "id": "Qwen/Qwen3-VL-235B-A22B-Instruct", - "name": "Qwen/Qwen3-VL-235B-A22B-Instruct", - "family": "qwen", - "attachment": true, + "inclusionai/ling-1t": { + "id": "inclusionai/ling-1t", + "name": "Ling-1T", + "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-04", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 1.5 }, - "limit": { "context": 262000, "output": 262000 } - }, - "Qwen/Qwen2.5-7B-Instruct": { - "id": "Qwen/Qwen2.5-7B-Instruct", - "name": "Qwen/Qwen2.5-7B-Instruct", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2024-09-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-10-09", + "last_updated": "2025-10-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.05, "output": 0.05 }, - "limit": { "context": 33000, "output": 4000 } + "limit": { + "context": 128000, + "output": 64000 + }, + "cost": { + "input": 0.56, + "output": 2.24, + "cache_read": 0.11 + } }, - "Qwen/Qwen3-Omni-30B-A3B-Instruct": { - "id": "Qwen/Qwen3-Omni-30B-A3B-Instruct", - "name": "Qwen/Qwen3-Omni-30B-A3B-Instruct", - "family": "qwen", - "attachment": true, - "reasoning": false, + "stepfun/step-3.5-flash-free": { + "id": "stepfun/step-3.5-flash-free", + "name": "Step 3.5 Flash (Free)", + "attachment": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-04", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image", "audio"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2026-02-02", + "last_updated": "2026-02-02", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 66000, "output": 66000 } + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "Qwen/Qwen3-30B-A3B-Instruct-2507": { - "id": "Qwen/Qwen3-30B-A3B-Instruct-2507", - "name": "Qwen/Qwen3-30B-A3B-Instruct-2507", - "family": "qwen", + "stepfun/step-3.5-flash": { + "id": "stepfun/step-3.5-flash", + "name": "Step 3.5 Flash", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-30", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2026-02-02", + "last_updated": "2026-02-02", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.09, "output": 0.3 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } }, - "Qwen/Qwen3-VL-32B-Instruct": { - "id": "Qwen/Qwen3-VL-32B-Instruct", - "name": "Qwen/Qwen3-VL-32B-Instruct", - "family": "qwen", + "stepfun/step-3": { + "id": "stepfun/step-3", + "name": "Step-3", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-21", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-07-31", + "last_updated": "2025-07-31", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.6 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 65536, + "output": 64000 + }, + "cost": { + "input": 0.21, + "output": 0.57 + } }, - "Qwen/Qwen3-Coder-480B-A35B-Instruct": { - "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct", - "name": "Qwen/Qwen3-Coder-480B-A35B-Instruct", - "family": "qwen", + "kuaishou/kat-coder-pro-v2": { + "id": "kuaishou/kat-coder-pro-v2", + "name": "KAT-Coder-Pro-V2", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-31", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-30", + "last_updated": "2026-03-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 1 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 256000, + "output": 80000 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06 + } }, - "Qwen/QwQ-32B": { - "id": "Qwen/QwQ-32B", - "name": "Qwen/QwQ-32B", - "family": "qwen", - "attachment": false, + "x-ai/grok-4-fast": { + "id": "x-ai/grok-4-fast", + "name": "Grok 4 Fast", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-03-06", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-09-19", + "last_updated": "2025-09-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.58 }, - "limit": { "context": 131000, "output": 131000 } + "limit": { + "context": 2000000, + "output": 64000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "Qwen/Qwen3-8B": { - "id": "Qwen/Qwen3-8B", - "name": "Qwen/Qwen3-8B", - "family": "qwen", + "x-ai/grok-code-fast-1": { + "id": "x-ai/grok-code-fast-1", + "name": "Grok Code Fast 1", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-04-30", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-08-26", + "last_updated": "2025-08-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.06, "output": 0.06 }, - "limit": { "context": 131000, "output": 131000 } + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 0.2, + "output": 1.5, + "cache_read": 0.02 + } }, - "Qwen/Qwen3-VL-8B-Instruct": { - "id": "Qwen/Qwen3-VL-8B-Instruct", - "name": "Qwen/Qwen3-VL-8B-Instruct", - "family": "qwen", + "x-ai/grok-4.1-fast-non-reasoning": { + "id": "x-ai/grok-4.1-fast-non-reasoning", + "name": "Grok 4.1 Fast Non Reasoning", "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-10-15", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.18, "output": 0.68 }, - "limit": { "context": 262000, "output": 262000 } - }, - "Qwen/Qwen3-Coder-30B-A3B-Instruct": { - "id": "Qwen/Qwen3-Coder-30B-A3B-Instruct", - "name": "Qwen/Qwen3-Coder-30B-A3B-Instruct", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-08-01", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-11-20", + "last_updated": "2025-11-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.07, "output": 0.28 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 2000000, + "output": 64000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "Qwen/Qwen2.5-VL-32B-Instruct": { - "id": "Qwen/Qwen2.5-VL-32B-Instruct", - "name": "Qwen/Qwen2.5-VL-32B-Instruct", - "family": "qwen", + "x-ai/grok-4": { + "id": "x-ai/grok-4", + "name": "Grok 4", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-03-24", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.27, "output": 0.27 }, - "limit": { "context": 131000, "output": 131000 } + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75 + } }, - "Qwen/Qwen3-VL-30B-A3B-Thinking": { - "id": "Qwen/Qwen3-VL-30B-A3B-Thinking", - "name": "Qwen/Qwen3-VL-30B-A3B-Thinking", - "family": "qwen", + "x-ai/grok-4.1-fast": { + "id": "x-ai/grok-4.1-fast", + "name": "Grok 4.1 Fast", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-11", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-11-20", + "last_updated": "2025-11-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.29, "output": 1 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 2000000, + "output": 64000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "Qwen/Qwen3-Omni-30B-A3B-Captioner": { - "id": "Qwen/Qwen3-Omni-30B-A3B-Captioner", - "name": "Qwen/Qwen3-Omni-30B-A3B-Captioner", - "family": "qwen", + "x-ai/grok-4.2-fast": { + "id": "x-ai/grok-4.2-fast", + "name": "Grok 4.2 Fast", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-04", - "last_updated": "2025-11-25", - "modalities": { "input": ["audio"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-03-20", + "last_updated": "2026-03-20", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 66000, "output": 66000 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 3, + "output": 9 + } }, - "Qwen/Qwen3-VL-8B-Thinking": { - "id": "Qwen/Qwen3-VL-8B-Thinking", - "name": "Qwen/Qwen3-VL-8B-Thinking", - "family": "qwen", + "x-ai/grok-4.2-fast-non-reasoning": { + "id": "x-ai/grok-4.2-fast-non-reasoning", + "name": "Grok 4.2 Fast Non Reasoning", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-15", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-03-20", + "last_updated": "2026-03-20", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.18, "output": 2 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 3, + "output": 9 + } }, - "Qwen/Qwen2.5-VL-72B-Instruct": { - "id": "Qwen/Qwen2.5-VL-72B-Instruct", - "name": "Qwen/Qwen2.5-VL-72B-Instruct", - "family": "qwen", + "openai/gpt-5.3-chat": { + "id": "openai/gpt-5.3-chat", + "name": "GPT-5.3 Chat", "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-01-28", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-03-20", + "last_updated": "2026-03-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.59, "output": 0.59 }, - "limit": { "context": 131000, "output": 4000 } + "limit": { + "context": 128000, + "output": 16380 + }, + "provider": { + "npm": "@ai-sdk/openai", + "api": "https://zenmux.ai/api/v1" + }, + "cost": { + "input": 1.75, + "output": 14 + } }, - "Qwen/Qwen3-Next-80B-A3B-Instruct": { - "id": "Qwen/Qwen3-Next-80B-A3B-Instruct", - "name": "Qwen/Qwen3-Next-80B-A3B-Instruct", - "family": "qwen", - "attachment": false, - "reasoning": false, + "openai/gpt-5.2-pro": { + "id": "openai/gpt-5.2-pro", + "name": "GPT-5.2-Pro", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-09-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.14, "output": 1.4 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai", + "api": "https://zenmux.ai/api/v1" + }, + "cost": { + "input": 21, + "output": 168 + } }, - "Qwen/Qwen3-235B-A22B-Thinking-2507": { - "id": "Qwen/Qwen3-235B-A22B-Thinking-2507", - "name": "Qwen/Qwen3-235B-A22B-Thinking-2507", - "family": "qwen", - "attachment": false, + "openai/gpt-5.3-codex": { + "id": "openai/gpt-5.3-codex", + "name": "GPT-5.3 Codex", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-28", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-03-20", + "last_updated": "2026-03-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.13, "output": 0.6 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai", + "api": "https://zenmux.ai/api/v1" + }, + "cost": { + "input": 1.75, + "output": 14 + } }, - "Qwen/Qwen2.5-72B-Instruct-128K": { - "id": "Qwen/Qwen2.5-72B-Instruct-128K", - "name": "Qwen/Qwen2.5-72B-Instruct-128K", - "family": "qwen", - "attachment": false, - "reasoning": false, + "openai/gpt-5.2": { + "id": "openai/gpt-5.2", + "name": "GPT-5.2", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2024-09-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-01-01", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["image", "text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.59, "output": 0.59 }, - "limit": { "context": 131000, "output": 4000 } + "limit": { + "context": 400000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/openai", + "api": "https://zenmux.ai/api/v1" + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.17 + } }, - "Qwen/Qwen3-14B": { - "id": "Qwen/Qwen3-14B", - "name": "Qwen/Qwen3-14B", - "family": "qwen", - "attachment": false, + "openai/gpt-5.4-mini": { + "id": "openai/gpt-5.4-mini", + "name": "GPT-5.4 Mini", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-04-30", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-03-20", + "last_updated": "2026-03-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.07, "output": 0.28 }, - "limit": { "context": 131000, "output": 131000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai", + "api": "https://zenmux.ai/api/v1" + }, + "cost": { + "input": 0.75, + "output": 4.5 + } }, - "Qwen/Qwen3-32B": { - "id": "Qwen/Qwen3-32B", - "name": "Qwen/Qwen3-32B", - "family": "qwen", - "attachment": false, + "openai/gpt-5.1-chat": { + "id": "openai/gpt-5.1-chat", + "name": "GPT-5.1 Chat", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-04-30", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["pdf", "image", "text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.14, "output": 0.57 }, - "limit": { "context": 131000, "output": 131000 } + "limit": { + "context": 128000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/openai", + "api": "https://zenmux.ai/api/v1" + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.12 + } }, - "Qwen/Qwen2.5-14B-Instruct": { - "id": "Qwen/Qwen2.5-14B-Instruct", - "name": "Qwen/Qwen2.5-14B-Instruct", - "family": "qwen", + "openai/gpt-5.4-nano": { + "id": "openai/gpt-5.4-nano", + "name": "GPT-5.4 Nano", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-09-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-03-20", + "last_updated": "2026-03-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.1 }, - "limit": { "context": 33000, "output": 4000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai", + "api": "https://zenmux.ai/api/v1" + }, + "cost": { + "input": 0.2, + "output": 1.25 + } }, - "Qwen/Qwen3-30B-A3B-Thinking-2507": { - "id": "Qwen/Qwen3-30B-A3B-Thinking-2507", - "name": "Qwen/Qwen3-30B-A3B-Thinking-2507", - "family": "qwen", - "attachment": false, + "openai/gpt-5.2-codex": { + "id": "openai/gpt-5.2-codex", + "name": "GPT-5.2-Codex", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-07-31", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-01-01", + "release_date": "2026-01-15", + "last_updated": "2026-01-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.09, "output": 0.3 }, - "limit": { "context": 262000, "output": 131000 } + "limit": { + "context": 400000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/openai", + "api": "https://zenmux.ai/api/v1" + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.17 + } }, - "Qwen/Qwen3-VL-235B-A22B-Thinking": { - "id": "Qwen/Qwen3-VL-235B-A22B-Thinking", - "name": "Qwen/Qwen3-VL-235B-A22B-Thinking", - "family": "qwen", + "openai/gpt-5.1-codex-mini": { + "id": "openai/gpt-5.1-codex-mini", + "name": "GPT-5.1-Codex-Mini", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-04", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.45, "output": 3.5 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 400000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/openai", + "api": "https://zenmux.ai/api/v1" + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.03 + } }, - "Qwen/Qwen3-VL-30B-A3B-Instruct": { - "id": "Qwen/Qwen3-VL-30B-A3B-Instruct", - "name": "Qwen/Qwen3-VL-30B-A3B-Instruct", - "family": "qwen", + "openai/gpt-5.1": { + "id": "openai/gpt-5.1", + "name": "GPT-5.1", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-05", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["image", "text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.29, "output": 1 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 400000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/openai", + "api": "https://zenmux.ai/api/v1" + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.12 + } }, - "Qwen/Qwen3-Next-80B-A3B-Thinking": { - "id": "Qwen/Qwen3-Next-80B-A3B-Thinking", - "name": "Qwen/Qwen3-Next-80B-A3B-Thinking", - "family": "qwen", - "attachment": false, + "openai/gpt-5.4-pro": { + "id": "openai/gpt-5.4-pro", + "name": "GPT-5.4 Pro", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-09-25", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-03-20", + "last_updated": "2026-03-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.14, "output": 0.57 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 1050000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai", + "api": "https://zenmux.ai/api/v1" + }, + "cost": { + "input": 45, + "output": 225 + } }, - "Qwen/Qwen3-Omni-30B-A3B-Thinking": { - "id": "Qwen/Qwen3-Omni-30B-A3B-Thinking", - "name": "Qwen/Qwen3-Omni-30B-A3B-Thinking", - "family": "qwen", + "openai/gpt-5-codex": { + "id": "openai/gpt-5-codex", + "name": "GPT-5 Codex", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-04", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image", "audio"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-09-23", + "last_updated": "2025-09-23", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 66000, "output": 66000 } + "limit": { + "context": 400000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/openai", + "api": "https://zenmux.ai/api/v1" + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.12 + } }, - "Qwen/Qwen2.5-Coder-32B-Instruct": { - "id": "Qwen/Qwen2.5-Coder-32B-Instruct", - "name": "Qwen/Qwen2.5-Coder-32B-Instruct", - "family": "qwen", - "attachment": false, - "reasoning": false, + "openai/gpt-5.4": { + "id": "openai/gpt-5.4", + "name": "GPT-5.4", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-11-11", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-03-20", + "last_updated": "2026-03-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.18, "output": 0.18 }, - "limit": { "context": 33000, "output": 4000 } + "limit": { + "context": 1050000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai", + "api": "https://zenmux.ai/api/v1" + }, + "cost": { + "input": 3.75, + "output": 18.75 + } }, - "ascend-tribe/pangu-pro-moe": { - "id": "ascend-tribe/pangu-pro-moe", - "name": "ascend-tribe/pangu-pro-moe", - "family": "pangu", - "attachment": false, + "openai/gpt-5": { + "id": "openai/gpt-5", + "name": "GPT-5", + "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": true, + "tool_call": true, "temperature": true, - "release_date": "2025-07-02", - "last_updated": "2026-01-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.6 }, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 400000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/openai", + "api": "https://zenmux.ai/api/v1" + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.12 + } }, - "inclusionAI/Ling-mini-2.0": { - "id": "inclusionAI/Ling-mini-2.0", - "name": "inclusionAI/Ling-mini-2.0", - "family": "ling", - "attachment": false, - "reasoning": false, + "openai/gpt-5.1-codex": { + "id": "openai/gpt-5.1-codex", + "name": "GPT-5.1-Codex", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-09-10", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.07, "output": 0.28 }, - "limit": { "context": 131000, "output": 131000 } + "limit": { + "context": 400000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/openai", + "api": "https://zenmux.ai/api/v1" + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.12 + } }, - "inclusionAI/Ring-flash-2.0": { - "id": "inclusionAI/Ring-flash-2.0", - "name": "inclusionAI/Ring-flash-2.0", - "family": "ring", + "z-ai/glm-4.7-flash-free": { + "id": "z-ai/glm-4.7-flash-free", + "name": "GLM 4.7 Flash (Free)", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-09-29", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2026-01-19", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.14, "output": 0.57 }, - "limit": { "context": 131000, "output": 131000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "inclusionAI/Ling-flash-2.0": { - "id": "inclusionAI/Ling-flash-2.0", - "name": "inclusionAI/Ling-flash-2.0", - "family": "ling", - "attachment": false, - "reasoning": false, + "z-ai/glm-5v-turbo": { + "id": "z-ai/glm-5v-turbo", + "name": "GLM 5V Turbo", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-09-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-01", + "last_updated": "2026-04-01", + "modalities": { + "input": ["text", "image", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.14, "output": 0.57 }, - "limit": { "context": 131000, "output": 131000 } + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 0.726, + "output": 3.1946, + "cache_read": 0.1743 + } }, - "moonshotai/Kimi-K2-Thinking": { - "id": "moonshotai/Kimi-K2-Thinking", - "name": "moonshotai/Kimi-K2-Thinking", - "family": "kimi-thinking", + "z-ai/glm-4.7": { + "id": "z-ai/glm-4.7", + "name": "GLM 4.7", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-11-07", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.55, "output": 2.5 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 0.28, + "output": 1.14, + "cache_read": 0.06 + } }, - "moonshotai/Kimi-K2-Instruct-0905": { - "id": "moonshotai/Kimi-K2-Instruct-0905", - "name": "moonshotai/Kimi-K2-Instruct-0905", - "family": "kimi", + "z-ai/glm-5": { + "id": "z-ai/glm-5", + "name": "GLM 5", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-09-08", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 262000, "output": 262000 } + "knowledge": "2025-01-01", + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 0.58, + "output": 2.6, + "cache_read": 0.14 + } }, - "tencent/Hunyuan-A13B-Instruct": { - "id": "tencent/Hunyuan-A13B-Instruct", - "name": "tencent/Hunyuan-A13B-Instruct", - "family": "hunyuan", + "z-ai/glm-4.7-flashx": { + "id": "z-ai/glm-4.7-flashx", + "name": "GLM 4.7 FlashX", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-06-30", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2026-01-19", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.14, "output": 0.57 }, - "limit": { "context": 131000, "output": 131000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 0.07, + "output": 0.42, + "cache_read": 0.01 + } }, - "tencent/Hunyuan-MT-7B": { - "id": "tencent/Hunyuan-MT-7B", - "name": "tencent/Hunyuan-MT-7B", - "family": "hunyuan", - "attachment": false, - "reasoning": false, + "z-ai/glm-4.6v-flash-free": { + "id": "z-ai/glm-4.6v-flash-free", + "name": "GLM 4.6V Flash (Free)", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-09-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-12-08", + "last_updated": "2025-12-08", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 33000, "output": 33000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "Kwaipilot/KAT-Dev": { - "id": "Kwaipilot/KAT-Dev", - "name": "Kwaipilot/KAT-Dev", - "family": "kat-coder", + "z-ai/glm-5.1": { + "id": "z-ai/glm-5.1", + "name": "GLM-5.1", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "release_date": "2025-09-27", - "last_updated": "2026-01-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-03", + "last_updated": "2026-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.6 }, - "limit": { "context": 128000, "output": 128000 } - } - } - }, - "evroc": { - "id": "evroc", - "env": ["EVROC_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://models.think.evroc.com/v1", - "name": "evroc", - "doc": "https://docs.evroc.com/products/think/overview.html", - "models": { - "openai/gpt-oss-120b": { - "id": "openai/gpt-oss-120b", - "name": "GPT OSS 120B", - "family": "gpt-oss", - "attachment": false, - "reasoning": true, - "tool_call": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.24, "output": 0.94 }, - "limit": { "context": 65536, "output": 65536 } - }, - "openai/whisper-large-v3": { - "id": "openai/whisper-large-v3", - "name": "Whisper 3 Large", - "family": "whisper", - "attachment": false, - "reasoning": false, - "tool_call": false, - "release_date": "2024-10-01", - "last_updated": "2024-10-01", - "modalities": { "input": ["audio"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.00236, "output": 0.00236, "output_audio": 2.36 }, - "limit": { "context": 448, "output": 4096 } - }, - "microsoft/Phi-4-multimodal-instruct": { - "id": "microsoft/Phi-4-multimodal-instruct", - "name": "Phi-4 15B", - "family": "phi", - "attachment": false, - "reasoning": false, - "tool_call": false, - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.24, "output": 0.47 }, - "limit": { "context": 32000, "output": 32000 } - }, - "nvidia/Llama-3.3-70B-Instruct-FP8": { - "id": "nvidia/Llama-3.3-70B-Instruct-FP8", - "name": "Llama 3.3 70B", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": false, - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1.18, "output": 1.18 }, - "limit": { "context": 131072, "output": 32768 } - }, - "intfloat/multilingual-e5-large-instruct": { - "id": "intfloat/multilingual-e5-large-instruct", - "name": "E5 Multi-Lingual Large Embeddings 0.6B", - "family": "text-embedding", - "attachment": false, - "reasoning": false, - "tool_call": false, - "release_date": "2024-06-01", - "last_updated": "2024-06-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.12, "output": 0.12 }, - "limit": { "context": 512, "output": 512 } - }, - "KBLab/kb-whisper-large": { - "id": "KBLab/kb-whisper-large", - "name": "KB Whisper", - "family": "whisper", - "attachment": false, - "reasoning": false, - "tool_call": false, - "release_date": "2024-10-01", - "last_updated": "2024-10-01", - "modalities": { "input": ["audio"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.00236, "output": 0.00236, "output_audio": 2.36 }, - "limit": { "context": 448, "output": 448 } - }, - "Qwen/Qwen3-VL-30B-A3B-Instruct": { - "id": "Qwen/Qwen3-VL-30B-A3B-Instruct", - "name": "Qwen3 VL 30B", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, - "release_date": "2025-07-30", - "last_updated": "2025-07-30", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.24, "output": 0.94 }, - "limit": { "context": 100000, "output": 100000 } - }, - "Qwen/Qwen3-30B-A3B-Instruct-2507-FP8": { - "id": "Qwen/Qwen3-30B-A3B-Instruct-2507-FP8", - "name": "Qwen3 30B 2507", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, - "release_date": "2025-07-30", - "last_updated": "2025-07-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.35, "output": 1.42 }, - "limit": { "context": 64000, "output": 64000 } - }, - "Qwen/Qwen3-Embedding-8B": { - "id": "Qwen/Qwen3-Embedding-8B", - "name": "Qwen3 Embedding 8B", - "family": "text-embedding", - "attachment": false, - "reasoning": false, - "tool_call": false, - "release_date": "2025-07-30", - "last_updated": "2025-07-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.12, "output": 0.12 }, - "limit": { "context": 40960, "output": 40960 } + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 0.8781, + "output": 3.5126, + "cache_read": 0.1903 + } }, - "moonshotai/Kimi-K2.5": { - "id": "moonshotai/Kimi-K2.5", - "name": "Kimi K2.5", - "family": "kimi", - "attachment": false, + "z-ai/glm-4.6v-flash": { + "id": "z-ai/glm-4.6v-flash", + "name": "GLM 4.6V FlashX", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1.47, "output": 5.9 }, - "limit": { "context": 262144, "output": 262144 } - }, - "mistralai/devstral-small-2-24b-instruct-2512": { - "id": "mistralai/devstral-small-2-24b-instruct-2512", - "name": "Devstral Small 2 24B Instruct 2512", - "family": "devstral", - "attachment": false, - "reasoning": false, - "tool_call": true, - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.12, "output": 0.47 }, - "limit": { "context": 32768, "output": 32768 } - }, - "mistralai/Magistral-Small-2509": { - "id": "mistralai/Magistral-Small-2509", - "name": "Magistral Small 1.2 24B", - "family": "magistral-small", - "attachment": false, - "reasoning": false, - "tool_call": false, - "release_date": "2025-06-01", - "last_updated": "2025-06-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.59, "output": 2.36 }, - "limit": { "context": 131072, "output": 131072 } + "temperature": true, + "knowledge": "2025-01-01", + "release_date": "2025-12-08", + "last_updated": "2025-12-08", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 0.02, + "output": 0.21, + "cache_read": 0.0043 + } }, - "mistralai/Voxtral-Small-24B-2507": { - "id": "mistralai/Voxtral-Small-24B-2507", - "name": "Voxtral Small 24B", - "family": "voxtral", + "z-ai/glm-4.5": { + "id": "z-ai/glm-4.5", + "name": "GLM 4.5", "attachment": false, - "reasoning": false, - "tool_call": false, - "release_date": "2025-03-01", - "last_updated": "2025-03-01", - "modalities": { "input": ["audio", "text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.00236, "output": 0.00236, "output_audio": 2.36 }, - "limit": { "context": 32000, "output": 32000 } - } - } - }, - "kilo": { - "id": "kilo", - "env": ["KILO_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.kilo.ai/api/gateway", - "name": "Kilo Gateway", - "doc": "https://kilo.ai", - "models": { - "giga-potato": { - "id": "giga-potato", - "name": "Giga Potato (free)", - "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-08-27", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-07-25", + "last_updated": "2025-07-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 32000 } + "limit": { + "context": 128000, + "output": 64000 + }, + "cost": { + "input": 0.35, + "output": 1.54, + "cache_read": 0.07 + } }, - "corethink:free": { - "id": "corethink:free", - "name": "CoreThink (free)", + "z-ai/glm-4.5-air": { + "id": "z-ai/glm-4.5-air", + "name": "GLM 4.5 Air", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-08-27", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-07-25", + "last_updated": "2025-07-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 78000, "output": 8192 } + "limit": { + "context": 128000, + "output": 64000 + }, + "cost": { + "input": 0.11, + "output": 0.56, + "cache_read": 0.02 + } }, - "giga-potato-thinking": { - "id": "giga-potato-thinking", - "name": "Giga Potato Thinking (free)", + "z-ai/glm-5-turbo": { + "id": "z-ai/glm-5-turbo", + "name": "GLM 5 Turbo", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-08-27", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2026-03-20", + "last_updated": "2026-03-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 32000 } + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 0.88, + "output": 3.48 + } }, - "morph-warp-grep-v2": { - "id": "morph-warp-grep-v2", - "name": "Morph: WarpGrep V2", + "z-ai/glm-4.6": { + "id": "z-ai/glm-4.6", + "name": "GLM 4.6", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-08-27", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 32000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 0.35, + "output": 1.54, + "cache_read": 0.07 + } }, - "eleutherai/llemma_7b": { - "id": "eleutherai/llemma_7b", - "name": "EleutherAI: Llemma 7b", - "attachment": false, - "reasoning": false, - "tool_call": false, + "z-ai/glm-4.6v": { + "id": "z-ai/glm-4.6v", + "name": "GLM 4.6V", + "attachment": true, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-04-14", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.8, "output": 1.2 }, - "limit": { "context": 4096, "output": 4096 } + "knowledge": "2025-01-01", + "release_date": "2025-12-08", + "last_updated": "2025-12-08", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 0.14, + "output": 0.42, + "cache_read": 0.03 + } }, - "meituan/longcat-flash-chat": { - "id": "meituan/longcat-flash-chat", - "name": "Meituan: LongCat Flash Chat", - "attachment": false, + "volcengine/doubao-seed-2.0-code": { + "id": "volcengine/doubao-seed-2.0-code", + "name": "Doubao Seed 2.0 Code", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2025-08-30", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2026-03-20", + "last_updated": "2026-03-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.8, "cache_read": 0.2 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 256000, + "output": 32000 + }, + "cost": { + "input": 0.9, + "output": 4.48 + } }, - "openai/gpt-5.2-codex": { - "id": "openai/gpt-5.2-codex", - "name": "OpenAI: GPT-5.2-Codex", + "volcengine/doubao-seed-code": { + "id": "volcengine/doubao-seed-code", + "name": "Doubao-Seed-Code", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2026-01-14", - "last_updated": "2026-01-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "output": 128000 } - }, - "openai/o1-pro": { - "id": "openai/o1-pro", - "name": "OpenAI: o1-pro", - "attachment": true, - "reasoning": true, - "tool_call": false, - "temperature": false, - "release_date": "2025-03-19", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01-01", + "release_date": "2025-11-11", + "last_updated": "2025-11-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 150, "output": 600 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 0.17, + "output": 1.12, + "cache_read": 0.03 + } }, - "openai/gpt-5.1-codex-mini": { - "id": "openai/gpt-5.1-codex-mini", - "name": "OpenAI: GPT-5.1-Codex-Mini", + "volcengine/doubao-seed-2.0-mini": { + "id": "volcengine/doubao-seed-2.0-mini", + "name": "Doubao-Seed-2.0-mini", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2026-02-14", + "release_date": "2026-02-14", + "last_updated": "2026-02-14", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.025 }, - "limit": { "context": 400000, "output": 100000 } + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 0.03, + "output": 0.28, + "cache_read": 0.01, + "cache_write": 0.0024 + } }, - "openai/gpt-5.4-pro": { - "id": "openai/gpt-5.4-pro", - "name": "OpenAI: GPT-5.4 Pro", + "volcengine/doubao-seed-2.0-lite": { + "id": "volcengine/doubao-seed-2.0-lite", + "name": "Doubao-Seed-2.0-lite", "attachment": true, "reasoning": true, "tool_call": true, - "release_date": "2026-03-06", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2026-02-14", + "release_date": "2026-02-14", + "last_updated": "2026-02-14", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 30, "output": 180 }, - "limit": { "context": 1050000, "output": 128000 } + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 0.09, + "output": 0.51, + "cache_read": 0.02, + "cache_write": 0.0024 + } }, - "openai/gpt-3.5-turbo-16k": { - "id": "openai/gpt-3.5-turbo-16k", - "name": "OpenAI: GPT-3.5 Turbo 16k", - "attachment": false, - "reasoning": false, + "volcengine/doubao-seed-1.8": { + "id": "volcengine/doubao-seed-1.8", + "name": "Doubao-Seed-1.8", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2023-08-28", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-12-18", + "last_updated": "2025-12-18", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 4 }, - "limit": { "context": 16385, "output": 4096 } + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 0.11, + "output": 0.28, + "cache_read": 0.02, + "cache_write": 0.0024 + } }, - "openai/gpt-4o:extended": { - "id": "openai/gpt-4o:extended", - "name": "OpenAI: GPT-4o (extended)", + "volcengine/doubao-seed-2.0-pro": { + "id": "volcengine/doubao-seed-2.0-pro", + "name": "Doubao-Seed-2.0-pro", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-05-13", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "knowledge": "2026-02-14", + "release_date": "2026-02-14", + "last_updated": "2026-02-14", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 6, "output": 18 }, - "limit": { "context": 128000, "output": 64000 } + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 0.45, + "output": 2.24, + "cache_read": 0.09, + "cache_write": 0.0024 + } }, - "openai/o3-mini": { - "id": "openai/o3-mini", - "name": "OpenAI: o3 Mini", + "baidu/ernie-5.0-thinking-preview": { + "id": "baidu/ernie-5.0-thinking-preview", + "name": "ERNIE 5.0", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2024-12-20", - "last_updated": "2026-03-15", - "modalities": { "input": ["pdf", "text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01-01", + "release_date": "2026-01-22", + "last_updated": "2026-01-22", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.55 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 128000, + "output": 64000 + }, + "cost": { + "input": 0.84, + "output": 3.37 + } }, - "openai/gpt-4-1106-preview": { - "id": "openai/gpt-4-1106-preview", - "name": "OpenAI: GPT-4 Turbo (older v1106)", - "attachment": false, - "reasoning": false, + "minimax/minimax-m2.7": { + "id": "minimax/minimax-m2.7", + "name": "MiniMax M2.7", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2023-11-06", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2026-03-20", + "last_updated": "2026-03-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 10, "output": 30 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 204800, + "output": 131070 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://zenmux.ai/api/anthropic/v1" + }, + "cost": { + "input": 0.3055, + "output": 1.2219 + } }, - "openai/gpt-5-pro": { - "id": "openai/gpt-5-pro", - "name": "OpenAI: GPT-5 Pro", + "minimax/minimax-m2.7-highspeed": { + "id": "minimax/minimax-m2.7-highspeed", + "name": "MiniMax M2.7 highspeed", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-10-06", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01-01", + "release_date": "2026-03-20", + "last_updated": "2026-03-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 120 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 204800, + "output": 131070 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://zenmux.ai/api/anthropic/v1" + }, + "cost": { + "input": 0.611, + "output": 2.4439 + } }, - "openai/gpt-3.5-turbo-0613": { - "id": "openai/gpt-3.5-turbo-0613", - "name": "OpenAI: GPT-3.5 Turbo (older v0613)", + "minimax/minimax-m2": { + "id": "minimax/minimax-m2", + "name": "MiniMax M2", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2023-06-13", - "last_updated": "2023-06-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-10-27", + "last_updated": "2025-10-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 2 }, - "limit": { "context": 4095, "output": 4096 } + "limit": { + "context": 204000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://zenmux.ai/api/anthropic/v1" + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03, + "cache_write": 0.38 + } }, - "openai/gpt-5-image-mini": { - "id": "openai/gpt-5-image-mini", - "name": "OpenAI: GPT-5 Image Mini", - "attachment": true, + "minimax/minimax-m2.1": { + "id": "minimax/minimax-m2.1", + "name": "MiniMax M2.1", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-10-16", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["image", "text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 2 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 204000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://zenmux.ai/api/anthropic/v1" + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03, + "cache_write": 0.38 + } }, - "openai/gpt-5": { - "id": "openai/gpt-5", - "name": "OpenAI: GPT-5", - "attachment": true, + "minimax/minimax-m2.5-lightning": { + "id": "minimax/minimax-m2.5-lightning", + "name": "MiniMax M2.5 highspeed", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-08-07", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01-01", + "release_date": "2026-02-13", + "last_updated": "2026-02-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 204800, + "output": 131072 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://zenmux.ai/api/anthropic/v1" + }, + "cost": { + "input": 0.6, + "output": 4.8, + "cache_read": 0.06, + "cache_write": 0.75 + } }, - "openai/gpt-4-0314": { - "id": "openai/gpt-4-0314", - "name": "OpenAI: GPT-4 (older v0314)", + "minimax/minimax-m2.5": { + "id": "minimax/minimax-m2.5", + "name": "MiniMax M2.5", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2023-05-28", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2026-02-13", + "last_updated": "2026-02-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 30, "output": 60 }, - "limit": { "context": 8191, "output": 4096 } + "limit": { + "context": 204800, + "output": 131072 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://zenmux.ai/api/anthropic/v1" + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03, + "cache_write": 0.375 + } }, - "openai/gpt-audio": { - "id": "openai/gpt-audio", - "name": "OpenAI: GPT Audio", + "qwen/qwen3-coder-plus": { + "id": "qwen/qwen3-coder-plus", + "name": "Qwen3-Coder-Plus", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2026-01-20", - "last_updated": "2026-03-15", - "modalities": { "input": ["audio", "text"], "output": ["audio", "text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } }, - "openai/gpt-4-turbo": { - "id": "openai/gpt-4-turbo", - "name": "OpenAI: GPT-4 Turbo", + "qwen/qwen3.5-flash": { + "id": "qwen/qwen3.5-flash", + "name": "Qwen3.5 Flash", "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2023-09-13", - "last_updated": "2024-04-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2026-03-20", + "last_updated": "2026-03-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 10, "output": 30 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 1020000, + "output": 1020000 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } }, - "openai/gpt-4o": { - "id": "openai/gpt-4o", - "name": "OpenAI: GPT-4o", - "attachment": true, - "reasoning": false, + "qwen/qwen3.6-plus": { + "id": "qwen/qwen3.6-plus", + "name": "Qwen3.6-Plus", + "attachment": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-05-13", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "release_date": "2026-03-30", + "last_updated": "2026-03-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 10, "cache_read": 1.25 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.05, + "cache_write": 0.625, + "tiers": [ + { + "input": 2, + "output": 6, + "cache_read": 0.2, + "cache_write": 2.5, + "tier": { + "type": "context", + "size": 256000 + } + } + ], + "context_over_200k": { + "input": 2, + "output": 6, + "cache_read": 0.2, + "cache_write": 2.5 + } + } }, - "openai/gpt-5.3-codex": { - "id": "openai/gpt-5.3-codex", - "name": "OpenAI: GPT-5.3-Codex", - "attachment": true, + "qwen/qwen3-max": { + "id": "qwen/qwen3-max", + "name": "Qwen3-Max-Thinking", + "attachment": false, "reasoning": true, "tool_call": true, - "release_date": "2026-02-25", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01-01", + "release_date": "2026-01-23", + "last_updated": "2026-01-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 1.2, + "output": 6 + } }, - "openai/o3-mini-high": { - "id": "openai/o3-mini-high", - "name": "OpenAI: o3 Mini High", + "qwen/qwen3.5-plus": { + "id": "qwen/qwen3.5-plus", + "name": "Qwen3.5 Plus", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-01-31", - "last_updated": "2026-03-15", - "modalities": { "input": ["pdf", "text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01-01", + "release_date": "2026-03-20", + "last_updated": "2026-03-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.55 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 0.8, + "output": 4.8 + } }, - "openai/gpt-5-mini": { - "id": "openai/gpt-5-mini", - "name": "OpenAI: GPT-5 Mini", + "google/gemini-3.1-flash-lite-preview": { + "id": "google/gemini-3.1-flash-lite-preview", + "name": "Gemini 3.1 Flash Lite Preview", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "temperature": false, - "release_date": "2025-08-07", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "temperature": true, + "release_date": "2025-03-20", + "last_updated": "2025-03-20", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.025 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 1050000, + "output": 65530 + }, + "cost": { + "input": 0.25, + "output": 1.5 + } }, - "openai/gpt-4-turbo-preview": { - "id": "openai/gpt-4-turbo-preview", - "name": "OpenAI: GPT-4 Turbo Preview", - "attachment": false, - "reasoning": false, + "google/gemini-3.1-pro-preview": { + "id": "google/gemini-3.1-pro-preview", + "name": "Gemini 3.1 Pro Preview", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-01-25", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2026-02-19", + "release_date": "2026-02-19", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text", "image", "pdf", "audio", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 10, "output": 30 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 1048000, + "output": 64000 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2, + "cache_write": 4.5 + } }, - "openai/gpt-4o-mini": { - "id": "openai/gpt-4o-mini", - "name": "OpenAI: GPT-4o-mini", + "google/gemini-3-flash-preview": { + "id": "google/gemini-3-flash-preview", + "name": "Gemini 3 Flash Preview", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-07-18", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text", "image", "pdf", "audio"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.6, "cache_read": 0.075 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 1048000, + "output": 64000 + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.05, + "cache_write": 1 + } }, - "openai/gpt-5.1-codex-max": { - "id": "openai/gpt-5.1-codex-max", - "name": "OpenAI: GPT-5.1-Codex-Max", + "google/gemini-2.5-pro": { + "id": "google/gemini-2.5-pro", + "name": "Gemini 2.5 Pro", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01-01", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["pdf", "image", "text", "audio", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 1048000, + "output": 64000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.31, + "cache_write": 4.5 + } }, - "openai/gpt-4.1": { - "id": "openai/gpt-4.1", - "name": "OpenAI: GPT-4.1", + "google/gemini-2.5-flash": { + "id": "google/gemini-2.5-flash", + "name": "Gemini 2.5 Flash", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-04-14", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["pdf", "image", "text", "audio"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 1048000, + "output": 64000 + }, + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.07, + "cache_write": 1 + } }, - "openai/gpt-3.5-turbo": { - "id": "openai/gpt-3.5-turbo", - "name": "OpenAI: GPT-3.5 Turbo", - "attachment": false, + "google/gemini-2.5-flash-lite": { + "id": "google/gemini-2.5-flash-lite", + "name": "Gemini 2.5 Flash Lite", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2023-03-01", - "last_updated": "2023-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-07-22", + "last_updated": "2025-07-22", + "modalities": { + "input": ["pdf", "image", "text", "audio"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 1.5 }, - "limit": { "context": 16385, "output": 4096 } + "limit": { + "context": 1048000, + "output": 64000 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.03, + "cache_write": 1 + } }, - "openai/gpt-3.5-turbo-instruct": { - "id": "openai/gpt-3.5-turbo-instruct", - "name": "OpenAI: GPT-3.5 Turbo Instruct", - "attachment": false, + "sapiens-ai/agnes-1.5-lite": { + "id": "sapiens-ai/agnes-1.5-lite", + "name": "Agnes 1.5 Lite", + "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2023-03-01", - "last_updated": "2023-09-21", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-26", + "last_updated": "2026-03-26", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.5, "output": 2 }, - "limit": { "context": 4095, "output": 4096 } + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.12, + "output": 0.6 + } }, - "openai/gpt-oss-120b": { - "id": "openai/gpt-oss-120b", - "name": "OpenAI: gpt-oss-120b", + "sapiens-ai/agnes-1.5-pro": { + "id": "sapiens-ai/agnes-1.5-pro", + "name": "Agnes 1.5 Pro", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.039, "output": 0.19 }, - "limit": { "context": 131072, "output": 26215 } - }, - "openai/gpt-5.1-chat": { - "id": "openai/gpt-5.1-chat", - "name": "OpenAI: GPT-5.1 Chat", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": false, - "release_date": "2025-11-13", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "release_date": "2026-03-21", + "last_updated": "2026-03-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.16, + "output": 0.8 + } }, - "openai/gpt-5.4": { - "id": "openai/gpt-5.4", - "name": "OpenAI: GPT-5.4", + "moonshotai/kimi-k2.5": { + "id": "moonshotai/kimi-k2.5", + "name": "Kimi K2.5", "attachment": true, "reasoning": true, "tool_call": true, - "release_date": "2026-03-06", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 15 }, - "limit": { "context": 1050000, "output": 128000 } - }, - "openai/o1": { - "id": "openai/o1", - "name": "OpenAI: o1", - "attachment": true, - "reasoning": false, - "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": false, - "release_date": "2024-12-05", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 60, "cache_read": 7.5 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 262000, + "output": 64000 + }, + "cost": { + "input": 0.58, + "output": 3.02, + "cache_read": 0.1 + } }, - "openai/gpt-4o-audio-preview": { - "id": "openai/gpt-4o-audio-preview", - "name": "OpenAI: GPT-4o Audio", + "moonshotai/kimi-k2-thinking-turbo": { + "id": "moonshotai/kimi-k2-thinking-turbo", + "name": "Kimi K2 Thinking Turbo", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-08-15", - "last_updated": "2026-03-15", - "modalities": { "input": ["audio", "text"], "output": ["audio", "text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-11-06", + "last_updated": "2025-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 262000, + "output": 64000 + }, + "cost": { + "input": 1.15, + "output": 8, + "cache_read": 0.15 + } }, - "openai/o3": { - "id": "openai/o3", - "name": "OpenAI: o3", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-04-16", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 200000, "output": 100000 } - }, - "openai/gpt-5-chat": { - "id": "openai/gpt-5-chat", - "name": "OpenAI: GPT-5 Chat", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2025-08-07", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 128000, "output": 16384 } - }, - "openai/gpt-4o-search-preview": { - "id": "openai/gpt-4o-search-preview", - "name": "OpenAI: GPT-4o Search Preview", + "moonshotai/kimi-k2-0905": { + "id": "moonshotai/kimi-k2-0905", + "name": "Kimi K2 0905", "attachment": false, "reasoning": false, - "tool_call": false, - "release_date": "2025-03-13", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01-01", + "release_date": "2025-09-04", + "last_updated": "2025-09-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 262000, + "output": 64000 + }, + "cost": { + "input": 0.6, + "output": 2.5, + "cache_read": 0.15 + } }, - "openai/o4-mini-high": { - "id": "openai/o4-mini-high", - "name": "OpenAI: o4 Mini High", + "moonshotai/kimi-k2.6": { + "id": "moonshotai/kimi-k2.6", + "name": "Kimi K2.6", "attachment": true, "reasoning": true, "tool_call": true, - "release_date": "2025-04-17", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.1, "output": 4.4 }, - "limit": { "context": 200000, "output": 100000 } + "interleaved": { + "field": "reasoning_content" + }, + "temperature": false, + "knowledge": "2025-01-01", + "release_date": "2026-04-20", + "last_updated": "2026-04-20", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262140, + "output": 262140 + }, + "cost": { + "input": 0.95, + "output": 4, + "cache_read": 0.16 + } }, - "openai/gpt-4o-2024-05-13": { - "id": "openai/gpt-4o-2024-05-13", - "name": "OpenAI: GPT-4o (2024-05-13)", - "attachment": true, - "reasoning": false, + "moonshotai/kimi-k2-thinking": { + "id": "moonshotai/kimi-k2-thinking", + "name": "Kimi K2 Thinking", + "attachment": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-05-13", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-11-06", + "last_updated": "2025-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 15 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 262000, + "output": 64000 + }, + "cost": { + "input": 0.6, + "output": 2.5, + "cache_read": 0.15 + } }, - "openai/gpt-4": { - "id": "openai/gpt-4", - "name": "OpenAI: GPT-4", - "attachment": false, - "reasoning": false, + "anthropic/claude-opus-4.1": { + "id": "anthropic/claude-opus-4.1", + "name": "Claude Opus 4.1", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2023-03-14", - "last_updated": "2024-04-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["image", "text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 30, "output": 60 }, - "limit": { "context": 8191, "output": 4096 } + "limit": { + "context": 200000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://zenmux.ai/api/anthropic/v1" + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "openai/gpt-5.3-chat": { - "id": "openai/gpt-5.3-chat", - "name": "OpenAI: GPT-5.3 Chat", + "anthropic/claude-3.7-sonnet": { + "id": "anthropic/claude-3.7-sonnet", + "name": "Claude 3.7 Sonnet", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "release_date": "2026-03-04", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01-01", + "release_date": "2025-02-24", + "last_updated": "2025-02-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 200000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://zenmux.ai/api/anthropic/v1" + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "openai/gpt-4o-2024-11-20": { - "id": "openai/gpt-4o-2024-11-20", - "name": "OpenAI: GPT-4o (2024-11-20)", + "anthropic/claude-opus-4.6": { + "id": "anthropic/claude-opus-4.6", + "name": "Claude Opus 4.6", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-11-20", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "knowledge": "2025-05-31", + "release_date": "2026-02-06", + "last_updated": "2026-02-06", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 10, "cache_read": 1.25 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://zenmux.ai/api/anthropic/v1" + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "openai/gpt-5.2-chat": { - "id": "openai/gpt-5.2-chat", - "name": "OpenAI: GPT-5.2 Chat", + "anthropic/claude-opus-4.7": { + "id": "anthropic/claude-opus-4.7", + "name": "Claude Opus 4.7", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": false, - "release_date": "2025-12-11", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://zenmux.ai/api/anthropic/v1" + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "openai/gpt-5.2": { - "id": "openai/gpt-5.2", - "name": "OpenAI: GPT-5.2", + "anthropic/claude-sonnet-4": { + "id": "anthropic/claude-sonnet-4", + "name": "Claude Sonnet 4", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-12-11", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01-01", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["image", "text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://zenmux.ai/api/anthropic/v1" + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "openai/o4-mini-deep-research": { - "id": "openai/o4-mini-deep-research", - "name": "OpenAI: o4 Mini Deep Research", + "anthropic/claude-sonnet-4.5": { + "id": "anthropic/claude-sonnet-4.5", + "name": "Claude Sonnet 4.5", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-06-26", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://zenmux.ai/api/anthropic/v1" + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "openai/gpt-audio-mini": { - "id": "openai/gpt-audio-mini", - "name": "OpenAI: GPT Audio Mini", - "attachment": false, - "reasoning": false, - "tool_call": false, + "anthropic/claude-opus-4.5": { + "id": "anthropic/claude-opus-4.5", + "name": "Claude Opus 4.5", + "attachment": true, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2026-01-20", - "last_updated": "2026-03-15", - "modalities": { "input": ["audio", "text"], "output": ["audio", "text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-11-24", + "last_updated": "2025-11-24", + "modalities": { + "input": ["pdf", "image", "text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 2.4 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 200000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://zenmux.ai/api/anthropic/v1" + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "openai/gpt-5.1": { - "id": "openai/gpt-5.1", - "name": "OpenAI: GPT-5.1", + "anthropic/claude-opus-4": { + "id": "anthropic/claude-opus-4", + "name": "Claude Opus 4", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-11-13", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } - }, - "openai/gpt-4o-mini-search-preview": { - "id": "openai/gpt-4o-mini-search-preview", - "name": "OpenAI: GPT-4o-mini Search Preview", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2025-01", - "last_updated": "2025-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01-01", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["image", "text", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 200000, + "output": 32000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://zenmux.ai/api/anthropic/v1" + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "openai/gpt-4.1-mini": { - "id": "openai/gpt-4.1-mini", - "name": "OpenAI: GPT-4.1 Mini", + "anthropic/claude-3.5-haiku": { + "id": "anthropic/claude-3.5-haiku", + "name": "Claude 3.5 Haiku", "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2025-04-14", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2024-11-04", + "last_updated": "2024-11-04", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 1.6, "cache_read": 0.1 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 200000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://zenmux.ai/api/anthropic/v1" + }, + "cost": { + "input": 0.8, + "output": 4, + "cache_read": 0.08, + "cache_write": 1 + } }, - "openai/gpt-4o-mini-2024-07-18": { - "id": "openai/gpt-4o-mini-2024-07-18", - "name": "OpenAI: GPT-4o-mini (2024-07-18)", + "anthropic/claude-haiku-4.5": { + "id": "anthropic/claude-haiku-4.5", + "name": "Claude Haiku 4.5", "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2024-07-18", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 200000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://zenmux.ai/api/anthropic/v1" + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } }, - "openai/gpt-5-nano": { - "id": "openai/gpt-5-nano", - "name": "OpenAI: GPT-5 Nano", + "anthropic/claude-sonnet-4.6": { + "id": "anthropic/claude-sonnet-4.6", + "name": "Claude Sonnet 4.6", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-08-07", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-08-31", + "release_date": "2026-02-18", + "last_updated": "2026-02-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.05, "output": 0.4, "cache_read": 0.005 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://zenmux.ai/api/anthropic/v1" + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "openai/gpt-oss-safeguard-20b": { - "id": "openai/gpt-oss-safeguard-20b", - "name": "OpenAI: gpt-oss-safeguard-20b", + "deepseek/deepseek-v4-flash": { + "id": "deepseek/deepseek-v4-flash", + "name": "DeepSeek V4 Flash", + "family": "deepseek-flash", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-10-29", - "last_updated": "2025-10-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.075, "output": 0.3, "cache_read": 0.037 }, - "limit": { "context": 131072, "output": 65536 } - }, - "openai/o3-pro": { - "id": "openai/o3-pro", - "name": "OpenAI: o3 Pro", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "release_date": "2025-04-16", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 20, "output": 80 }, - "limit": { "context": 200000, "output": 100000 } + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 0.14, + "output": 0.28, + "cache_read": 0.028 + } }, - "openai/gpt-oss-20b": { - "id": "openai/gpt-oss-20b", - "name": "OpenAI: gpt-oss-20b", + "deepseek/deepseek-v4-pro": { + "id": "deepseek/deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.03, "output": 0.14 }, - "limit": { "context": 131072, "output": 26215 } - }, - "openai/gpt-4o-2024-08-06": { - "id": "openai/gpt-4o-2024-08-06", - "name": "OpenAI: GPT-4o (2024-08-06)", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2024-08-06", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 10, "cache_read": 1.25 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 1.74, + "output": 3.48, + "cache_read": 0.145 + } }, - "openai/gpt-4.1-nano": { - "id": "openai/gpt-4.1-nano", - "name": "OpenAI: GPT-4.1 Nano", - "attachment": true, - "reasoning": false, + "tencent/hy3-preview": { + "id": "tencent/hy3-preview", + "name": "Hy3 preview", + "family": "Hy", + "attachment": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-04-14", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.025 }, - "limit": { "context": 1047576, "output": 32768 } + "release_date": "2026-04-20", + "last_updated": "2026-04-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 0.172, + "output": 0.572, + "cache_read": 0.058, + "cache_write": 0 + } }, - "openai/gpt-5-image": { - "id": "openai/gpt-5-image", - "name": "OpenAI: GPT-5 Image", + "openai/gpt-5.5": { + "id": "openai/gpt-5.5", + "name": "GPT-5.5", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2025-10-14", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["image", "text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-12-01", + "release_date": "2026-04-23", + "last_updated": "2026-04-23", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 10, "output": 10 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "experimental": { + "modes": { + "fast": { + "cost": { + "input": 12.5, + "output": 75, + "cache_read": 1.25 + }, + "provider": { + "body": { + "service_tier": "priority" + } + } + } + } + }, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5, + "context_over_200k": { + "input": 10, + "output": 45, + "cache_read": 1 + }, + "tiers": [ + { + "input": 10, + "output": 45, + "cache_read": 1, + "tier": { + "type": "context", + "size": 272000 + } + } + ] + } }, - "openai/gpt-5-codex": { - "id": "openai/gpt-5-codex", - "name": "OpenAI: GPT-5 Codex", + "openai/gpt-5.5-pro": { + "id": "openai/gpt-5.5-pro", + "name": "GPT-5.5 Pro", + "family": "gpt-pro", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": false, - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-12-01", + "release_date": "2026-04-23", + "last_updated": "2026-04-23", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 30, + "output": 180, + "context_over_200k": { + "input": 60, + "output": 270 + }, + "tiers": [ + { + "input": 60, + "output": 270, + "tier": { + "type": "context", + "size": 272000 + } + } + ] + } }, - "openai/o4-mini": { - "id": "openai/o4-mini", - "name": "OpenAI: o4 Mini", - "attachment": true, + "xiaomi/mimo-v2.5-pro": { + "id": "xiaomi/mimo-v2.5-pro", + "name": "MiMo-V2.5-Pro", + "family": "mimo", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-04-16", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.275 }, - "limit": { "context": 200000, "output": 100000 } + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.2, + "context_over_200k": { + "input": 2, + "output": 6, + "cache_read": 0.4 + }, + "tiers": [ + { + "input": 2, + "output": 6, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 256000 + } + } + ] + } }, - "openai/o3-deep-research": { - "id": "openai/o3-deep-research", - "name": "OpenAI: o3 Deep Research", + "xiaomi/mimo-v2-omni": { + "id": "xiaomi/mimo-v2-omni", + "name": "MiMo V2 Omni", + "family": "mimo", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-06-26", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 10, "output": 40, "cache_read": 2.5 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 265000, + "output": 265000 + }, + "cost": { + "input": 0.4, + "output": 2, + "cache_read": 0.08 + } }, - "openai/gpt-5.1-codex": { - "id": "openai/gpt-5.1-codex", - "name": "OpenAI: GPT-5.1-Codex", + "xiaomi/mimo-v2.5": { + "id": "xiaomi/mimo-v2.5", + "name": "MiMo-V2.5", + "family": "mimo", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 0.4, + "output": 2, + "cache_read": 0.08, + "context_over_200k": { + "input": 0.8, + "output": 4, + "cache_read": 0.16 + }, + "tiers": [ + { + "input": 0.8, + "output": 4, + "cache_read": 0.16, + "tier": { + "type": "context", + "size": 256000 + } + } + ] + } }, - "openai/gpt-5.2-pro": { - "id": "openai/gpt-5.2-pro", - "name": "OpenAI: GPT-5.2 Pro", - "attachment": true, + "xiaomi/mimo-v2-pro": { + "id": "xiaomi/mimo-v2-pro", + "name": "MiMo V2 Pro", + "family": "mimo", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "release_date": "2025-12-11", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 21, "output": 168 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 1000000, + "output": 256000 + }, + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.2, + "context_over_200k": { + "input": 2, + "output": 6, + "cache_read": 0.4 + }, + "tiers": [ + { + "input": 2, + "output": 6, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 256000 + } + } + ] + } }, - "prime-intellect/intellect-3": { - "id": "prime-intellect/intellect-3", - "name": "Prime Intellect: INTELLECT-3", + "xiaomi/mimo-v2-flash": { + "id": "xiaomi/mimo-v2-flash", + "name": "MiMo-V2-Flash", + "family": "mimo", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-11-26", + "knowledge": "2024-12-01", + "release_date": "2025-12-16", "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 1.1 }, - "limit": { "context": 131072, "output": 131072 } - }, - "microsoft/phi-4": { - "id": "microsoft/phi-4", - "name": "Microsoft: Phi 4", - "attachment": false, - "reasoning": false, - "tool_call": false, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.3, + "cache_read": 0.01 + } + } + } + }, + "upstage": { + "id": "upstage", + "env": ["UPSTAGE_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.upstage.ai/v1/solar", + "name": "Upstage", + "doc": "https://developers.upstage.ai/docs/apis/chat", + "models": { + "solar-pro2": { + "id": "solar-pro2", + "name": "solar-pro2", + "family": "solar-pro", + "attachment": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.06, "output": 0.14 }, - "limit": { "context": 16384, "output": 16384 } + "knowledge": "2025-03", + "release_date": "2025-05-20", + "last_updated": "2025-05-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 65536, + "output": 8192 + }, + "cost": { + "input": 0.25, + "output": 0.25 + } }, - "microsoft/wizardlm-2-8x22b": { - "id": "microsoft/wizardlm-2-8x22b", - "name": "WizardLM-2 8x22B", + "solar-mini": { + "id": "solar-mini", + "name": "solar-mini", + "family": "solar-mini", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2024-04-24", - "last_updated": "2024-04-24", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.62, "output": 0.62 }, - "limit": { "context": 65535, "output": 8000 } + "knowledge": "2024-09", + "release_date": "2024-06-12", + "last_updated": "2025-04-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "output": 4096 + }, + "cost": { + "input": 0.15, + "output": 0.15 + } }, - "cohere/command-r-08-2024": { - "id": "cohere/command-r-08-2024", - "name": "Cohere: Command R (08-2024)", + "solar-pro3": { + "id": "solar-pro3", + "name": "solar-pro3", + "family": "solar-pro", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-08-30", - "last_updated": "2024-08-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 128000, "output": 4000 } - }, - "cohere/command-r-plus-08-2024": { - "id": "cohere/command-r-plus-08-2024", - "name": "Cohere: Command R+ (08-2024)", + "knowledge": "2025-03", + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.25, + "output": 0.25 + } + } + } + }, + "novita-ai": { + "id": "novita-ai", + "env": ["NOVITA_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.novita.ai/openai", + "name": "NovitaAI", + "doc": "https://novita.ai/docs/guides/introduction", + "models": { + "deepseek/deepseek-r1-turbo": { + "id": "deepseek/deepseek-r1-turbo", + "name": "DeepSeek R1 (Turbo)\t", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-08-30", - "last_updated": "2024-08-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-03-05", + "last_updated": "2025-03-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 128000, "output": 4000 } + "limit": { + "context": 64000, + "output": 16000 + }, + "cost": { + "input": 0.7, + "output": 2.5 + } }, - "cohere/command-r7b-12-2024": { - "id": "cohere/command-r7b-12-2024", - "name": "Cohere: Command R7B (12-2024)", + "deepseek/deepseek-v3-0324": { + "id": "deepseek/deepseek-v3-0324", + "name": "DeepSeek V3 0324", + "family": "deepseek", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-02-27", - "last_updated": "2024-02-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-07", + "release_date": "2025-03-25", + "last_updated": "2025-03-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.0375, "output": 0.15 }, - "limit": { "context": 128000, "output": 4000 } + "limit": { + "context": 163840, + "output": 163840 + }, + "cost": { + "input": 0.27, + "output": 1.12, + "cache_read": 0.135 + } }, - "cohere/command-a": { - "id": "cohere/command-a", - "name": "Cohere: Command A", - "attachment": false, + "deepseek/deepseek-ocr-2": { + "id": "deepseek/deepseek-ocr-2", + "name": "deepseek/deepseek-ocr-2", + "attachment": true, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-03-13", - "last_updated": "2025-03-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 256000, "output": 8192 } + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 0.03, + "output": 0.03 + } }, - "kwaipilot/kat-coder-pro": { - "id": "kwaipilot/kat-coder-pro", - "name": "Kwaipilot: KAT-Coder-Pro V1", - "attachment": false, + "deepseek/deepseek-ocr": { + "id": "deepseek/deepseek-ocr", + "name": "DeepSeek-OCR", + "attachment": true, "reasoning": false, - "tool_call": true, + "tool_call": false, + "structured_output": true, "temperature": true, - "release_date": "2025-09-30", + "release_date": "2025-10-24", "last_updated": "2025-10-24", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.207, "output": 0.828, "cache_read": 0.0414 }, - "limit": { "context": 256000, "output": 128000 } + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 0.03, + "output": 0.03 + } }, - "switchpoint/router": { - "id": "switchpoint/router", - "name": "Switchpoint Router", + "deepseek/deepseek-r1-distill-llama-70b": { + "id": "deepseek/deepseek-r1-distill-llama-70b", + "name": "DeepSeek R1 Distill LLama 70B", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, "tool_call": false, + "structured_output": true, "temperature": true, - "release_date": "2025-07-12", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.85, "output": 3.4 }, - "limit": { "context": 131072, "output": 32768 } + "release_date": "2025-01-27", + "last_updated": "2025-01-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 0.8 + } }, - "morph/morph-v3-large": { - "id": "morph/morph-v3-large", - "name": "Morph: Morph V3 Large", + "deepseek/deepseek-prover-v2-671b": { + "id": "deepseek/deepseek-prover-v2-671b", + "name": "Deepseek Prover V2 671B", "attachment": false, "reasoning": false, "tool_call": false, "temperature": true, - "release_date": "2024-08-15", - "last_updated": "2024-08-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.9, "output": 1.9 }, - "limit": { "context": 262144, "output": 131072 } + "release_date": "2025-04-30", + "last_updated": "2025-04-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 160000, + "output": 160000 + }, + "cost": { + "input": 0.7, + "output": 2.5 + } }, - "morph/morph-v3-fast": { - "id": "morph/morph-v3-fast", - "name": "Morph: Morph V3 Fast", + "deepseek/deepseek-r1-0528-qwen3-8b": { + "id": "deepseek/deepseek-r1-0528-qwen3-8b", + "name": "DeepSeek R1 0528 Qwen3 8B", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2025-05-29", + "last_updated": "2025-05-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32000 + }, + "cost": { + "input": 0.06, + "output": 0.09 + } + }, + "deepseek/deepseek-r1-distill-qwen-32b": { + "id": "deepseek/deepseek-r1-distill-qwen-32b", + "name": "DeepSeek R1 Distill Qwen 32B", + "family": "deepseek-thinking", "attachment": false, "reasoning": false, "tool_call": false, "temperature": true, - "release_date": "2024-08-15", - "last_updated": "2024-08-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.8, "output": 1.2 }, - "limit": { "context": 81920, "output": 38000 } + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 64000, + "output": 32000 + }, + "cost": { + "input": 0.3, + "output": 0.3 + } }, - "x-ai/grok-4-fast": { - "id": "x-ai/grok-4-fast", - "name": "xAI: Grok 4 Fast", - "attachment": true, + "deepseek/deepseek-v3.2-exp": { + "id": "deepseek/deepseek-v3.2-exp", + "name": "Deepseek V3.2 Exp", + "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-08-19", - "last_updated": "2025-08-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 65536 + }, + "cost": { + "input": 0.27, + "output": 0.41 + } }, - "x-ai/grok-4.20-beta": { - "id": "x-ai/grok-4.20-beta", - "name": "xAI: Grok 4.20 Beta", - "attachment": true, + "deepseek/deepseek-v3.1": { + "id": "deepseek/deepseek-v3.1", + "name": "DeepSeek V3.1", + "family": "deepseek", + "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-03-12", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 6 }, - "limit": { "context": 2000000, "output": 32768 } + "release_date": "2025-08-21", + "last_updated": "2025-08-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.27, + "output": 1, + "cache_read": 0.135 + } }, - "x-ai/grok-4.1-fast": { - "id": "x-ai/grok-4.1-fast", - "name": "xAI: Grok 4.1 Fast", - "attachment": true, + "deepseek/deepseek-v3.2": { + "id": "deepseek/deepseek-v3.2", + "name": "Deepseek V3.2", + "family": "deepseek", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-11-19", - "last_updated": "2025-11-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 65536 + }, + "cost": { + "input": 0.269, + "output": 0.4, + "cache_read": 0.1345 + } }, - "x-ai/grok-4": { - "id": "x-ai/grok-4", - "name": "xAI: Grok 4", - "attachment": true, - "reasoning": true, + "deepseek/deepseek-v3-turbo": { + "id": "deepseek/deepseek-v3-turbo", + "name": "DeepSeek V3 (Turbo)\t", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.75 }, - "limit": { "context": 256000, "output": 51200 } + "release_date": "2025-03-05", + "last_updated": "2025-03-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 64000, + "output": 16000 + }, + "cost": { + "input": 0.4, + "output": 1.3 + } }, - "x-ai/grok-code-fast-1": { - "id": "x-ai/grok-code-fast-1", - "name": "xAI: Grok Code Fast 1", + "deepseek/deepseek-r1-distill-qwen-14b": { + "id": "deepseek/deepseek-r1-distill-qwen-14b", + "name": "DeepSeek R1 Distill Qwen 14B", + "family": "deepseek-thinking", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "release_date": "2025-08-26", - "last_updated": "2025-08-26", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 1.5, "cache_read": 0.02 }, - "limit": { "context": 256000, "output": 10000 } + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.15 + } }, - "x-ai/grok-4.20-multi-agent-beta": { - "id": "x-ai/grok-4.20-multi-agent-beta", - "name": "xAI: Grok 4.20 Multi-Agent Beta", - "attachment": true, + "deepseek/deepseek-r1-0528": { + "id": "deepseek/deepseek-r1-0528", + "name": "DeepSeek R1 0528", + "family": "deepseek-thinking", + "attachment": false, "reasoning": true, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-03-12", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 6 }, - "limit": { "context": 2000000, "output": 32768 } + "knowledge": "2024-07", + "release_date": "2025-05-28", + "last_updated": "2025-05-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 32768 + }, + "cost": { + "input": 0.7, + "output": 2.5, + "cache_read": 0.35 + } }, - "x-ai/grok-3-mini": { - "id": "x-ai/grok-3-mini", - "name": "xAI: Grok 3 Mini", + "deepseek/deepseek-v3.1-terminus": { + "id": "deepseek/deepseek-v3.1-terminus", + "name": "Deepseek V3.1 Terminus", + "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 0.5, "cache_read": 0.075 }, - "limit": { "context": 131072, "output": 26215 } + "release_date": "2025-09-22", + "last_updated": "2025-09-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.27, + "output": 1, + "cache_read": 0.135 + } }, - "x-ai/grok-3-beta": { - "id": "x-ai/grok-3-beta", - "name": "xAI: Grok 3 Beta", + "inclusionai/ling-2.6-1t": { + "id": "inclusionai/ling-2.6-1t", + "name": "Ling-2.6-1T", + "family": "ling", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.75 }, - "limit": { "context": 131072, "output": 26215 } + "release_date": "2026-04-23", + "last_updated": "2026-04-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "x-ai/grok-code-fast-1:optimized:free": { - "id": "x-ai/grok-code-fast-1:optimized:free", - "name": "xAI: Grok Code Fast 1 Optimized (experimental, free)", + "inclusionai/ling-2.6-flash": { + "id": "inclusionai/ling-2.6-flash", + "name": "Ling-2.6-flash", + "family": "ling", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-08-27", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 10000 } + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.3, + "cache_read": 0.02 + } }, - "x-ai/grok-3": { - "id": "x-ai/grok-3", - "name": "xAI: Grok 3", + "paddlepaddle/paddleocr-vl": { + "id": "paddlepaddle/paddleocr-vl", + "name": "PaddleOCR-VL", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-10-22", + "last_updated": "2025-10-22", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 16384, + "output": 16384 + }, + "cost": { + "input": 0.02, + "output": 0.02 + } + }, + "nousresearch/hermes-2-pro-llama-3-8b": { + "id": "nousresearch/hermes-2-pro-llama-3-8b", + "name": "Hermes 2 Pro Llama 3 8B", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, + "structured_output": true, "temperature": true, - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.75 }, - "limit": { "context": 131072, "output": 26215 } + "release_date": "2024-06-27", + "last_updated": "2024-06-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 0.14, + "output": 0.14 + } }, - "x-ai/grok-3-mini-beta": { - "id": "x-ai/grok-3-mini-beta", - "name": "xAI: Grok 3 Mini Beta", + "zai-org/glm-4.7": { + "id": "zai-org/glm-4.7", + "name": "GLM-4.7", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 0.5, "cache_read": 0.075 }, - "limit": { "context": 131072, "output": 26215 } + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.11 + } }, - "anthropic/claude-opus-4.6": { - "id": "anthropic/claude-opus-4.6", - "name": "Anthropic: Claude Opus 4.6", - "attachment": true, + "zai-org/glm-5": { + "id": "zai-org/glm-5", + "name": "GLM-5", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 1000000, "output": 128000 } + "release_date": "2026-02-11", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202800, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3.2, + "cache_read": 0.2 + } }, - "anthropic/claude-haiku-4.5": { - "id": "anthropic/claude-haiku-4.5", - "name": "Anthropic: Claude Haiku 4.5", - "attachment": true, + "zai-org/glm-5.1": { + "id": "zai-org/glm-5.1", + "name": "GLM-5.1", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 64000 } + "release_date": "2026-03-27", + "last_updated": "2026-03-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 1.4, + "output": 4.4, + "cache_read": 0.26 + } }, - "anthropic/claude-3.7-sonnet:thinking": { - "id": "anthropic/claude-3.7-sonnet:thinking", - "name": "Anthropic: Claude 3.7 Sonnet (thinking)", - "attachment": true, + "zai-org/glm-4.5": { + "id": "zai-org/glm-4.5", + "name": "GLM-4.5", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-02-19", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 98304 + }, + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.11 + } }, - "anthropic/claude-opus-4.1": { - "id": "anthropic/claude-opus-4.1", - "name": "Anthropic: Claude Opus 4.1", - "attachment": true, + "zai-org/glm-4.5-air": { + "id": "zai-org/glm-4.5-air", + "name": "GLM 4.5 Air", + "family": "glm-air", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "knowledge": "2025-04", + "release_date": "2025-10-13", + "last_updated": "2025-10-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 98304 + }, + "cost": { + "input": 0.13, + "output": 0.85 + } }, - "anthropic/claude-3.7-sonnet": { - "id": "anthropic/claude-3.7-sonnet", - "name": "Anthropic: Claude 3.7 Sonnet", + "zai-org/glm-4.5v": { + "id": "zai-org/glm-4.5v", + "name": "GLM 4.5V", + "family": "glmv", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-02-19", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "knowledge": "2025-04", + "release_date": "2025-08-11", + "last_updated": "2025-08-11", + "modalities": { + "input": ["text", "video", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 65536, + "output": 16384 + }, + "cost": { + "input": 0.6, + "output": 1.8, + "cache_read": 0.11 + } }, - "anthropic/claude-sonnet-4.6": { - "id": "anthropic/claude-sonnet-4.6", - "name": "Anthropic: Claude Sonnet 4.6", - "attachment": true, + "zai-org/glm-4.6": { + "id": "zai-org/glm-4.6", + "name": "GLM 4.6", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2026-02-17", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 1000000, "output": 128000 } + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.55, + "output": 2.2, + "cache_read": 0.11 + } }, - "anthropic/claude-sonnet-4": { - "id": "anthropic/claude-sonnet-4", - "name": "Anthropic: Claude Sonnet 4", + "zai-org/glm-4.6v": { + "id": "zai-org/glm-4.6v", + "name": "GLM 4.6V", + "family": "glmv", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-05-22", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "knowledge": "2025-04", + "release_date": "2025-12-08", + "last_updated": "2025-12-08", + "modalities": { + "input": ["text", "video", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.3, + "output": 0.9, + "cache_read": 0.055 + } }, - "anthropic/claude-3.5-haiku": { - "id": "anthropic/claude-3.5-haiku", - "name": "Anthropic: Claude 3.5 Haiku", - "attachment": true, - "reasoning": false, + "zai-org/glm-4.7-flash": { + "id": "zai-org/glm-4.7-flash", + "name": "GLM-4.7-Flash", + "family": "glm", + "attachment": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.8, "output": 4, "cache_read": 0.08, "cache_write": 1 }, - "limit": { "context": 200000, "output": 8192 } + "knowledge": "2025-04", + "release_date": "2026-01-19", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 0.07, + "output": 0.4, + "cache_read": 0.01 + } }, - "anthropic/claude-opus-4.5": { - "id": "anthropic/claude-opus-4.5", - "name": "Anthropic: Claude Opus 4.5", + "zai-org/autoglm-phone-9b-multilingual": { + "id": "zai-org/autoglm-phone-9b-multilingual", + "name": "AutoGLM-Phone-9B-Multilingual", "attachment": true, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "release_date": "2025-11-24", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 200000, "output": 64000 } + "release_date": "2025-12-10", + "last_updated": "2025-12-10", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 65536, + "output": 65536 + }, + "cost": { + "input": 0.035, + "output": 0.138 + } }, - "anthropic/claude-3.5-sonnet": { - "id": "anthropic/claude-3.5-sonnet", - "name": "Anthropic: Claude 3.5 Sonnet", - "attachment": true, + "mistralai/mistral-nemo": { + "id": "mistralai/mistral-nemo", + "name": "Mistral Nemo", + "family": "mistral-nemo", + "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, + "structured_output": true, "temperature": true, - "release_date": "2024-10-22", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 6, "output": 30 }, - "limit": { "context": 200000, "output": 8192 } + "release_date": "2024-07-30", + "last_updated": "2024-07-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 60288, + "output": 16000 + }, + "cost": { + "input": 0.04, + "output": 0.17 + } }, - "anthropic/claude-3-haiku": { - "id": "anthropic/claude-3-haiku", - "name": "Anthropic: Claude 3 Haiku", - "attachment": true, + "baichuan/baichuan-m2-32b": { + "id": "baichuan/baichuan-m2-32b", + "name": "baichuan-m2-32b", + "family": "baichuan", + "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, + "structured_output": false, "temperature": true, - "release_date": "2024-03-07", - "last_updated": "2024-03-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 1.25, "cache_read": 0.03, "cache_write": 0.3 }, - "limit": { "context": 200000, "output": 4096 } + "knowledge": "2024-12", + "release_date": "2025-08-13", + "last_updated": "2025-08-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.07, + "output": 0.07 + } }, - "anthropic/claude-sonnet-4.5": { - "id": "anthropic/claude-sonnet-4.5", - "name": "Anthropic: Claude Sonnet 4.5", + "meta-llama/llama-4-scout-17b-16e-instruct": { + "id": "meta-llama/llama-4-scout-17b-16e-instruct", + "name": "Llama 4 Scout Instruct", "attachment": true, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "release_date": "2025-09-29", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 1000000, "output": 64000 } + "release_date": "2025-04-06", + "last_updated": "2025-04-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.18, + "output": 0.59 + } }, - "anthropic/claude-opus-4": { - "id": "anthropic/claude-opus-4", - "name": "Anthropic: Claude Opus 4", - "attachment": true, - "reasoning": true, + "meta-llama/llama-3.3-70b-instruct": { + "id": "meta-llama/llama-3.3-70b-instruct", + "name": "Llama 3.3 70B Instruct", + "family": "llama", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2025-05-22", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "knowledge": "2023-12", + "release_date": "2024-12-07", + "last_updated": "2024-12-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 120000 + }, + "cost": { + "input": 0.135, + "output": 0.4 + } }, - "alpindale/goliath-120b": { - "id": "alpindale/goliath-120b", - "name": "Goliath 120B", + "meta-llama/llama-3.2-3b-instruct": { + "id": "meta-llama/llama-3.2-3b-instruct", + "name": "Llama 3.2 3B Instruct", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": false, "temperature": true, - "release_date": "2023-11-10", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-09-18", + "last_updated": "2024-09-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 3.75, "output": 7.5 }, - "limit": { "context": 6144, "output": 1024 } + "limit": { + "context": 32768, + "output": 32000 + }, + "cost": { + "input": 0.03, + "output": 0.05 + } }, - "relace/relace-search": { - "id": "relace/relace-search", - "name": "Relace: Relace Search", + "meta-llama/llama-3-8b-instruct": { + "id": "meta-llama/llama-3-8b-instruct", + "name": "Llama 3 8B Instruct", + "family": "llama", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "temperature": true, - "release_date": "2025-12-09", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1, "output": 3 }, - "limit": { "context": 256000, "output": 128000 } + "release_date": "2024-04-25", + "last_updated": "2024-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 0.04, + "output": 0.04 + } }, - "relace/relace-apply-3": { - "id": "relace/relace-apply-3", - "name": "Relace: Relace Apply 3", + "meta-llama/llama-3.1-8b-instruct": { + "id": "meta-llama/llama-3.1-8b-instruct", + "name": "Llama 3.1 8B Instruct", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": false, - "release_date": "2025-09-26", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.85, "output": 1.25 }, - "limit": { "context": 256000, "output": 128000 } + "temperature": true, + "release_date": "2024-07-24", + "last_updated": "2024-07-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 16384, + "output": 16384 + }, + "cost": { + "input": 0.02, + "output": 0.05 + } }, - "sao10k/l3.1-70b-hanami-x1": { - "id": "sao10k/l3.1-70b-hanami-x1", - "name": "Sao10K: Llama 3.1 70B Hanami x1", + "meta-llama/llama-3-70b-instruct": { + "id": "meta-llama/llama-3-70b-instruct", + "name": "Llama3 70B Instruct", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": false, + "structured_output": true, "temperature": true, - "release_date": "2025-01-08", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-04-25", + "last_updated": "2024-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 3, "output": 3 }, - "limit": { "context": 16000, "output": 16000 } + "limit": { + "context": 8192, + "output": 8000 + }, + "cost": { + "input": 0.51, + "output": 0.74 + } }, - "sao10k/l3-lunaris-8b": { - "id": "sao10k/l3-lunaris-8b", - "name": "Sao10K: Llama 3 8B Lunaris", - "attachment": false, + "meta-llama/llama-4-maverick-17b-128e-instruct-fp8": { + "id": "meta-llama/llama-4-maverick-17b-128e-instruct-fp8", + "name": "Llama 4 Maverick Instruct", + "attachment": true, "reasoning": false, "tool_call": false, "temperature": true, - "release_date": "2024-08-13", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-04-06", + "last_updated": "2025-04-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.04, "output": 0.05 }, - "limit": { "context": 8192, "output": 8192 } + "limit": { + "context": 1048576, + "output": 8192 + }, + "cost": { + "input": 0.27, + "output": 0.85 + } }, - "sao10k/l3.3-euryale-70b": { - "id": "sao10k/l3.3-euryale-70b", - "name": "Sao10K: Llama 3.3 Euryale 70B", + "gryphe/mythomax-l2-13b": { + "id": "gryphe/mythomax-l2-13b", + "name": "Mythomax L2 13B", "attachment": false, "reasoning": false, "tool_call": false, "temperature": true, - "release_date": "2024-12-18", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-04-25", + "last_updated": "2024-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.65, "output": 0.75 }, - "limit": { "context": 131072, "output": 16384 } + "limit": { + "context": 4096, + "output": 3200 + }, + "cost": { + "input": 0.09, + "output": 0.09 + } }, - "sao10k/l3.1-euryale-70b": { - "id": "sao10k/l3.1-euryale-70b", - "name": "Sao10K: Llama 3.1 Euryale 70B v2.2", + "sao10k/l31-70b-euryale-v2.2": { + "id": "sao10k/l31-70b-euryale-v2.2", + "name": "L31 70B Euryale V2.2", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2024-08-28", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-09-19", + "last_updated": "2024-09-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.85, "output": 0.85 }, - "limit": { "context": 131072, "output": 16384 } + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 1.48, + "output": 1.48 + } }, - "sao10k/l3-euryale-70b": { - "id": "sao10k/l3-euryale-70b", - "name": "Sao10k: Llama 3 Euryale 70B v2.1", + "sao10k/l3-70b-euryale-v2.1": { + "id": "sao10k/l3-70b-euryale-v2.1", + "name": "L3 70B Euryale V2.1\t", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, "release_date": "2024-06-18", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "last_updated": "2024-06-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1.48, "output": 1.48 }, - "limit": { "context": 8192, "output": 8192 } + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 1.48, + "output": 1.48 + } }, - "upstage/solar-pro-3": { - "id": "upstage/solar-pro-3", - "name": "Upstage: Solar Pro 3", + "sao10k/l3-8b-lunaris": { + "id": "sao10k/l3-8b-lunaris", + "name": "Sao10k L3 8B Lunaris\t", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, + "structured_output": true, "temperature": true, - "release_date": "2026-01-27", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 128000, "output": 32768 } + "release_date": "2024-11-28", + "last_updated": "2024-11-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 0.05, + "output": 0.05 + } }, - "mancer/weaver": { - "id": "mancer/weaver", - "name": "Mancer: Weaver (alpha)", + "microsoft/wizardlm-2-8x22b": { + "id": "microsoft/wizardlm-2-8x22b", + "name": "Wizardlm 2 8x22B", "attachment": false, "reasoning": false, "tool_call": false, "temperature": true, - "release_date": "2023-08-02", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.75, "output": 1 }, - "limit": { "context": 8000, "output": 2000 } + "release_date": "2024-04-24", + "last_updated": "2024-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 65535, + "output": 8000 + }, + "cost": { + "input": 0.62, + "output": 0.62 + } }, - "deepseek/deepseek-v3.1-terminus": { - "id": "deepseek/deepseek-v3.1-terminus", - "name": "DeepSeek: DeepSeek V3.1 Terminus", - "attachment": false, + "openai/gpt-oss-20b": { + "id": "openai/gpt-oss-20b", + "name": "OpenAI: GPT OSS 20B", + "attachment": true, "reasoning": true, - "tool_call": true, + "tool_call": false, + "structured_output": true, "temperature": true, - "release_date": "2025-09-22", - "last_updated": "2025-09-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-08-06", + "last_updated": "2025-08-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.21, "output": 0.79, "cache_read": 0.13 }, - "limit": { "context": 163840, "output": 32768 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.04, + "output": 0.15 + } }, - "deepseek/deepseek-r1-distill-llama-70b": { - "id": "deepseek/deepseek-r1-distill-llama-70b", - "name": "DeepSeek: R1 Distill Llama 70B", - "attachment": false, + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "name": "OpenAI GPT OSS 120B", + "attachment": true, "reasoning": true, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-01-23", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-08-06", + "last_updated": "2025-08-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.7, "output": 0.8, "cache_read": 0.015 }, - "limit": { "context": 131072, "output": 16384 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.05, + "output": 0.25 + } }, - "deepseek/deepseek-r1": { - "id": "deepseek/deepseek-r1", - "name": "DeepSeek: R1", + "minimaxai/minimax-m1-80k": { + "id": "minimaxai/minimax-m1-80k", + "name": "MiniMax M1", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.7, "output": 2.5 }, - "limit": { "context": 64000, "output": 16000 } + "limit": { + "context": 1000000, + "output": 40000 + }, + "cost": { + "input": 0.55, + "output": 2.2 + } }, - "deepseek/deepseek-chat": { - "id": "deepseek/deepseek-chat", - "name": "DeepSeek: DeepSeek V3", + "Sao10K/L3-8B-Stheno-v3.2": { + "id": "Sao10K/L3-8B-Stheno-v3.2", + "name": "L3 8B Stheno V3.2", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2024-12-01", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-11-29", + "last_updated": "2024-11-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.32, "output": 0.89, "cache_read": 0.15 }, - "limit": { "context": 163840, "output": 163840 } + "limit": { + "context": 8192, + "output": 32000 + }, + "cost": { + "input": 0.05, + "output": 0.05 + } }, - "deepseek/deepseek-v3.2-exp": { - "id": "deepseek/deepseek-v3.2-exp", - "name": "DeepSeek: DeepSeek V3.2 Exp", + "xiaomimimo/mimo-v2-flash": { + "id": "xiaomimimo/mimo-v2-flash", + "name": "XiaomiMiMo/MiMo-V2-Flash", + "family": "mimo", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-01-01", - "last_updated": "2025-09-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2025-12-19", + "last_updated": "2025-12-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.27, "output": 0.41 }, - "limit": { "context": 163840, "output": 65536 } + "limit": { + "context": 262144, + "output": 32000 + }, + "cost": { + "input": 0.1, + "output": 0.3, + "cache_read": 0.3 + } }, - "deepseek/deepseek-chat-v3.1": { - "id": "deepseek/deepseek-chat-v3.1", - "name": "DeepSeek: DeepSeek V3.1", - "attachment": false, + "baidu/ernie-4.5-vl-28b-a3b-thinking": { + "id": "baidu/ernie-4.5-vl-28b-a3b-thinking", + "name": "ERNIE-4.5-VL-28B-A3B-Thinking", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-08-21", - "last_updated": "2025-08-21", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-11-26", + "last_updated": "2025-11-26", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.75 }, - "limit": { "context": 32768, "output": 7168 } + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.39, + "output": 0.39 + } }, - "deepseek/deepseek-v3.2-speciale": { - "id": "deepseek/deepseek-v3.2-speciale", - "name": "DeepSeek: DeepSeek V3.2 Speciale", - "attachment": false, + "baidu/ernie-4.5-vl-424b-a47b": { + "id": "baidu/ernie-4.5-vl-424b-a47b", + "name": "ERNIE 4.5 VL 424B A47B", + "attachment": true, "reasoning": true, "tool_call": false, "temperature": true, - "release_date": "2025-12-01", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-06-30", + "last_updated": "2025-06-30", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.4, "output": 1.2, "cache_read": 0.135 }, - "limit": { "context": 163840, "output": 163840 } - }, - "deepseek/deepseek-r1-distill-qwen-32b": { - "id": "deepseek/deepseek-r1-distill-qwen-32b", - "name": "DeepSeek: R1 Distill Qwen 32B", + "limit": { + "context": 123000, + "output": 16000 + }, + "cost": { + "input": 0.42, + "output": 1.25 + } + }, + "baidu/ernie-4.5-21B-a3b": { + "id": "baidu/ernie-4.5-21B-a3b", + "name": "ERNIE 4.5 21B A3B", + "family": "ernie", "attachment": false, - "reasoning": true, - "tool_call": false, + "reasoning": false, + "tool_call": true, "temperature": true, - "release_date": "2025-01-01", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-03", + "release_date": "2025-06-30", + "last_updated": "2025-06-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.29, "output": 0.29 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 120000, + "output": 8000 + }, + "cost": { + "input": 0.07, + "output": 0.28 + } }, - "deepseek/deepseek-chat-v3-0324": { - "id": "deepseek/deepseek-chat-v3-0324", - "name": "DeepSeek: DeepSeek V3 0324", + "baidu/ernie-4.5-300b-a47b-paddle": { + "id": "baidu/ernie-4.5-300b-a47b-paddle", + "name": "ERNIE 4.5 300B A47B", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, + "structured_output": true, "temperature": true, - "release_date": "2025-03-24", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-06-30", + "last_updated": "2025-06-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 0.77, "cache_read": 0.095 }, - "limit": { "context": 163840, "output": 65536 } + "limit": { + "context": 123000, + "output": 12000 + }, + "cost": { + "input": 0.28, + "output": 1.1 + } }, - "deepseek/deepseek-r1-0528": { - "id": "deepseek/deepseek-r1-0528", - "name": "DeepSeek: R1 0528", + "baidu/ernie-4.5-21B-a3b-thinking": { + "id": "baidu/ernie-4.5-21B-a3b-thinking", + "name": "ERNIE-4.5-21B-A3B-Thinking", + "family": "ernie", "attachment": false, "reasoning": true, - "tool_call": true, + "tool_call": false, "temperature": true, - "release_date": "2025-05-28", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-03", + "release_date": "2025-09-19", + "last_updated": "2025-09-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.45, "output": 2.15, "cache_read": 0.2 }, - "limit": { "context": 163840, "output": 65536 } + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.07, + "output": 0.28 + } }, - "deepseek/deepseek-v3.2": { - "id": "deepseek/deepseek-v3.2", - "name": "DeepSeek: DeepSeek V3.2", - "attachment": false, + "baidu/ernie-4.5-vl-28b-a3b": { + "id": "baidu/ernie-4.5-vl-28b-a3b", + "name": "ERNIE 4.5 VL 28B A3B", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-12-01", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-06-30", + "last_updated": "2025-06-30", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.26, "output": 0.38, "cache_read": 0.125 }, - "limit": { "context": 163840, "output": 65536 } + "limit": { + "context": 30000, + "output": 8000 + }, + "cost": { + "input": 1.4, + "output": 5.6 + } }, - "nvidia/llama-3.1-nemotron-70b-instruct": { - "id": "nvidia/llama-3.1-nemotron-70b-instruct", - "name": "NVIDIA: Llama 3.1 Nemotron 70B Instruct", + "minimax/minimax-m2.7": { + "id": "minimax/minimax-m2.7", + "name": "MiniMax M2.7", + "family": "minimax-m2.7", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2024-10-12", - "last_updated": "2024-10-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.2, "output": 1.2 }, - "limit": { "context": 131072, "output": 16384 } + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06 + } }, - "nvidia/llama-3.3-nemotron-super-49b-v1.5": { - "id": "nvidia/llama-3.3-nemotron-super-49b-v1.5", - "name": "NVIDIA: Llama 3.3 Nemotron Super 49B V1.5", + "minimax/minimax-m2": { + "id": "minimax/minimax-m2", + "name": "MiniMax-M2", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-03-16", - "last_updated": "2025-03-16", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 131072, "output": 26215 } - }, - "nvidia/nemotron-nano-12b-v2-vl": { - "id": "nvidia/nemotron-nano-12b-v2-vl", - "name": "NVIDIA: Nemotron Nano 12B 2 VL", - "attachment": true, - "reasoning": true, - "tool_call": false, - "temperature": true, - "release_date": "2025-10-28", - "last_updated": "2026-01-31", - "modalities": { "input": ["image", "text", "video"], "output": ["text"] }, + "release_date": "2025-10-27", + "last_updated": "2025-10-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 0.6 }, - "limit": { "context": 131072, "output": 26215 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03 + } }, - "nvidia/nemotron-nano-9b-v2": { - "id": "nvidia/nemotron-nano-9b-v2", - "name": "NVIDIA: Nemotron Nano 9B V2", + "minimax/minimax-m2.1": { + "id": "minimax/minimax-m2.1", + "name": "Minimax M2.1", + "family": "minimax", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-08-18", - "last_updated": "2025-08-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.04, "output": 0.16 }, - "limit": { "context": 131072, "output": 26215 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03 + } }, - "nvidia/nemotron-3-super-120b-a12b:free": { - "id": "nvidia/nemotron-3-super-120b-a12b:free", - "name": "NVIDIA: Nemotron 3 Super (free)", + "minimax/minimax-m2.5": { + "id": "minimax/minimax-m2.5", + "name": "MiniMax M2.5", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2026-03-12", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 262144 } + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 204800, + "output": 131100 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03 + } }, - "nvidia/nemotron-3-nano-30b-a3b": { - "id": "nvidia/nemotron-3-nano-30b-a3b", - "name": "NVIDIA: Nemotron 3 Nano 30B A3B", + "minimax/minimax-m2.5-highspeed": { + "id": "minimax/minimax-m2.5-highspeed", + "name": "MiniMax M2.5 Highspeed", + "family": "minimax-m2.5", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2024-12", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.05, "output": 0.2 }, - "limit": { "context": 262144, "output": 52429 } + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 204800, + "output": 131100 + }, + "cost": { + "input": 0.6, + "output": 2.4, + "cache_read": 0.03 + } }, - "gryphe/mythomax-l2-13b": { - "id": "gryphe/mythomax-l2-13b", - "name": "MythoMax 13B", + "qwen/qwen2.5-7b-instruct": { + "id": "qwen/qwen2.5-7b-instruct", + "name": "Qwen2.5 7B Instruct", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-04-25", - "last_updated": "2024-04-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.06, "output": 0.06 }, - "limit": { "context": 4096, "output": 4096 } + "limit": { + "context": 32000, + "output": 32000 + }, + "cost": { + "input": 0.07, + "output": 0.07 + } }, - "z-ai/glm-4.7-flash": { - "id": "z-ai/glm-4.7-flash", - "name": "Z.ai: GLM 4.7 Flash", - "attachment": false, + "qwen/qwen3.5-122b-a10b": { + "id": "qwen/qwen3.5-122b-a10b", + "name": "Qwen3.5-122B-A10B", + "family": "qwen", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-01-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-26", + "last_updated": "2026-02-26", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.06, "output": 0.4, "cache_read": 0.01 }, - "limit": { "context": 202752, "output": 40551 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.4, + "output": 3.2 + } }, - "z-ai/glm-4.5": { - "id": "z-ai/glm-4.5", - "name": "Z.ai: GLM 4.5", - "attachment": false, + "qwen/qwen3.5-27b": { + "id": "qwen/qwen3.5-27b", + "name": "Qwen3.5-27B", + "family": "qwen", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-07-28", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-26", + "last_updated": "2026-02-26", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.2, "cache_read": 0.175 }, - "limit": { "context": 131072, "output": 98304 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.4 + } }, - "z-ai/glm-4.6": { - "id": "z-ai/glm-4.6", - "name": "Z.ai: GLM 4.6", + "qwen/qwen3-235b-a22b-instruct-2507": { + "id": "qwen/qwen3-235b-a22b-instruct-2507", + "name": "Qwen3 235B A22B Instruct 2507", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-09-30", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-22", + "last_updated": "2025-07-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.39, "output": 1.9, "cache_read": 0.175 }, - "limit": { "context": 204800, "output": 204800 } + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.09, + "output": 0.58 + } }, - "z-ai/glm-4.6v": { - "id": "z-ai/glm-4.6v", - "name": "Z.ai: GLM 4.6V", + "qwen/qwen3-omni-30b-a3b-instruct": { + "id": "qwen/qwen3-omni-30b-a3b-instruct", + "name": "Qwen3 Omni 30B A3B Instruct", + "family": "qwen", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-09-30", - "last_updated": "2026-01-10", - "modalities": { "input": ["image", "text", "video"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-09-24", + "last_updated": "2025-09-24", + "modalities": { + "input": ["text", "video", "audio", "image"], + "output": ["text", "audio"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 0.9 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 65536, + "output": 16384 + }, + "cost": { + "input": 0.25, + "output": 0.97, + "input_audio": 2.2, + "output_audio": 1.788 + } }, - "z-ai/glm-5": { - "id": "z-ai/glm-5", - "name": "Z.ai: GLM 5", - "attachment": false, + "qwen/qwen3.5-397b-a17b": { + "id": "qwen/qwen3.5-397b-a17b", + "name": "Qwen3.5-397B-A17B", + "family": "qwen", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-17", + "last_updated": "2026-02-17", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.72, "output": 2.3 }, - "limit": { "context": 202752, "output": 131072 } + "limit": { + "context": 262144, + "output": 64000 + }, + "cost": { + "input": 0.6, + "output": 3.6 + } }, - "z-ai/glm-4.5-air": { - "id": "z-ai/glm-4.5-air", - "name": "Z.ai: GLM 4.5 Air", - "attachment": false, - "reasoning": true, - "tool_call": true, + "qwen/qwen2.5-vl-72b-instruct": { + "id": "qwen/qwen2.5-vl-72b-instruct", + "name": "Qwen2.5 VL 72B Instruct", + "family": "qwen", + "attachment": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-03-25", + "last_updated": "2025-03-25", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.13, "output": 0.85, "cache_read": 0.025 }, - "limit": { "context": 131072, "output": 98304 } + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.8, + "output": 0.8 + } }, - "z-ai/glm-4.5v": { - "id": "z-ai/glm-4.5v", - "name": "Z.ai: GLM 4.5V", + "qwen/qwen3-vl-235b-a22b-thinking": { + "id": "qwen/qwen3-vl-235b-a22b-thinking", + "name": "Qwen3 VL 235B A22B Thinking", "attachment": true, "reasoning": true, - "tool_call": true, + "tool_call": false, "temperature": true, - "release_date": "2025-08-11", - "last_updated": "2025-08-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-09-24", + "last_updated": "2025-09-24", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 1.8, "cache_read": 0.11 }, - "limit": { "context": 65536, "output": 16384 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.98, + "output": 3.95 + } }, - "z-ai/glm-4.7": { - "id": "z-ai/glm-4.7", - "name": "Z.ai: GLM 4.7", - "attachment": false, - "reasoning": true, + "qwen/qwen3-vl-30b-a3b-thinking": { + "id": "qwen/qwen3-vl-30b-a3b-thinking", + "name": "qwen/qwen3-vl-30b-a3b-thinking", + "attachment": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-12-22", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-10-11", + "last_updated": "2025-10-11", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.38, "output": 1.98, "cache_read": 0.2 }, - "limit": { "context": 202752, "output": 65535 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.2, + "output": 1 + } }, - "z-ai/glm-4-32b": { - "id": "z-ai/glm-4-32b", - "name": "Z.ai: GLM 4 32B ", - "attachment": false, - "reasoning": false, + "qwen/qwen3-omni-30b-a3b-thinking": { + "id": "qwen/qwen3-omni-30b-a3b-thinking", + "name": "Qwen3 Omni 30B A3B Thinking", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-07-25", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-09-24", + "last_updated": "2025-09-24", + "modalities": { + "input": ["text", "audio", "video", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.1 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 65536, + "output": 16384 + }, + "cost": { + "input": 0.25, + "output": 0.97, + "input_audio": 2.2, + "output_audio": 1.788 + } }, - "nex-agi/deepseek-v3.1-nex-n1": { - "id": "nex-agi/deepseek-v3.1-nex-n1", - "name": "Nex AGI: DeepSeek V3.1 Nex N1", - "attachment": false, + "qwen/qwen3-vl-8b-instruct": { + "id": "qwen/qwen3-vl-8b-instruct", + "name": "qwen/qwen3-vl-8b-instruct", + "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-01-01", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.27, "output": 1 }, - "limit": { "context": 131072, "output": 163840 } + "release_date": "2025-10-17", + "last_updated": "2025-10-17", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.08, + "output": 0.5 + } }, - "allenai/olmo-3.1-32b-instruct": { - "id": "allenai/olmo-3.1-32b-instruct", - "name": "AllenAI: Olmo 3.1 32B Instruct", + "qwen/qwen3-max": { + "id": "qwen/qwen3-max", + "name": "Qwen3 Max", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-01-07", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.6 }, - "limit": { "context": 65536, "output": 32768 } + "knowledge": "2025-04", + "release_date": "2025-09-24", + "last_updated": "2025-09-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 2.11, + "output": 8.45 + } }, - "allenai/olmo-2-0325-32b-instruct": { - "id": "allenai/olmo-2-0325-32b-instruct", - "name": "AllenAI: Olmo 2 32B Instruct", + "qwen/qwen3-32b-fp8": { + "id": "qwen/qwen3-32b-fp8", + "name": "Qwen3 32B", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": false, - "release_date": "2025-03-15", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "release_date": "2025-04-29", + "last_updated": "2025-04-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.05, "output": 0.2 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 40960, + "output": 20000 + }, + "cost": { + "input": 0.1, + "output": 0.45 + } }, - "allenai/olmo-3-32b-think": { - "id": "allenai/olmo-3-32b-think", - "name": "AllenAI: Olmo 3 32B Think", + "qwen/qwen3-4b-fp8": { + "id": "qwen/qwen3-4b-fp8", + "name": "Qwen3 4B", "attachment": false, "reasoning": true, "tool_call": false, "temperature": true, - "release_date": "2025-11-22", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-04-29", + "last_updated": "2025-04-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.5 }, - "limit": { "context": 65536, "output": 65536 } + "limit": { + "context": 128000, + "output": 20000 + }, + "cost": { + "input": 0.03, + "output": 0.03 + } }, - "allenai/olmo-3-7b-think": { - "id": "allenai/olmo-3-7b-think", - "name": "AllenAI: Olmo 3 7B Think", + "qwen/qwen3-235b-a22b-thinking-2507": { + "id": "qwen/qwen3-235b-a22b-thinking-2507", + "name": "Qwen3 235B A22b Thinking 2507", + "family": "qwen", "attachment": false, "reasoning": true, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2025-11-22", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-25", + "last_updated": "2025-07-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.12, "output": 0.2 }, - "limit": { "context": 65536, "output": 65536 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.3, + "output": 3 + } }, - "allenai/olmo-3.1-32b-think": { - "id": "allenai/olmo-3.1-32b-think", - "name": "AllenAI: Olmo 3.1 32B Think", + "qwen/qwen3-next-80b-a3b-thinking": { + "id": "qwen/qwen3-next-80b-a3b-thinking", + "name": "Qwen3 Next 80B A3B Thinking", "attachment": false, "reasoning": true, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-12-17", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-09-10", + "last_updated": "2025-09-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.5 }, - "limit": { "context": 65536, "output": 65536 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.15, + "output": 1.5 + } }, - "allenai/olmo-3-7b-instruct": { - "id": "allenai/olmo-3-7b-instruct", - "name": "AllenAI: Olmo 3 7B Instruct", + "qwen/qwen-mt-plus": { + "id": "qwen/qwen-mt-plus", + "name": "Qwen MT Plus", "attachment": false, "reasoning": false, "tool_call": false, "temperature": true, - "release_date": "2025-11-22", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-09-03", + "last_updated": "2025-09-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.2 }, - "limit": { "context": 65536, "output": 65536 } + "limit": { + "context": 16384, + "output": 8192 + }, + "cost": { + "input": 0.25, + "output": 0.75 + } }, - "allenai/molmo-2-8b": { - "id": "allenai/molmo-2-8b", - "name": "AllenAI: Molmo2 8B", - "attachment": true, + "qwen/qwen3-next-80b-a3b-instruct": { + "id": "qwen/qwen3-next-80b-a3b-instruct", + "name": "Qwen3 Next 80B A3B Instruct", + "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-01-09", - "last_updated": "2026-01-31", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "release_date": "2025-09-10", + "last_updated": "2025-09-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 36864, "output": 36864 } - }, - "google/gemini-2.5-flash-lite": { - "id": "google/gemini-2.5-flash-lite", - "name": "Google: Gemini 2.5 Flash Lite", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-06-17", - "last_updated": "2026-03-15", - "modalities": { "input": ["audio", "image", "pdf", "text", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "reasoning": 0.4, "cache_read": 0.01, "cache_write": 0.083333 }, - "limit": { "context": 1048576, "output": 65535 } - }, - "google/gemini-2.5-flash-lite-preview-09-2025": { - "id": "google/gemini-2.5-flash-lite-preview-09-2025", - "name": "Google: Gemini 2.5 Flash Lite Preview 09-2025", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-09-25", - "last_updated": "2026-03-15", - "modalities": { "input": ["audio", "image", "pdf", "text", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "reasoning": 0.4, "cache_read": 0.01, "cache_write": 0.083333 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.15, + "output": 1.5 + } }, - "google/gemma-2-9b-it": { - "id": "google/gemma-2-9b-it", - "name": "Google: Gemma 2 9B", + "qwen/qwen3-30b-a3b-fp8": { + "id": "qwen/qwen3-30b-a3b-fp8", + "name": "Qwen3 30B A3B", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": false, "temperature": true, - "release_date": "2024-06-28", - "last_updated": "2024-06-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-04-29", + "last_updated": "2025-04-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.03, "output": 0.09 }, - "limit": { "context": 8192, "output": 1639 } + "limit": { + "context": 40960, + "output": 20000 + }, + "cost": { + "input": 0.09, + "output": 0.45 + } }, - "google/gemini-3.1-pro-preview": { - "id": "google/gemini-3.1-pro-preview", - "name": "Google: Gemini 3.1 Pro Preview", - "attachment": true, - "reasoning": true, + "qwen/qwen3-coder-next": { + "id": "qwen/qwen3-coder-next", + "name": "Qwen3 Coder Next", + "family": "qwen", + "attachment": false, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-02-19", - "last_updated": "2026-03-15", - "modalities": { "input": ["audio", "image", "pdf", "text", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 12, "reasoning": 12 }, - "limit": { "context": 1048576, "output": 65536 } + "release_date": "2026-02-03", + "last_updated": "2026-02-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.2, + "output": 1.5 + } }, - "google/gemini-3-pro-preview": { - "id": "google/gemini-3-pro-preview", - "name": "Google: Gemini 3 Pro Preview", - "attachment": true, - "reasoning": true, + "qwen/qwen3-coder-480b-a35b-instruct": { + "id": "qwen/qwen3-coder-480b-a35b-instruct", + "name": "Qwen3 Coder 480B A35B Instruct", + "family": "qwen", + "attachment": false, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-11-18", - "last_updated": "2026-03-15", - "modalities": { "input": ["audio", "image", "pdf", "text", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 12, "reasoning": 12, "cache_read": 0.2, "cache_write": 0.375 }, - "limit": { "context": 1048576, "output": 65536 } + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 1.3 + } }, - "google/gemma-3-27b-it": { - "id": "google/gemma-3-27b-it", - "name": "Google: Gemma 3 27B", + "qwen/qwen3-vl-30b-a3b-instruct": { + "id": "qwen/qwen3-vl-30b-a3b-instruct", + "name": "qwen/qwen3-vl-30b-a3b-instruct", "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-03-12", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "release_date": "2025-10-11", + "last_updated": "2025-10-11", + "modalities": { + "input": ["text", "video", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.03, "output": 0.11, "cache_read": 0.02 }, - "limit": { "context": 128000, "output": 65536 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.2, + "output": 0.7 + } }, - "google/gemma-3-4b-it": { - "id": "google/gemma-3-4b-it", - "name": "Google: Gemma 3 4B", - "attachment": true, + "qwen/qwen3-coder-30b-a3b-instruct": { + "id": "qwen/qwen3-coder-30b-a3b-instruct", + "name": "Qwen3 Coder 30b A3B Instruct", + "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-03-13", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "release_date": "2025-10-09", + "last_updated": "2025-10-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.04, "output": 0.08 }, - "limit": { "context": 131072, "output": 19200 } + "limit": { + "context": 160000, + "output": 32768 + }, + "cost": { + "input": 0.07, + "output": 0.27 + } }, - "google/gemma-3n-e4b-it": { - "id": "google/gemma-3n-e4b-it", - "name": "Google: Gemma 3n 4B", + "qwen/qwen3-235b-a22b-fp8": { + "id": "qwen/qwen3-235b-a22b-fp8", + "name": "Qwen3 235B A22B", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": false, "temperature": true, - "release_date": "2025-05-20", - "last_updated": "2025-05-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-04-29", + "last_updated": "2025-04-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.02, "output": 0.04 }, - "limit": { "context": 32768, "output": 6554 } + "limit": { + "context": 40960, + "output": 20000 + }, + "cost": { + "input": 0.2, + "output": 0.8 + } }, - "google/gemini-2.5-pro-preview-05-06": { - "id": "google/gemini-2.5-pro-preview-05-06", - "name": "Google: Gemini 2.5 Pro Preview 05-06", - "attachment": true, + "qwen/qwen3-8b-fp8": { + "id": "qwen/qwen3-8b-fp8", + "name": "Qwen3 8B", + "attachment": false, "reasoning": true, - "tool_call": true, + "tool_call": false, "temperature": true, - "release_date": "2025-05-06", - "last_updated": "2026-03-15", - "modalities": { "input": ["audio", "image", "pdf", "text", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "reasoning": 10, "cache_read": 0.125, "cache_write": 0.375 }, - "limit": { "context": 1048576, "output": 65535 } + "release_date": "2025-04-29", + "last_updated": "2025-04-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 20000 + }, + "cost": { + "input": 0.035, + "output": 0.138 + } }, - "google/gemini-2.0-flash-001": { - "id": "google/gemini-2.0-flash-001", - "name": "Google: Gemini 2.0 Flash", + "qwen/qwen3-vl-235b-a22b-instruct": { + "id": "qwen/qwen3-vl-235b-a22b-instruct", + "name": "Qwen3 VL 235B A22B Instruct", "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-12-11", - "last_updated": "2026-03-15", - "modalities": { "input": ["audio", "image", "pdf", "text", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.025, "cache_write": 0.083333 }, - "limit": { "context": 1048576, "output": 8192 } + "release_date": "2025-09-24", + "last_updated": "2025-09-24", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.3, + "output": 1.5 + } }, - "google/gemini-2.0-flash-lite-001": { - "id": "google/gemini-2.0-flash-lite-001", - "name": "Google: Gemini 2.0 Flash Lite", - "attachment": true, + "qwen/qwen-2.5-72b-instruct": { + "id": "qwen/qwen-2.5-72b-instruct", + "name": "Qwen 2.5 72B Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-12-11", - "last_updated": "2026-03-15", - "modalities": { "input": ["audio", "image", "pdf", "text", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.075, "output": 0.3 }, - "limit": { "context": 1048576, "output": 8192 } - }, - "google/gemini-3-flash-preview": { - "id": "google/gemini-3-flash-preview", - "name": "Google: Gemini 3 Flash Preview", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-12-17", - "last_updated": "2026-03-15", - "modalities": { "input": ["audio", "image", "pdf", "text", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 3, "reasoning": 3, "cache_read": 0.05, "cache_write": 0.083333 }, - "limit": { "context": 1048576, "output": 65536 } - }, - "google/gemini-2.5-pro-preview": { - "id": "google/gemini-2.5-pro-preview", - "name": "Google: Gemini 2.5 Pro Preview 06-05", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-06-05", - "last_updated": "2026-03-15", - "modalities": { "input": ["audio", "image", "pdf", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "reasoning": 10, "cache_read": 0.125, "cache_write": 0.375 }, - "limit": { "context": 1048576, "output": 65536 } - }, - "google/gemini-2.5-pro": { - "id": "google/gemini-2.5-pro", - "name": "Google: Gemini 2.5 Pro", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-03-20", - "last_updated": "2026-03-15", - "modalities": { "input": ["audio", "image", "pdf", "text", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "reasoning": 10, "cache_read": 0.125, "cache_write": 0.375 }, - "limit": { "context": 1048576, "output": 65536 } - }, - "google/gemini-2.5-flash": { - "id": "google/gemini-2.5-flash", - "name": "Google: Gemini 2.5 Flash", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-07-17", - "last_updated": "2026-03-15", - "modalities": { "input": ["audio", "image", "pdf", "text", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.5, "reasoning": 2.5, "cache_read": 0.03, "cache_write": 0.083333 }, - "limit": { "context": 1048576, "output": 65535 } + "knowledge": "2024-04", + "release_date": "2024-10-15", + "last_updated": "2024-10-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32000, + "output": 8192 + }, + "cost": { + "input": 0.38, + "output": 0.4 + } }, - "google/gemini-3.1-pro-preview-customtools": { - "id": "google/gemini-3.1-pro-preview-customtools", - "name": "Google: Gemini 3.1 Pro Preview Custom Tools", + "qwen/qwen3.5-35b-a3b": { + "id": "qwen/qwen3.5-35b-a3b", + "name": "Qwen3.5-35B-A3B", + "family": "qwen", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, "release_date": "2026-02-26", - "last_updated": "2026-03-15", - "modalities": { "input": ["audio", "image", "pdf", "text", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 12, "reasoning": 12 }, - "limit": { "context": 1048576, "output": 65536 } + "last_updated": "2026-02-26", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.25, + "output": 2 + } }, - "google/gemma-2-27b-it": { - "id": "google/gemma-2-27b-it", - "name": "Google: Gemma 2 27B", + "kwaipilot/kat-coder-pro": { + "id": "kwaipilot/kat-coder-pro", + "name": "Kat Coder Pro", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-06-24", - "last_updated": "2024-06-24", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01-05", + "last_updated": "2026-01-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.65, "output": 0.65 }, - "limit": { "context": 8192, "output": 2048 } + "limit": { + "context": 256000, + "output": 128000 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06 + } }, - "google/gemini-3.1-flash-lite-preview": { - "id": "google/gemini-3.1-flash-lite-preview", - "name": "Google: Gemini 3.1 Flash Lite Preview", + "google/gemma-4-31b-it": { + "id": "google/gemma-4-31b-it", + "name": "Gemma 4 31B", + "family": "gemma", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-03-03", - "last_updated": "2026-03-15", - "modalities": { "input": ["audio", "image", "pdf", "text", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 1.5, "reasoning": 1.5 }, - "limit": { "context": 1048576, "output": 65536 } - }, - "google/gemini-3.1-flash-image-preview": { - "id": "google/gemini-3.1-flash-image-preview", - "name": "Google: Nano Banana 2 (Gemini 3.1 Flash Image Preview)", - "attachment": true, - "reasoning": true, - "tool_call": false, - "temperature": true, - "release_date": "2026-02-26", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["image", "text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 3 }, - "limit": { "context": 65536, "output": 65536 } - }, - "google/gemini-2.5-flash-image": { - "id": "google/gemini-2.5-flash-image", - "name": "Google: Nano Banana (Gemini 2.5 Flash Image)", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2025-10-08", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["image", "text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.5 }, - "limit": { "context": 32768, "output": 32768 } + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0.14, + "output": 0.4 + } }, "google/gemma-3-12b-it": { "id": "google/gemma-3-12b-it", - "name": "Google: Gemma 3 12B", + "name": "Gemma 3 12B", + "family": "gemma", "attachment": true, "reasoning": false, "tool_call": false, "temperature": true, "release_date": "2025-03-13", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "last_updated": "2025-03-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.04, "output": 0.13, "cache_read": 0.015 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.05, + "output": 0.1 + } }, - "google/gemini-3-pro-image-preview": { - "id": "google/gemini-3-pro-image-preview", - "name": "Google: Nano Banana Pro (Gemini 3 Pro Image Preview)", + "google/gemma-3-27b-it": { + "id": "google/gemma-3-27b-it", + "name": "Gemma 3 27B", + "family": "gemma", "attachment": true, - "reasoning": true, - "tool_call": false, - "temperature": true, - "release_date": "2025-11-20", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["image", "text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 12, "reasoning": 12 }, - "limit": { "context": 65536, "output": 32768 } - }, - "undi95/remm-slerp-l2-13b": { - "id": "undi95/remm-slerp-l2-13b", - "name": "ReMM SLERP 13B", - "attachment": false, "reasoning": false, "tool_call": false, "temperature": true, - "release_date": "2023-07-22", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-03-25", + "last_updated": "2025-03-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.45, "output": 0.65 }, - "limit": { "context": 6144, "output": 4096 } + "limit": { + "context": 98304, + "output": 16384 + }, + "cost": { + "input": 0.119, + "output": 0.2 + } }, - "amazon/nova-lite-v1": { - "id": "amazon/nova-lite-v1", - "name": "Amazon: Nova Lite 1.0", + "google/gemma-4-26b-a4b-it": { + "id": "google/gemma-4-26b-a4b-it", + "name": "Gemma 4 26B A4B", + "family": "gemma", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-12-06", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.06, "output": 0.24 }, - "limit": { "context": 300000, "output": 5120 } + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0.13, + "output": 0.4 + } }, - "amazon/nova-2-lite-v1": { - "id": "amazon/nova-2-lite-v1", - "name": "Amazon: Nova 2 Lite", + "moonshotai/kimi-k2.5": { + "id": "moonshotai/kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi", "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2024-12-01", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "pdf", "text", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.5 }, - "limit": { "context": 1000000, "output": 65535 } + "knowledge": "2025-01", + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 3, + "cache_read": 0.1 + } }, - "amazon/nova-micro-v1": { - "id": "amazon/nova-micro-v1", - "name": "Amazon: Nova Micro 1.0", + "moonshotai/kimi-k2-instruct": { + "id": "moonshotai/kimi-k2-instruct", + "name": "Kimi K2 Instruct", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-12-06", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.035, "output": 0.14 }, - "limit": { "context": 128000, "output": 5120 } - }, - "amazon/nova-pro-v1": { - "id": "amazon/nova-pro-v1", - "name": "Amazon: Nova Pro 1.0", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2024-12-03", - "last_updated": "2024-12-03", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.8, "output": 3.2 }, - "limit": { "context": 300000, "output": 5120 } - }, - "amazon/nova-premier-v1": { - "id": "amazon/nova-premier-v1", - "name": "Amazon: Nova Premier 1.0", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2025-11-01", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 12.5 }, - "limit": { "context": 1000000, "output": 32000 } + "release_date": "2025-07-11", + "last_updated": "2025-07-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.57, + "output": 2.3 + } }, - "baidu/ernie-4.5-21b-a3b": { - "id": "baidu/ernie-4.5-21b-a3b", - "name": "Baidu: ERNIE 4.5 21B A3B", + "moonshotai/kimi-k2-0905": { + "id": "moonshotai/kimi-k2-0905", + "name": "Kimi K2 0905", + "family": "kimi", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-06-30", - "last_updated": "2025-06-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.07, "output": 0.28 }, - "limit": { "context": 120000, "output": 8000 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 2.5 + } }, - "baidu/ernie-4.5-300b-a47b": { - "id": "baidu/ernie-4.5-300b-a47b", - "name": "Baidu: ERNIE 4.5 300B A47B ", + "moonshotai/kimi-k2-thinking": { + "id": "moonshotai/kimi-k2-thinking", + "name": "Kimi K2 Thinking", + "family": "kimi", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-06-30", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-11-07", + "last_updated": "2025-11-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.28, "output": 1.1 }, - "limit": { "context": 123000, "output": 12000 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 2.5 + } }, - "baidu/ernie-4.5-21b-a3b-thinking": { - "id": "baidu/ernie-4.5-21b-a3b-thinking", - "name": "Baidu: ERNIE 4.5 21B A3B Thinking", + "deepseek/deepseek-v4-flash": { + "id": "deepseek/deepseek-v4-flash", + "name": "DeepSeek V4 Flash", + "family": "deepseek-flash", "attachment": false, "reasoning": true, - "tool_call": false, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-09-19", - "last_updated": "2025-09-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.07, "output": 0.28 }, - "limit": { "context": 131072, "output": 65536 } + "limit": { + "context": 1048576, + "output": 393216 + }, + "cost": { + "input": 0.14, + "output": 0.28, + "cache_read": 0.028 + } }, - "baidu/ernie-4.5-vl-28b-a3b": { - "id": "baidu/ernie-4.5-vl-28b-a3b", - "name": "Baidu: ERNIE 4.5 VL 28B A3B", - "attachment": true, + "deepseek/deepseek-v4-pro": { + "id": "deepseek/deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-06-30", - "last_updated": "2025-06-30", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.14, "output": 0.56 }, - "limit": { "context": 30000, "output": 8000 } + "limit": { + "context": 1048576, + "output": 393216 + }, + "cost": { + "input": 1.69, + "output": 3.38, + "cache_read": 0.13 + } }, - "baidu/ernie-4.5-vl-424b-a47b": { - "id": "baidu/ernie-4.5-vl-424b-a47b", - "name": "Baidu: ERNIE 4.5 VL 424B A47B ", + "moonshotai/kimi-k2.6": { + "id": "moonshotai/kimi-k2.6", + "name": "Kimi K2.6", + "family": "kimi-k2.6", "attachment": true, "reasoning": true, - "tool_call": false, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-06-30", - "last_updated": "2026-01", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.42, "output": 1.25 }, - "limit": { "context": 123000, "output": 16000 } - }, - "ibm-granite/granite-4.0-h-micro": { - "id": "ibm-granite/granite-4.0-h-micro", - "name": "IBM: Granite 4.0 Micro", - "attachment": false, - "reasoning": false, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.95, + "output": 4, + "cache_read": 0.16 + } + } + } + }, + "xiaomi-token-plan-cn": { + "id": "xiaomi-token-plan-cn", + "env": ["XIAOMI_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://token-plan-cn.xiaomimimo.com/v1", + "name": "Xiaomi Token Plan (China)", + "doc": "https://platform.xiaomimimo.com/#/docs", + "models": { + "mimo-v2-tts": { + "id": "mimo-v2-tts", + "name": "MiMo-V2-TTS", + "family": "mimo", + "attachment": false, + "reasoning": false, "tool_call": false, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["audio"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "mimo-v2.5-pro": { + "id": "mimo-v2.5-pro", + "name": "MiMo-V2.5-Pro", + "family": "mimo", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-10-20", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.017, "output": 0.11 }, - "limit": { "context": 131000, "output": 32768 } + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } }, - "kilo/auto": { - "id": "kilo/auto", - "name": "Kilo: Auto", + "mimo-v2-omni": { + "id": "mimo-v2-omni", + "name": "MiMo-V2-Omni", + "family": "mimo", "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2024-06-01", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25 }, - "limit": { "context": 1000000, "output": 128000 } + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } }, - "kilo/auto-free": { - "id": "kilo/auto-free", - "name": "Deprecated Kilo Auto Free", - "attachment": false, + "mimo-v2.5": { + "id": "mimo-v2.5", + "name": "MiMo-V2.5", + "family": "mimo", + "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2026-03-15", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 204800, "output": 131072 } + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } }, - "kilo/auto-small": { - "id": "kilo/auto-small", - "name": "Deprecated Kilo Auto Small", - "attachment": true, + "mimo-v2-pro": { + "id": "mimo-v2-pro", + "name": "MiMo-V2-Pro", + "family": "mimo", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2026-03-15", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.05, "output": 0.4 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } }, - "meta-llama/llama-3.3-70b-instruct": { - "id": "meta-llama/llama-3.3-70b-instruct", - "name": "Meta: Llama 3.3 70B Instruct", + "mimo-v2-flash": { + "id": "mimo-v2-flash", + "name": "MiMo-V2-Flash", + "family": "mimo", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2024-08-01", + "knowledge": "2024-12-01", + "release_date": "2025-12-16", "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.32 }, - "limit": { "context": 131072, "output": 16384 } - }, - "meta-llama/llama-3.2-1b-instruct": { - "id": "meta-llama/llama-3.2-1b-instruct", - "name": "Meta: Llama 3.2 1B Instruct", + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + } + } + }, + "wandb": { + "id": "wandb", + "env": ["WANDB_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.inference.wandb.ai/v1", + "name": "Weights & Biases", + "doc": "https://docs.wandb.ai/guides/integrations/inference/", + "models": { + "Qwen/Qwen3-30B-A3B-Instruct-2507": { + "id": "Qwen/Qwen3-30B-A3B-Instruct-2507", + "name": "Qwen3 30B A3B Instruct 2507", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-09-18", - "last_updated": "2026-01-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-07-29", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.027, "output": 0.2 }, - "limit": { "context": 60000, "output": 12000 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } }, - "meta-llama/llama-guard-4-12b": { - "id": "meta-llama/llama-guard-4-12b", - "name": "Meta: Llama Guard 4 12B", - "attachment": true, + "Qwen/Qwen3-235B-A22B-Instruct-2507": { + "id": "Qwen/Qwen3-235B-A22B-Instruct-2507", + "name": "Qwen3 235B A22B Instruct 2507", + "family": "qwen", + "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04-28", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.18, "output": 0.18 }, - "limit": { "context": 163840, "output": 32768 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "meta-llama/llama-3.1-405b-instruct": { - "id": "meta-llama/llama-3.1-405b-instruct", - "name": "Meta: Llama 3.1 405B Instruct", + "Qwen/Qwen3-235B-A22B-Thinking-2507": { + "id": "Qwen/Qwen3-235B-A22B-Thinking-2507", + "name": "Qwen3-235B-A22B-Thinking-2507", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-07-16", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-25", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 4, "output": 4 }, - "limit": { "context": 131000, "output": 26200 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "meta-llama/llama-3.1-8b-instruct": { - "id": "meta-llama/llama-3.1-8b-instruct", - "name": "Meta: Llama 3.1 8B Instruct", + "Qwen/Qwen3-Coder-480B-A35B-Instruct": { + "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct", + "name": "Qwen3-Coder-480B-A35B-Instruct", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-07-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.02, "output": 0.05 }, - "limit": { "context": 16384, "output": 16384 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 1, + "output": 1.5 + } }, - "meta-llama/llama-3.2-11b-vision-instruct": { - "id": "meta-llama/llama-3.2-11b-vision-instruct", - "name": "Meta: Llama 3.2 11B Vision Instruct", - "attachment": true, + "zai-org/GLM-5-FP8": { + "id": "zai-org/GLM-5-FP8", + "name": "GLM 5", + "family": "glm", + "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-02-11", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.049, "output": 0.049 }, - "limit": { "context": 131072, "output": 16384 } + "limit": { + "context": 200000, + "output": 200000 + }, + "cost": { + "input": 1, + "output": 3.2 + } }, - "meta-llama/llama-4-scout": { - "id": "meta-llama/llama-4-scout", - "name": "Meta: Llama 4 Scout", - "attachment": true, - "reasoning": false, + "meta-llama/Llama-4-Scout-17B-16E-Instruct": { + "id": "meta-llama/Llama-4-Scout-17B-16E-Instruct", + "name": "Llama 4 Scout 17B 16E Instruct", + "family": "llama", + "attachment": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2025-01-31", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.08, "output": 0.3 }, - "limit": { "context": 327680, "output": 16384 } + "limit": { + "context": 64000, + "output": 64000 + }, + "cost": { + "input": 0.17, + "output": 0.66 + } }, - "meta-llama/llama-3-8b-instruct": { - "id": "meta-llama/llama-3-8b-instruct", - "name": "Meta: Llama 3 8B Instruct", + "meta-llama/Llama-3.3-70B-Instruct": { + "id": "meta-llama/Llama-3.3-70B-Instruct", + "name": "Llama-3.3-70B-Instruct", + "family": "llama", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-04-25", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2024-12-06", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.03, "output": 0.04 }, - "limit": { "context": 8192, "output": 16384 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.71, + "output": 0.71 + } }, - "meta-llama/llama-3-70b-instruct": { - "id": "meta-llama/llama-3-70b-instruct", - "name": "Meta: Llama 3 70B Instruct", + "meta-llama/Llama-3.1-8B-Instruct": { + "id": "meta-llama/Llama-3.1-8B-Instruct", + "name": "Meta-Llama-3.1-8B-Instruct", + "family": "llama", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": true, + "knowledge": "2023-12", "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.51, "output": 0.74 }, - "limit": { "context": 8192, "output": 8000 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.22, + "output": 0.22 + } }, - "meta-llama/llama-4-maverick": { - "id": "meta-llama/llama-4-maverick", - "name": "Meta: Llama 4 Maverick", - "attachment": true, + "meta-llama/Llama-3.1-70B-Instruct": { + "id": "meta-llama/Llama-3.1-70B-Instruct", + "name": "Llama 3.1 70B", + "family": "llama", + "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-04-05", - "last_updated": "2025-12-24", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2024-07-23", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 1048576, "output": 16384 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.8, + "output": 0.8 + } }, - "meta-llama/llama-guard-3-8b": { - "id": "meta-llama/llama-guard-3-8b", - "name": "Llama Guard 3 8B", + "OpenPipe/Qwen3-14B-Instruct": { + "id": "OpenPipe/Qwen3-14B-Instruct", + "name": "OpenPipe Qwen3 14B Instruct", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-04-18", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-04-29", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.02, "output": 0.06 }, - "limit": { "context": 131072, "output": 26215 } + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.05, + "output": 0.22 + } }, - "meta-llama/llama-3.2-3b-instruct": { - "id": "meta-llama/llama-3.2-3b-instruct", - "name": "Meta: Llama 3.2 3B Instruct", + "microsoft/Phi-4-mini-instruct": { + "id": "microsoft/Phi-4-mini-instruct", + "name": "Phi-4-mini-instruct", + "family": "phi", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-09-18", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-10", + "release_date": "2024-12-11", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.051, "output": 0.34 }, - "limit": { "context": 80000, "output": 16384 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.08, + "output": 0.35 + } }, - "meta-llama/llama-3.1-70b-instruct": { - "id": "meta-llama/llama-3.1-70b-instruct", - "name": "Meta: Llama 3.1 70B Instruct", + "nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-FP8": { + "id": "nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-FP8", + "name": "NVIDIA Nemotron 3 Super 120B", + "family": "nemotron", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-07-16", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-11", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.4, "output": 0.4 }, - "limit": { "context": 131072, "output": 26215 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.2, + "output": 0.8 + } }, - "meta-llama/llama-3.1-405b": { - "id": "meta-llama/llama-3.1-405b", - "name": "Meta: Llama 3.1 405B (base)", + "deepseek-ai/DeepSeek-V3.1": { + "id": "deepseek-ai/DeepSeek-V3.1", + "name": "DeepSeek V3.1", + "family": "deepseek", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-08-02", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-08-21", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 4, "output": 4 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 161000, + "output": 161000 + }, + "cost": { + "input": 0.55, + "output": 1.65 + } }, - "openrouter/hunter-alpha": { - "id": "openrouter/hunter-alpha", - "name": "Hunter Alpha", + "openai/gpt-oss-20b": { + "id": "openai/gpt-oss-20b", + "name": "gpt-oss-20b", + "family": "gpt-oss", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2026-03-12", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 1048576, "output": 32000 } - }, - "openrouter/free": { - "id": "openrouter/free", - "name": "Free Models Router", - "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-02-01", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "release_date": "2025-08-05", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 200000, "output": 32768 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.05, + "output": 0.2 + } }, - "openrouter/bodybuilder": { - "id": "openrouter/bodybuilder", - "name": "Body Builder (beta)", + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "name": "gpt-oss-120b", + "family": "gpt-oss", "attachment": false, "reasoning": false, - "tool_call": false, - "release_date": "2026-03-15", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 32768 }, - "status": "beta" - }, - "openrouter/auto": { - "id": "openrouter/auto", - "name": "Auto Router", - "attachment": true, - "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-03-15", - "last_updated": "2026-03-15", - "modalities": { "input": ["audio", "image", "pdf", "text", "video"], "output": ["image", "text"] }, + "release_date": "2025-08-05", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 2000000, "output": 32768 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "openrouter/healer-alpha": { - "id": "openrouter/healer-alpha", - "name": "Healer Alpha", + "moonshotai/Kimi-K2.5": { + "id": "moonshotai/Kimi-K2.5", + "name": "Kimi K2.5", + "family": "kimi", "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2026-03-12", - "last_updated": "2026-03-15", - "modalities": { "input": ["audio", "image", "text", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 32000 } + "release_date": "2026-01-27", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.5, + "output": 2.85 + } }, - "perplexity/sonar-deep-research": { - "id": "perplexity/sonar-deep-research", - "name": "Perplexity: Sonar Deep Research", + "MiniMaxAI/MiniMax-M2.5": { + "id": "MiniMaxAI/MiniMax-M2.5", + "name": "MiniMax M2.5", + "family": "minimax", "attachment": false, - "reasoning": true, - "tool_call": false, - "temperature": true, - "release_date": "2025-01-27", - "last_updated": "2025-01-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 8 }, - "limit": { "context": 128000, "output": 25600 } - }, - "perplexity/sonar": { - "id": "perplexity/sonar", - "name": "Perplexity: Sonar", - "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-01-01", - "last_updated": "2025-09-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1, "output": 1 }, - "limit": { "context": 127072, "output": 25415 } + "release_date": "2026-02-12", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 196608, + "output": 196608 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "perplexity/sonar-reasoning-pro": { - "id": "perplexity/sonar-reasoning-pro", - "name": "Perplexity: Sonar Reasoning Pro", - "attachment": true, + "zai-org/GLM-5.1": { + "id": "zai-org/GLM-5.1", + "name": "GLM-5.1", + "family": "glm", + "attachment": false, "reasoning": true, - "tool_call": false, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2024-01-01", - "last_updated": "2025-09-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-03-27", + "last_updated": "2026-03-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8 }, - "limit": { "context": 128000, "output": 25600 } - }, - "perplexity/sonar-pro": { - "id": "perplexity/sonar-pro", - "name": "Perplexity: Sonar Pro", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2024-01-01", - "last_updated": "2025-09-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 200000, "output": 8000 } - }, - "perplexity/sonar-pro-search": { - "id": "perplexity/sonar-pro-search", - "name": "Perplexity: Sonar Pro Search", - "attachment": true, - "reasoning": true, - "tool_call": false, - "temperature": true, - "release_date": "2025-10-31", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 200000, "output": 8000 } - }, - "essentialai/rnj-1-instruct": { - "id": "essentialai/rnj-1-instruct", - "name": "EssentialAI: Rnj 1 Instruct", - "attachment": false, + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 1.4, + "output": 4.4, + "cache_read": 0.26, + "cache_write": 0 + } + } + } + }, + "chutes": { + "id": "chutes", + "env": ["CHUTES_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://llm.chutes.ai/v1", + "name": "Chutes", + "doc": "https://llm.chutes.ai/v1/models", + "models": { + "NousResearch/DeepHermes-3-Mistral-24B-Preview": { + "id": "NousResearch/DeepHermes-3-Mistral-24B-Preview", + "name": "DeepHermes 3 Mistral 24B Preview", + "family": "nousresearch", + "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-12-05", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.15 }, - "limit": { "context": 32768, "output": 6554 } + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.0245, + "output": 0.0978, + "cache_read": 0.01225 + } }, - "arcee-ai/coder-large": { - "id": "arcee-ai/coder-large", - "name": "Arcee AI: Coder Large", + "NousResearch/Hermes-4-14B": { + "id": "NousResearch/Hermes-4-14B", + "name": "Hermes 4 14B", + "family": "nousresearch", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-05-06", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.5, "output": 0.8 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 40960, + "output": 40960 + }, + "cost": { + "input": 0.0136, + "output": 0.0543, + "cache_read": 0.0068 + } }, - "arcee-ai/trinity-large-preview:free": { - "id": "arcee-ai/trinity-large-preview:free", - "name": "Arcee AI: Trinity Large Preview (free)", + "Qwen/Qwen3-30B-A3B": { + "id": "Qwen/Qwen3-30B-A3B", + "name": "Qwen3 30B A3B", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2026-01-28", - "last_updated": "2026-01-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131000, "output": 26200 } + "limit": { + "context": 40960, + "output": 40960 + }, + "cost": { + "input": 0.06, + "output": 0.22, + "cache_read": 0.03 + } }, - "arcee-ai/virtuoso-large": { - "id": "arcee-ai/virtuoso-large", - "name": "Arcee AI: Virtuoso Large", + "Qwen/Qwen3-32B-TEE": { + "id": "Qwen/Qwen3-32B-TEE", + "name": "Qwen3 32B TEE", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-05-06", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-25", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.75, "output": 1.2 }, - "limit": { "context": 131072, "output": 64000 } + "limit": { + "context": 40960, + "output": 40960 + }, + "cost": { + "input": 0.08, + "output": 0.24, + "cache_read": 0.04 + } }, - "arcee-ai/trinity-mini": { - "id": "arcee-ai/trinity-mini", - "name": "Arcee AI: Trinity Mini", - "attachment": false, + "Qwen/Qwen3.6-27B-TEE": { + "id": "Qwen/Qwen3.6-27B-TEE", + "name": "Qwen3.6 27B TEE", + "family": "qwen", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-12", - "last_updated": "2026-01-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-25", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.045, "output": 0.15 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.195, + "output": 1.56, + "cache_read": 0.0975 + } }, - "arcee-ai/spotlight": { - "id": "arcee-ai/spotlight", - "name": "Arcee AI: Spotlight", - "attachment": true, + "Qwen/Qwen2.5-Coder-32B-Instruct": { + "id": "Qwen/Qwen2.5-Coder-32B-Instruct", + "name": "Qwen2.5 Coder 32B Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": false, + "structured_output": true, "temperature": true, - "release_date": "2025-05-06", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.18, "output": 0.18 }, - "limit": { "context": 131072, "output": 65537 } + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.0272, + "output": 0.1087, + "cache_read": 0.0136 + } }, - "arcee-ai/maestro-reasoning": { - "id": "arcee-ai/maestro-reasoning", - "name": "Arcee AI: Maestro Reasoning", + "Qwen/Qwen3Guard-Gen-0.6B": { + "id": "Qwen/Qwen3Guard-Gen-0.6B", + "name": "Qwen3Guard Gen 0.6B", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": false, "temperature": true, - "release_date": "2025-05-06", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.9, "output": 3.3 }, - "limit": { "context": 131072, "output": 32000 } + "limit": { + "context": 32768, + "output": 8192 + }, + "cost": { + "input": 0.01, + "output": 0.0109, + "cache_read": 0.005 + } }, - "bytedance/ui-tars-1.5-7b": { - "id": "bytedance/ui-tars-1.5-7b", - "name": "ByteDance: UI-TARS 7B ", - "attachment": true, + "Qwen/Qwen3-Next-80B-A3B-Instruct": { + "id": "Qwen/Qwen3-Next-80B-A3B-Instruct", + "name": "Qwen3 Next 80B A3B Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-07-23", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.2 }, - "limit": { "context": 128000, "output": 2048 } + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.1, + "output": 0.8, + "cache_read": 0.05 + } }, - "kilo-auto/free": { - "id": "kilo-auto/free", - "name": "Kilo Auto Free", + "Qwen/Qwen3-235B-A22B-Instruct-2507-TEE": { + "id": "Qwen/Qwen3-235B-A22B-Instruct-2507-TEE", + "name": "Qwen3 235B A22B Instruct 2507 TEE", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-03-15", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 204800, "output": 131072 } + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.6, + "cache_read": 0.05 + } }, - "kilo-auto/balanced": { - "id": "kilo-auto/balanced", - "name": "Kilo Auto Balanced", + "Qwen/Qwen3-Coder-Next-TEE": { + "id": "Qwen/Qwen3-Coder-Next-TEE", + "name": "Qwen3 Coder Next TEE", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-03-15", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.6, "output": 3 }, - "limit": { "context": 204800, "output": 131072 } + "release_date": "2026-04-25", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.12, + "output": 0.75, + "cache_read": 0.06 + } }, - "kilo-auto/small": { - "id": "kilo-auto/small", - "name": "Kilo Auto Small", - "attachment": true, + "Qwen/Qwen3-235B-A22B-Thinking-2507": { + "id": "Qwen/Qwen3-235B-A22B-Thinking-2507", + "name": "Qwen3 235B A22B Thinking 2507", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2026-03-15", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.05, "output": 0.4 }, - "limit": { "context": 400000, "output": 128000 } + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.11, + "output": 0.6, + "cache_read": 0.055 + } }, - "kilo-auto/frontier": { - "id": "kilo-auto/frontier", - "name": "Kilo Auto Frontier", + "Qwen/Qwen2.5-VL-32B-Instruct": { + "id": "Qwen/Qwen2.5-VL-32B-Instruct", + "name": "Qwen2.5 VL 32B Instruct", + "family": "qwen", "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2026-03-15", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5, "output": 25 }, - "limit": { "context": 1000000, "output": 128000 } - }, - "nousresearch/hermes-2-pro-llama-3-8b": { - "id": "nousresearch/hermes-2-pro-llama-3-8b", - "name": "NousResearch: Hermes 2 Pro - Llama-3 8B", - "attachment": false, "reasoning": false, "tool_call": false, + "structured_output": true, "temperature": true, - "release_date": "2024-05-27", - "last_updated": "2024-06-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.14, "output": 0.14 }, - "limit": { "context": 8192, "output": 8192 } + "limit": { + "context": 16384, + "output": 16384 + }, + "cost": { + "input": 0.0543, + "output": 0.2174, + "cache_read": 0.02715 + } }, - "nousresearch/hermes-4-70b": { - "id": "nousresearch/hermes-4-70b", - "name": "Nous: Hermes 4 70B", - "attachment": false, + "Qwen/Qwen3.5-397B-A17B-TEE": { + "id": "Qwen/Qwen3.5-397B-A17B-TEE", + "name": "Qwen3.5 397B A17B TEE", + "family": "qwen", + "attachment": true, "reasoning": true, - "tool_call": false, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-08-25", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-18", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.13, "output": 0.4, "cache_read": 0.055 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.39, + "output": 2.34, + "cache_read": 0.195 + } }, - "nousresearch/hermes-3-llama-3.1-70b": { - "id": "nousresearch/hermes-3-llama-3.1-70b", - "name": "Nous: Hermes 3 70B Instruct", + "Qwen/Qwen2.5-72B-Instruct": { + "id": "Qwen/Qwen2.5-72B-Instruct", + "name": "Qwen2.5 72B Instruct", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-08-18", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 0.3 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.2989, + "output": 1.1957, + "cache_read": 0.14945 + } }, - "nousresearch/hermes-4-405b": { - "id": "nousresearch/hermes-4-405b", - "name": "Nous: Hermes 4 405B", + "zai-org/GLM-5.1-TEE": { + "id": "zai-org/GLM-5.1-TEE", + "name": "GLM 5.1 TEE", + "family": "glm", "attachment": false, "reasoning": true, - "tool_call": false, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-08-25", - "last_updated": "2025-08-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-08", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1, "output": 3 }, - "limit": { "context": 131072, "output": 26215 } + "limit": { + "context": 202752, + "output": 65535 + }, + "cost": { + "input": 1.05, + "output": 3.5, + "cache_read": 0.525 + } }, - "nousresearch/hermes-3-llama-3.1-405b": { - "id": "nousresearch/hermes-3-llama-3.1-405b", - "name": "Nous: Hermes 3 405B Instruct", + "zai-org/GLM-4.7-FP8": { + "id": "zai-org/GLM-4.7-FP8", + "name": "GLM 4.7 FP8", + "family": "glm", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-08-16", - "last_updated": "2024-08-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01-27", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1, "output": 1 }, - "limit": { "context": 131072, "output": 16384 } + "limit": { + "context": 202752, + "output": 65535 + }, + "cost": { + "input": 0.2989, + "output": 1.1957, + "cache_read": 0.14945 + } }, - "minimax/minimax-m2-her": { - "id": "minimax/minimax-m2-her", - "name": "MiniMax: MiniMax M2-her", + "zai-org/GLM-5-TEE": { + "id": "zai-org/GLM-5-TEE", + "name": "GLM 5 TEE", + "family": "glm", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2026-01-23", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-14", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 65536, "output": 2048 } + "limit": { + "context": 202752, + "output": 65535 + }, + "cost": { + "input": 0.95, + "output": 2.55, + "cache_read": 0.475 + } }, - "minimax/minimax-01": { - "id": "minimax/minimax-01", - "name": "MiniMax: MiniMax-01", - "attachment": true, - "reasoning": false, - "tool_call": false, + "zai-org/GLM-4.7-TEE": { + "id": "zai-org/GLM-4.7-TEE", + "name": "GLM 4.7 TEE", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-01-15", - "last_updated": "2025-01-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 1.1 }, - "limit": { "context": 1000192, "output": 1000192 } + "limit": { + "context": 202752, + "output": 65535 + }, + "cost": { + "input": 0.39, + "output": 1.75, + "cache_read": 0.195 + } }, - "minimax/minimax-m2": { - "id": "minimax/minimax-m2", - "name": "MiniMax: MiniMax M2", - "attachment": false, + "zai-org/GLM-4.6V": { + "id": "zai-org/GLM-4.6V", + "name": "GLM 4.6V", + "family": "glm", + "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-10-23", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.255, "output": 1, "cache_read": 0.03 }, - "limit": { "context": 196608, "output": 196608 } + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 0.9, + "cache_read": 0.15 + } }, - "minimax/minimax-m2.1": { - "id": "minimax/minimax-m2.1", - "name": "MiniMax: MiniMax M2.1", + "zai-org/GLM-5-Turbo": { + "id": "zai-org/GLM-5-Turbo", + "name": "GLM 5 Turbo", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-11", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.27, "output": 0.95, "cache_read": 0.03 }, - "limit": { "context": 196608, "output": 39322 } + "limit": { + "context": 202752, + "output": 65535 + }, + "cost": { + "input": 0.4891, + "output": 1.9565, + "cache_read": 0.24455 + } }, - "minimax/minimax-m2.5": { - "id": "minimax/minimax-m2.5", - "name": "MiniMax: MiniMax M2.5", + "XiaomiMiMo/MiMo-V2-Flash-TEE": { + "id": "XiaomiMiMo/MiMo-V2-Flash-TEE", + "name": "MiMo V2 Flash TEE", + "family": "mimo", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-25", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.25, "output": 1.2, "cache_read": 0.029 }, - "limit": { "context": 196608, "output": 196608 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.09, + "output": 0.29, + "cache_read": 0.045 + } }, - "minimax/minimax-m1": { - "id": "minimax/minimax-m1", - "name": "MiniMax: MiniMax M1", + "deepseek-ai/DeepSeek-R1-Distill-Llama-70B": { + "id": "deepseek-ai/DeepSeek-R1-Distill-Llama-70B", + "name": "DeepSeek R1 Distill Llama 70B", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.4, "output": 2.2 }, - "limit": { "context": 1000000, "output": 40000 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.0272, + "output": 0.1087, + "cache_read": 0.0136 + } }, - "minimax/minimax-m2.5:free": { - "id": "minimax/minimax-m2.5:free", - "name": "MiniMax: MiniMax M2.5 (free)", + "deepseek-ai/DeepSeek-V3.1-TEE": { + "id": "deepseek-ai/DeepSeek-V3.1-TEE", + "name": "DeepSeek V3.1 TEE", + "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 163840, + "output": 65536 + }, + "cost": { + "input": 0.27, + "output": 1, + "cache_read": 0.135 + } }, - "qwen/qwen3-coder-30b-a3b-instruct": { - "id": "qwen/qwen3-coder-30b-a3b-instruct", - "name": "Qwen: Qwen3 Coder 30B A3B Instruct", + "deepseek-ai/DeepSeek-V3-0324-TEE": { + "id": "deepseek-ai/DeepSeek-V3-0324-TEE", + "name": "DeepSeek V3 0324 TEE", + "family": "deepseek", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-07-31", - "last_updated": "2025-07-31", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.07, "output": 0.27 }, - "limit": { "context": 160000, "output": 32768 } + "limit": { + "context": 163840, + "output": 65536 + }, + "cost": { + "input": 0.25, + "output": 1, + "cache_read": 0.125 + } }, - "qwen/qwen3-8b": { - "id": "qwen/qwen3-8b", - "name": "Qwen: Qwen3 8B", + "deepseek-ai/DeepSeek-R1-0528-TEE": { + "id": "deepseek-ai/DeepSeek-R1-0528-TEE", + "name": "DeepSeek R1 0528 TEE", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-04", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.05, "output": 0.4, "cache_read": 0.05 }, - "limit": { "context": 40960, "output": 8192 } + "limit": { + "context": 163840, + "output": 65536 + }, + "cost": { + "input": 0.45, + "output": 2.15, + "cache_read": 0.225 + } }, - "qwen/qwen3.5-9b": { - "id": "qwen/qwen3.5-9b", - "name": "Qwen: Qwen3.5-9B", - "attachment": true, + "deepseek-ai/DeepSeek-V3.2-TEE": { + "id": "deepseek-ai/DeepSeek-V3.2-TEE", + "name": "DeepSeek V3.2 TEE", + "family": "deepseek", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2026-03-10", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text", "video"], "output": ["text"] }, + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.05, "output": 0.15 }, - "limit": { "context": 256000, "output": 32768 } + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.28, + "output": 0.42, + "cache_read": 0.14 + } }, - "qwen/qwen-turbo": { - "id": "qwen/qwen-turbo", - "name": "Qwen: Qwen-Turbo", + "openai/gpt-oss-120b-TEE": { + "id": "openai/gpt-oss-120b-TEE", + "name": "gpt oss 120b TEE", + "family": "gpt-oss", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2024-11-01", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.0325, "output": 0.13, "cache_read": 0.01 }, - "limit": { "context": 131072, "output": 8192 } + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.09, + "output": 0.36, + "cache_read": 0.045 + } }, - "qwen/qwen-vl-max": { - "id": "qwen/qwen-vl-max", - "name": "Qwen: Qwen VL Max", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2024-04-08", - "last_updated": "2025-08-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.8, "output": 3.2 }, - "limit": { "context": 131072, "output": 32768 } - }, - "qwen/qwen3-vl-235b-a22b-instruct": { - "id": "qwen/qwen3-vl-235b-a22b-instruct", - "name": "Qwen: Qwen3 VL 235B A22B Instruct", + "unsloth/gemma-3-12b-it": { + "id": "unsloth/gemma-3-12b-it", + "name": "gemma 3 12b it", + "family": "unsloth", "attachment": true, "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2025-09-23", - "last_updated": "2026-01-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.88, "cache_read": 0.11 }, - "limit": { "context": 262144, "output": 52429 } - }, - "qwen/qwq-32b": { - "id": "qwen/qwq-32b", - "name": "Qwen: QwQ 32B", - "attachment": false, - "reasoning": true, - "tool_call": true, + "tool_call": false, "temperature": true, - "release_date": "2024-11-28", - "last_updated": "2025-04-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.4 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.03, + "output": 0.1, + "cache_read": 0.015 + } }, - "qwen/qwen3-coder-next": { - "id": "qwen/qwen3-coder-next", - "name": "Qwen: Qwen3 Coder Next", + "unsloth/Llama-3.2-3B-Instruct": { + "id": "unsloth/Llama-3.2-3B-Instruct", + "name": "Llama 3.2 3B Instruct", + "family": "unsloth", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "temperature": true, - "release_date": "2026-02-02", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-02-12", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.12, "output": 0.75, "cache_read": 0.035 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 16384, + "output": 16384 + }, + "cost": { + "input": 0.01, + "output": 0.0136, + "cache_read": 0.005 + } }, - "qwen/qwen-vl-plus": { - "id": "qwen/qwen-vl-plus", - "name": "Qwen: Qwen VL Plus", + "unsloth/gemma-3-4b-it": { + "id": "unsloth/gemma-3-4b-it", + "name": "gemma 3 4b it", + "family": "unsloth", "attachment": true, "reasoning": false, "tool_call": false, + "structured_output": true, "temperature": true, - "release_date": "2024-01-25", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1365, "output": 0.4095, "cache_read": 0.042 }, - "limit": { "context": 131072, "output": 8192 } - }, - "qwen/qwen-max": { - "id": "qwen/qwen-max", - "name": "Qwen: Qwen-Max ", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2024-04-03", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.04, "output": 4.16, "cache_read": 0.32 }, - "limit": { "context": 32768, "output": 8192 } - }, - "qwen/qwen3.5-flash-02-23": { - "id": "qwen/qwen3.5-flash-02-23", - "name": "Qwen: Qwen3.5-Flash", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2026-02-26", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text", "video"], "output": ["text"] }, + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 1000000, "output": 65536 } - }, - "qwen/qwen3-vl-32b-instruct": { - "id": "qwen/qwen3-vl-32b-instruct", - "name": "Qwen: Qwen3 VL 32B Instruct", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2025-10-21", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.104, "output": 0.416 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 96000, + "output": 96000 + }, + "cost": { + "input": 0.01, + "output": 0.0272, + "cache_read": 0.005 + } }, - "qwen/qwen2.5-coder-7b-instruct": { - "id": "qwen/qwen2.5-coder-7b-instruct", - "name": "Qwen: Qwen2.5 Coder 7B Instruct", + "unsloth/Llama-3.2-1B-Instruct": { + "id": "unsloth/Llama-3.2-1B-Instruct", + "name": "Llama 3.2 1B Instruct", + "family": "unsloth", "attachment": false, "reasoning": false, "tool_call": false, "temperature": true, - "release_date": "2024-09-17", - "last_updated": "2024-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.03, "output": 0.09 }, - "limit": { "context": 32768, "output": 6554 } - }, - "qwen/qwen3-14b": { - "id": "qwen/qwen3-14b", - "name": "Qwen: Qwen3 14B", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-04", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01-27", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.06, "output": 0.24, "cache_read": 0.025 }, - "limit": { "context": 40960, "output": 40960 } + "limit": { + "context": 16384, + "output": 8192 + }, + "cost": { + "input": 0.01, + "output": 0.0109, + "cache_read": 0.005 + } }, - "qwen/qwen3-30b-a3b-thinking-2507": { - "id": "qwen/qwen3-30b-a3b-thinking-2507", - "name": "Qwen: Qwen3 30B A3B Thinking 2507", + "unsloth/Mistral-Nemo-Instruct-2407": { + "id": "unsloth/Mistral-Nemo-Instruct-2407", + "name": "Mistral Nemo Instruct 2407", + "family": "unsloth", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, + "structured_output": true, "temperature": true, - "release_date": "2025-07-29", - "last_updated": "2025-07-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.051, "output": 0.34 }, - "limit": { "context": 32768, "output": 6554 } - }, - "qwen/qwen3.5-plus-02-15": { - "id": "qwen/qwen3.5-plus-02-15", - "name": "Qwen: Qwen3.5 Plus 2026-02-15", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2026-02-15", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.26, "output": 1.56 }, - "limit": { "context": 1000000, "output": 65536 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.02, + "output": 0.04, + "cache_read": 0.01 + } }, - "qwen/qwen3.5-35b-a3b": { - "id": "qwen/qwen3.5-35b-a3b", - "name": "Qwen: Qwen3.5-35B-A3B", + "unsloth/gemma-3-27b-it": { + "id": "unsloth/gemma-3-27b-it", + "name": "gemma 3 27b it", + "family": "unsloth", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-02-26", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text", "video"], "output": ["text"] }, + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1625, "output": 1.3 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 128000, + "output": 65536 + }, + "cost": { + "input": 0.0272, + "output": 0.1087, + "cache_read": 0.0136 + } }, - "qwen/qwen3.5-122b-a10b": { - "id": "qwen/qwen3.5-122b-a10b", - "name": "Qwen: Qwen3.5-122B-A10B", + "google/gemma-4-31B-turbo-TEE": { + "id": "google/gemma-4-31B-turbo-TEE", + "name": "gemma 4 31B turbo TEE", + "family": "gemma", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-02-26", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.26, "output": 2.08 }, - "limit": { "context": 262144, "output": 65536 } - }, - "qwen/qwen3-30b-a3b": { - "id": "qwen/qwen3-30b-a3b", - "name": "Qwen: Qwen3 30B A3B", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-04", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-25", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.08, "output": 0.28, "cache_read": 0.03 }, - "limit": { "context": 40960, "output": 40960 } - }, - "qwen/qwen-plus": { - "id": "qwen/qwen-plus", - "name": "Qwen: Qwen-Plus", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2024-01-25", - "last_updated": "2025-09-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 1.2, "cache_read": 0.08 }, - "limit": { "context": 1000000, "output": 32768 } + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.13, + "output": 0.38, + "cache_read": 0.065 + } }, - "qwen/qwen3-vl-8b-thinking": { - "id": "qwen/qwen3-vl-8b-thinking", - "name": "Qwen: Qwen3 VL 8B Thinking", + "moonshotai/Kimi-K2.6-TEE": { + "id": "moonshotai/Kimi-K2.6-TEE", + "name": "Kimi K2.6 TEE", + "family": "kimi", "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-10-15", - "last_updated": "2025-11-25", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.117, "output": 1.365 }, - "limit": { "context": 131072, "output": 32768 } - }, - "qwen/qwen3-coder": { - "id": "qwen/qwen3-coder", - "name": "Qwen: Qwen3 Coder 480B A35B", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-12", + "release_date": "2026-04-20", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.22, "output": 1, "cache_read": 0.022 }, - "limit": { "context": 262144, "output": 52429 } + "limit": { + "context": 262144, + "output": 65535 + }, + "cost": { + "input": 0.95, + "output": 4, + "cache_read": 0.475 + } }, - "qwen/qwen3.5-27b": { - "id": "qwen/qwen3.5-27b", - "name": "Qwen: Qwen3.5-27B", + "moonshotai/Kimi-K2.5-TEE": { + "id": "moonshotai/Kimi-K2.5-TEE", + "name": "Kimi K2.5 TEE", + "family": "kimi", "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2026-02-26", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.195, "output": 1.56 }, - "limit": { "context": 262144, "output": 65536 } - }, - "qwen/qwen2.5-vl-32b-instruct": { - "id": "qwen/qwen2.5-vl-32b-instruct", - "name": "Qwen: Qwen2.5 VL 32B Instruct", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2025-03-24", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.6, "cache_read": 0.025 }, - "limit": { "context": 128000, "output": 16384 } - }, - "qwen/qwen-2.5-7b-instruct": { - "id": "qwen/qwen-2.5-7b-instruct", - "name": "Qwen: Qwen2.5 7B Instruct", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2024-09", - "last_updated": "2025-04-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2026-01-27", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.04, "output": 0.1 }, - "limit": { "context": 32768, "output": 6554 } + "limit": { + "context": 262144, + "output": 65535 + }, + "cost": { + "input": 0.44, + "output": 2, + "cache_read": 0.22 + } }, - "qwen/qwen-plus-2025-07-28:thinking": { - "id": "qwen/qwen-plus-2025-07-28:thinking", - "name": "Qwen: Qwen Plus 0728 (thinking)", + "MiniMaxAI/MiniMax-M2.5-TEE": { + "id": "MiniMaxAI/MiniMax-M2.5-TEE", + "name": "MiniMax M2.5 TEE", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-09-09", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-15", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.26, "output": 0.78 }, - "limit": { "context": 1000000, "output": 32768 } + "limit": { + "context": 196608, + "output": 65536 + }, + "cost": { + "input": 0.15, + "output": 1.2, + "cache_read": 0.075 + } }, - "qwen/qwen2.5-vl-72b-instruct": { - "id": "qwen/qwen2.5-vl-72b-instruct", - "name": "Qwen: Qwen2.5 VL 72B Instruct", + "rednote-hilab/dots.ocr": { + "id": "rednote-hilab/dots.ocr", + "name": "dots.ocr", + "family": "rednote", "attachment": true, "reasoning": false, "tool_call": false, + "structured_output": true, "temperature": true, - "release_date": "2025-02-01", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "release_date": "2025-12-29", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.8, "output": 0.8, "cache_read": 0.075 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.01, + "output": 0.0109, + "cache_read": 0.005 + } }, - "qwen/qwen3-235b-a22b-2507": { - "id": "qwen/qwen3-235b-a22b-2507", - "name": "Qwen: Qwen3 235B A22B Instruct 2507", + "tngtech/DeepSeek-TNG-R1T2-Chimera-TEE": { + "id": "tngtech/DeepSeek-TNG-R1T2-Chimera-TEE", + "name": "DeepSeek TNG R1T2 Chimera TEE", + "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-04", - "last_updated": "2026-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-25", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.071, "output": 0.1 }, - "limit": { "context": 262144, "output": 52429 } - }, - "qwen/qwen3-vl-8b-instruct": { - "id": "qwen/qwen3-vl-8b-instruct", - "name": "Qwen: Qwen3 VL 8B Instruct", - "attachment": true, + "limit": { + "context": 163840, + "output": 163840 + }, + "cost": { + "input": 0.3, + "output": 1.1, + "cache_read": 0.15 + } + } + } + }, + "dinference": { + "id": "dinference", + "env": ["DINFERENCE_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.dinference.com/v1", + "name": "DInference", + "doc": "https://dinference.com", + "models": { + "gpt-oss-120b": { + "id": "gpt-oss-120b", + "name": "GPT OSS 120B", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2025-10-15", - "last_updated": "2025-11-25", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "release_date": "2025-08", + "last_updated": "2025-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.08, "output": 0.5 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.0675, + "output": 0.27 + } }, - "qwen/qwen3-next-80b-a3b-thinking": { - "id": "qwen/qwen3-next-80b-a3b-thinking", - "name": "Qwen: Qwen3 Next 80B A3B Thinking", + "glm-4.7": { + "id": "glm-4.7", + "name": "GLM-4.7", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-09-11", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.0975, "output": 0.78 }, - "limit": { "context": 131072, "output": 32768 } - }, - "qwen/qwen3-vl-30b-a3b-instruct": { - "id": "qwen/qwen3-vl-30b-a3b-instruct", - "name": "Qwen: Qwen3 VL 30B A3B Instruct", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2025-10-05", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.13, "output": 0.52 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 0.45, + "output": 1.65 + } }, - "qwen/qwen3-235b-a22b-thinking-2507": { - "id": "qwen/qwen3-235b-a22b-thinking-2507", - "name": "Qwen: Qwen3 235B A22B Thinking 2507", + "glm-5": { + "id": "glm-5", + "name": "GLM-5", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-07-25", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.11, "output": 0.6 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 0.75, + "output": 2.4 + } }, - "qwen/qwen3-max-thinking": { - "id": "qwen/qwen3-max-thinking", - "name": "Qwen: Qwen3 Max Thinking", + "glm-5.1": { + "id": "glm-5.1", + "name": "GLM-5.1", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2026-01-23", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-27", + "last_updated": "2026-03-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.78, "output": 3.9 }, - "limit": { "context": 262144, "output": 32768 } + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 3.89 + } }, - "qwen/qwen3-next-80b-a3b-instruct": { - "id": "qwen/qwen3-next-80b-a3b-instruct", - "name": "Qwen: Qwen3 Next 80B A3B Instruct", + "minimax-m2.5": { + "id": "minimax-m2.5", + "name": "MiniMax-M2.5", + "family": "minimax", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-09-11", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.09, "output": 1.1 }, - "limit": { "context": 131072, "output": 52429 } - }, - "qwen/qwen3-235b-a22b": { - "id": "qwen/qwen3-235b-a22b", - "name": "Qwen: Qwen3 235B A22B", + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 0.22, + "output": 0.88 + } + } + } + }, + "vivgrid": { + "id": "vivgrid", + "env": ["VIVGRID_API_KEY"], + "npm": "@ai-sdk/openai", + "api": "https://api.vivgrid.com/v1", + "name": "Vivgrid", + "doc": "https://docs.vivgrid.com/models", + "models": { + "gpt-5.1-codex-max": { + "id": "gpt-5.1-codex-max", + "name": "GPT-5.1 Codex Max", + "family": "gpt-codex", "attachment": false, "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2024-12-01", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.455, "output": 1.82, "cache_read": 0.15 }, - "limit": { "context": 131072, "output": 8192 } + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "qwen/qwen3-vl-30b-a3b-thinking": { - "id": "qwen/qwen3-vl-30b-a3b-thinking", - "name": "Qwen: Qwen3 VL 30B A3B Thinking", + "gemini-3.1-flash-lite-preview": { + "id": "gemini-3.1-flash-lite-preview", + "name": "Gemini 3.1 Flash Lite Preview", + "family": "gemini-flash-lite", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-10-11", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.13, "output": 1.56 }, - "limit": { "context": 131072, "output": 32768 } - }, - "qwen/qwen-plus-2025-07-28": { - "id": "qwen/qwen-plus-2025-07-28", - "name": "Qwen: Qwen Plus 0728", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2025-09-09", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.26, "output": 0.78 }, - "limit": { "context": 1000000, "output": 32768 } + "knowledge": "2025-01", + "release_date": "2026-03-03", + "last_updated": "2026-03-03", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible" + }, + "cost": { + "input": 0.25, + "output": 1.5, + "cache_read": 0.025, + "cache_write": 1 + } }, - "qwen/qwen3.5-397b-a17b": { - "id": "qwen/qwen3.5-397b-a17b", - "name": "Qwen: Qwen3.5 397B A17B", + "gemini-3.1-pro-preview": { + "id": "gemini-3.1-pro-preview", + "name": "Gemini 3.1 Pro Preview", + "family": "gemini-pro", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-02-15", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text", "video"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-02-19", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.39, "output": 2.34 }, - "limit": { "context": 262144, "output": 65536 } - }, - "qwen/qwen3-coder-plus": { - "id": "qwen/qwen3-coder-plus", - "name": "Qwen: Qwen3 Coder Plus", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2025-07-01", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.65, "output": 3.25, "cache_read": 0.2 }, - "limit": { "context": 1000000, "output": 65536 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible" + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2, + "tiers": [ + { + "input": 4, + "output": 18, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 18, + "cache_read": 0.4 + } + } }, - "qwen/qwen3-max": { - "id": "qwen/qwen3-max", - "name": "Qwen: Qwen3 Max", - "attachment": false, - "reasoning": false, + "gpt-5-mini": { + "id": "gpt-5-mini", + "name": "GPT-5 Mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2025-09-05", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.2, "output": 6, "cache_read": 0.24 }, - "limit": { "context": 262144, "output": 32768 } + "limit": { + "context": 272000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible" + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.03 + } }, - "qwen/qwen3-32b": { - "id": "qwen/qwen3-32b", - "name": "Qwen: Qwen3 32B", + "gpt-5.3-codex": { + "id": "gpt-5.3-codex", + "name": "GPT-5.3 Codex", + "family": "gpt-codex", "attachment": false, "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2024-12-01", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.08, "output": 0.24, "cache_read": 0.04 }, - "limit": { "context": 40960, "output": 40960 } + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-02-24", + "last_updated": "2026-02-24", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "qwen/qwen3-vl-235b-a22b-thinking": { - "id": "qwen/qwen3-vl-235b-a22b-thinking", - "name": "Qwen: Qwen3 VL 235B A22B Thinking", + "gpt-5.4-mini": { + "id": "gpt-5.4-mini", + "name": "GPT-5.4 Mini", + "family": "gpt-mini", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2025-09-24", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.26, "output": 2.6 }, - "limit": { "context": 131072, "output": 32768 } + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible" + }, + "cost": { + "input": 0.75, + "output": 4.5, + "cache_read": 0.075 + } }, - "qwen/qwen-2.5-72b-instruct": { - "id": "qwen/qwen-2.5-72b-instruct", - "name": "Qwen2.5 72B Instruct", - "attachment": false, - "reasoning": false, + "gpt-5.4-nano": { + "id": "gpt-5.4-nano", + "name": "GPT-5.4 Nano", + "family": "gpt-nano", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2024-09", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.12, "output": 0.39 }, - "limit": { "context": 32768, "output": 16384 } + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible" + }, + "cost": { + "input": 0.2, + "output": 1.25, + "cache_read": 0.02 + } }, - "qwen/qwen3-coder-flash": { - "id": "qwen/qwen3-coder-flash", - "name": "Qwen: Qwen3 Coder Flash", + "gpt-5.2-codex": { + "id": "gpt-5.2-codex", + "name": "GPT-5.2 Codex", + "family": "gpt-codex", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2025-07-23", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-01-14", + "last_updated": "2026-01-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.195, "output": 0.975, "cache_read": 0.06 }, - "limit": { "context": 1000000, "output": 65536 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "qwen/qwen-2.5-vl-7b-instruct": { - "id": "qwen/qwen-2.5-vl-7b-instruct", - "name": "Qwen: Qwen2.5-VL 7B Instruct", + "gpt-5.4": { + "id": "gpt-5.4", + "name": "GPT-5.4", + "family": "gpt", "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2024-08-28", - "last_updated": "2024-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 32768, "output": 6554 } + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible" + }, + "cost": { + "input": 2.5, + "output": 15, + "cache_read": 0.25 + } }, - "qwen/qwen-2.5-coder-32b-instruct": { - "id": "qwen/qwen-2.5-coder-32b-instruct", - "name": "Qwen2.5 Coder 32B Instruct", + "deepseek-v3.2": { + "id": "deepseek-v3.2", + "name": "DeepSeek-V3.2", + "family": "deepseek", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2024-11-11", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-07", + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 0.2, "cache_read": 0.015 }, - "limit": { "context": 32768, "output": 8192 } + "limit": { + "context": 128000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible" + }, + "cost": { + "input": 0.28, + "output": 0.42 + } }, - "qwen/qwen3-30b-a3b-instruct-2507": { - "id": "qwen/qwen3-30b-a3b-instruct-2507", - "name": "Qwen: Qwen3 30B A3B Instruct 2507", + "gpt-5.1-codex": { + "id": "gpt-5.1-codex", + "name": "GPT-5.1 Codex", + "family": "gpt-codex", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2025-07-29", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.09, "output": 0.3, "cache_read": 0.04 }, - "limit": { "context": 262144, "output": 262144 } + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "xiaomi/mimo-v2-flash": { - "id": "xiaomi/mimo-v2-flash", - "name": "Xiaomi: MiMo-V2-Flash", - "attachment": false, + "gpt-5.5": { + "id": "gpt-5.5", + "name": "GPT-5.5", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2025-12-14", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.09, "output": 0.29, "cache_read": 0.045 }, - "limit": { "context": 262144, "output": 65536 } + "structured_output": true, + "temperature": false, + "knowledge": "2025-12-01", + "release_date": "2026-04-23", + "last_updated": "2026-04-23", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible" + }, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5, + "context_over_200k": { + "input": 10, + "output": 45, + "cache_read": 1 + }, + "tiers": [ + { + "input": 10, + "output": 45, + "cache_read": 1, + "tier": { + "type": "context", + "size": 272000 + } + } + ] + } }, - "stepfun/step-3.5-flash": { - "id": "stepfun/step-3.5-flash", - "name": "StepFun: Step 3.5 Flash", + "deepseek-v4-pro": { + "id": "deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2026-01-29", - "last_updated": "2026-01-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.3, "cache_read": 0.02 }, - "limit": { "context": 256000, "output": 256000 } - }, - "stepfun/step-3.5-flash:free": { - "id": "stepfun/step-3.5-flash:free", - "name": "StepFun: Step 3.5 Flash (free)", - "attachment": false, + "limit": { + "context": 1000000, + "output": 384000 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible" + }, + "cost": { + "input": 1.74, + "output": 3.48, + "cache_read": 0.145 + } + } + } + }, + "deepinfra": { + "id": "deepinfra", + "env": ["DEEPINFRA_API_KEY"], + "npm": "@ai-sdk/deepinfra", + "name": "Deep Infra", + "doc": "https://deepinfra.com/models", + "models": { + "Qwen/Qwen3.5-397B-A17B": { + "id": "Qwen/Qwen3.5-397B-A17B", + "name": "Qwen 3.5 397B A17B", + "family": "qwen", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-01-29", - "last_updated": "2026-01-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-02-01", + "last_updated": "2026-04-20", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 256000 } + "limit": { + "context": 262144, + "output": 81920 + }, + "cost": { + "input": 0.54, + "output": 3.4 + } }, - "alfredpros/codellama-7b-instruct-solidity": { - "id": "alfredpros/codellama-7b-instruct-solidity", - "name": "AlfredPros: CodeLLaMa 7B Instruct Solidity", - "attachment": false, - "reasoning": false, - "tool_call": false, + "Qwen/Qwen3.5-35B-A3B": { + "id": "Qwen/Qwen3.5-35B-A3B", + "name": "Qwen 3.5 35B A3B", + "family": "qwen", + "attachment": true, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-04-14", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-02-01", + "last_updated": "2026-04-20", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.8, "output": 1.2 }, - "limit": { "context": 4096, "output": 4096 } + "limit": { + "context": 262144, + "output": 81920 + }, + "cost": { + "input": 0.2, + "output": 0.95 + } }, - "ai21/jamba-large-1.7": { - "id": "ai21/jamba-large-1.7", - "name": "AI21: Jamba Large 1.7", + "Qwen/Qwen3-Coder-480B-A35B-Instruct-Turbo": { + "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct-Turbo", + "name": "Qwen3 Coder 480B A35B Instruct Turbo", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2025-08-09", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 8 }, - "limit": { "context": 256000, "output": 4096 } - }, - "liquid/lfm-2.2-6b": { - "id": "liquid/lfm-2.2-6b", - "name": "LiquidAI: LFM2-2.6B", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2025-10-20", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.01, "output": 0.02 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 262144, + "output": 66536 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "liquid/lfm2-8b-a1b": { - "id": "liquid/lfm2-8b-a1b", - "name": "LiquidAI: LFM2-8B-A1B", + "Qwen/Qwen3-Coder-480B-A35B-Instruct": { + "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct", + "name": "Qwen3 Coder 480B A35B Instruct", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2025-10-20", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.01, "output": 0.02 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 262144, + "output": 66536 + }, + "cost": { + "input": 0.4, + "output": 1.6 + } }, - "liquid/lfm-2-24b-a2b": { - "id": "liquid/lfm-2-24b-a2b", - "name": "LiquidAI: LFM2-24B-A2B", - "attachment": false, - "reasoning": false, - "tool_call": false, + "Qwen/Qwen3.6-35B-A3B": { + "id": "Qwen/Qwen3.6-35B-A3B", + "name": "Qwen3.6 35B A3B", + "family": "qwen", + "attachment": true, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2026-02-26", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-01", + "last_updated": "2026-04-01", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.03, "output": 0.12 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 262144, + "output": 81920 + }, + "cost": { + "input": 0.2, + "output": 1 + } }, - "aion-labs/aion-2.0": { - "id": "aion-labs/aion-2.0", - "name": "AionLabs: Aion-2.0", + "zai-org/GLM-4.7-Flash": { + "id": "zai-org/GLM-4.7-Flash", + "name": "GLM-4.7-Flash", + "family": "glm-flash", "attachment": false, "reasoning": true, - "tool_call": false, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2026-02-24", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.8, "output": 1.6 }, - "limit": { "context": 131072, "output": 32768 } + "knowledge": "2025-04", + "release_date": "2026-01-19", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 16384 + }, + "cost": { + "input": 0.06, + "output": 0.4 + } }, - "aion-labs/aion-rp-llama-3.1-8b": { - "id": "aion-labs/aion-rp-llama-3.1-8b", - "name": "AionLabs: Aion-RP 1.0 (8B)", + "zai-org/GLM-4.5": { + "id": "zai-org/GLM-4.5", + "name": "GLM-4.5", + "family": "glm", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2025-02-05", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.8, "output": 1.6 }, - "limit": { "context": 32768, "output": 32768 } + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 98304 + }, + "status": "deprecated", + "cost": { + "input": 0.6, + "output": 2.2 + } }, - "aion-labs/aion-1.0-mini": { - "id": "aion-labs/aion-1.0-mini", - "name": "AionLabs: Aion-1.0-Mini", + "zai-org/GLM-4.7": { + "id": "zai-org/GLM-4.7", + "name": "GLM-4.7", + "family": "glm", "attachment": false, "reasoning": true, - "tool_call": false, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-02-05", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.7, "output": 1.4 }, - "limit": { "context": 131072, "output": 32768 } + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 16384 + }, + "cost": { + "input": 0.43, + "output": 1.75, + "cache_read": 0.08 + } }, - "aion-labs/aion-1.0": { - "id": "aion-labs/aion-1.0", - "name": "AionLabs: Aion-1.0", + "zai-org/GLM-5.1": { + "id": "zai-org/GLM-5.1", + "name": "GLM-5.1", + "family": "glm", "attachment": false, "reasoning": true, - "tool_call": false, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-02-05", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 4, "output": 8 }, - "limit": { "context": 131072, "output": 32768 } + "knowledge": "2025-04", + "release_date": "2026-04-07", + "last_updated": "2026-04-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 16384 + }, + "cost": { + "input": 1.4, + "output": 4.4, + "cache_read": 0.26 + } }, - "moonshotai/kimi-k2-thinking": { - "id": "moonshotai/kimi-k2-thinking", - "name": "MoonshotAI: Kimi K2 Thinking", + "zai-org/GLM-5": { + "id": "zai-org/GLM-5", + "name": "GLM-5", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-11-06", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-12", + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.47, "output": 2, "cache_read": 0.2 }, - "limit": { "context": 131072, "output": 65535 } + "limit": { + "context": 202752, + "output": 16384 + }, + "cost": { + "input": 0.8, + "output": 2.56, + "cache_read": 0.16 + } }, - "moonshotai/kimi-k2.5": { - "id": "moonshotai/kimi-k2.5", - "name": "MoonshotAI: Kimi K2.5", + "zai-org/GLM-4.6V": { + "id": "zai-org/GLM-4.6V", + "name": "GLM-4.6V", + "family": "glm", "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2026-01-27", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.45, "output": 2.2 }, - "limit": { "context": 262144, "output": 65535 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 0.9 + } }, - "moonshotai/kimi-k2-0905": { - "id": "moonshotai/kimi-k2-0905", - "name": "MoonshotAI: Kimi K2 0905", + "zai-org/GLM-4.6": { + "id": "zai-org/GLM-4.6", + "name": "GLM-4.6", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.4, "output": 2, "cache_read": 0.15 }, - "limit": { "context": 131072, "output": 26215 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.43, + "output": 1.74, + "cache_read": 0.08 + } }, - "moonshotai/kimi-k2": { - "id": "moonshotai/kimi-k2", - "name": "MoonshotAI: Kimi K2 0711", + "meta-llama/Llama-4-Scout-17B-16E-Instruct": { + "id": "meta-llama/Llama-4-Scout-17B-16E-Instruct", + "name": "Llama 4 Scout 17B", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": true, - "temperature": true, - "release_date": "2025-07-11", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.55, "output": 2.2 }, - "limit": { "context": 131000, "output": 26215 } - }, - "tencent/hunyuan-a13b-instruct": { - "id": "tencent/hunyuan-a13b-instruct", - "name": "Tencent: Hunyuan A13B Instruct", - "attachment": false, - "reasoning": true, - "tool_call": false, - "temperature": true, - "release_date": "2025-06-30", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.14, "output": 0.57 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 10000000, + "output": 16384 + }, + "cost": { + "input": 0.08, + "output": 0.3 + } }, - "alibaba/tongyi-deepresearch-30b-a3b": { - "id": "alibaba/tongyi-deepresearch-30b-a3b", - "name": "Tongyi DeepResearch 30B A3B", + "meta-llama/Llama-3.1-8B-Instruct": { + "id": "meta-llama/Llama-3.1-8B-Instruct", + "name": "Llama 3.1 8B", + "family": "llama", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-09-18", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.09, "output": 0.45 }, - "limit": { "context": 131072, "output": 131072 } - }, - "bytedance-seed/seed-2.0-mini": { - "id": "bytedance-seed/seed-2.0-mini", - "name": "ByteDance Seed: Seed-2.0-Mini", - "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "temperature": true, - "release_date": "2026-02-27", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text", "video"], "output": ["text"] }, + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 262144, "output": 131072 } + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.02, + "output": 0.05 + } }, - "bytedance-seed/seed-1.6-flash": { - "id": "bytedance-seed/seed-1.6-flash", - "name": "ByteDance Seed: Seed 1.6 Flash", - "attachment": true, - "reasoning": true, + "meta-llama/Llama-3.1-70B-Instruct": { + "id": "meta-llama/Llama-3.1-70B-Instruct", + "name": "Llama 3.1 70B", + "family": "llama", + "attachment": false, + "reasoning": false, "tool_call": true, - "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text", "video"], "output": ["text"] }, + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.075, "output": 0.3 }, - "limit": { "context": 262144, "output": 32768 } + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.4, + "output": 0.4 + } }, - "bytedance-seed/seed-1.6": { - "id": "bytedance-seed/seed-1.6", - "name": "ByteDance Seed: Seed 1.6", - "attachment": true, - "reasoning": true, + "meta-llama/Llama-3.1-8B-Instruct-Turbo": { + "id": "meta-llama/Llama-3.1-8B-Instruct-Turbo", + "name": "Llama 3.1 8B Turbo", + "family": "llama", + "attachment": false, + "reasoning": false, "tool_call": true, - "temperature": true, - "release_date": "2025-09", - "last_updated": "2025-09", - "modalities": { "input": ["image", "text", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 2 }, - "limit": { "context": 262144, "output": 32768 } + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.02, + "output": 0.03 + } }, - "bytedance-seed/seed-2.0-lite": { - "id": "bytedance-seed/seed-2.0-lite", - "name": "ByteDance Seed: Seed-2.0-Lite", - "attachment": true, - "reasoning": true, + "meta-llama/Llama-3.3-70B-Instruct-Turbo": { + "id": "meta-llama/Llama-3.3-70B-Instruct-Turbo", + "name": "Llama 3.3 70B Turbo", + "family": "llama", + "attachment": false, + "reasoning": false, "tool_call": true, - "temperature": true, - "release_date": "2026-03-10", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text", "video"], "output": ["text"] }, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.25, "output": 2 }, - "limit": { "context": 262144, "output": 131072 } + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.1, + "output": 0.32 + } }, - "inflection/inflection-3-pi": { - "id": "inflection/inflection-3-pi", - "name": "Inflection: Inflection 3 Pi", + "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8": { + "id": "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8", + "name": "Llama 4 Maverick 17B FP8", + "family": "llama", "attachment": false, "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2024-10-11", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 8000, "output": 1024 } + "tool_call": true, + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "inflection/inflection-3-productivity": { - "id": "inflection/inflection-3-productivity", - "name": "Inflection: Inflection 3 Productivity", + "meta-llama/Llama-3.1-70B-Instruct-Turbo": { + "id": "meta-llama/Llama-3.1-70B-Instruct-Turbo", + "name": "Llama 3.1 70B Turbo", + "family": "llama", "attachment": false, "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2024-10-11", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 8000, "output": 1024 } + "tool_call": true, + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.4, + "output": 0.4 + } }, - "writer/palmyra-x5": { - "id": "writer/palmyra-x5", - "name": "Writer: Palmyra X5", + "deepseek-ai/DeepSeek-R1-0528": { + "id": "deepseek-ai/DeepSeek-R1-0528", + "name": "DeepSeek-R1-0528", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": false, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-04-28", - "last_updated": "2025-04-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-07", + "release_date": "2025-05-28", + "last_updated": "2025-05-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 6 }, - "limit": { "context": 1040000, "output": 8192 } + "limit": { + "context": 163840, + "output": 64000 + }, + "cost": { + "input": 0.5, + "output": 2.15, + "cache_read": 0.35 + } }, - "inception/mercury-2": { - "id": "inception/mercury-2", - "name": "Inception: Mercury 2", + "deepseek-ai/DeepSeek-V3.2": { + "id": "deepseek-ai/DeepSeek-V3.2", + "name": "DeepSeek-V3.2", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2026-02-24", - "last_updated": "2026-02-24", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2025-12-02", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 0.75, "cache_read": 0.025 }, - "limit": { "context": 128000, "output": 50000 } + "limit": { + "context": 163840, + "output": 64000 + }, + "cost": { + "input": 0.26, + "output": 0.38, + "cache_read": 0.13 + } }, - "inception/mercury-coder": { - "id": "inception/mercury-coder", - "name": "Inception: Mercury Coder", + "openai/gpt-oss-20b": { + "id": "openai/gpt-oss-20b", + "name": "GPT OSS 20B", + "family": "gpt-oss", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-02-26", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 0.75 }, - "limit": { "context": 128000, "output": 32000 } + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.03, + "output": 0.14 + } }, - "inception/mercury": { - "id": "inception/mercury", - "name": "Inception: Mercury", + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "name": "GPT OSS 120B", + "family": "gpt-oss", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-06-26", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 0.75 }, - "limit": { "context": 128000, "output": 32000 } - }, - "anthracite-org/magnum-v4-72b": { - "id": "anthracite-org/magnum-v4-72b", - "name": "Magnum v4 72B", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2024-10-22", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 3, "output": 5 }, - "limit": { "context": 16384, "output": 2048 } + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.05, + "output": 0.24 + } }, - "thedrummer/skyfall-36b-v2": { - "id": "thedrummer/skyfall-36b-v2", - "name": "TheDrummer: Skyfall 36B V2", + "moonshotai/Kimi-K2-Thinking": { + "id": "moonshotai/Kimi-K2-Thinking", + "name": "Kimi K2 Thinking", + "family": "kimi-thinking", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-03-11", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-11-06", + "last_updated": "2025-11-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.55, "output": 0.8 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.47, + "output": 2 + } }, - "thedrummer/rocinante-12b": { - "id": "thedrummer/rocinante-12b", - "name": "TheDrummer: Rocinante 12B", - "attachment": false, - "reasoning": false, + "moonshotai/Kimi-K2.6": { + "id": "moonshotai/Kimi-K2.6", + "name": "Kimi K2.6", + "family": "kimi", + "attachment": true, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2024-09-30", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.17, "output": 0.43 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 262144, + "output": 16384 + }, + "cost": { + "input": 0.75, + "output": 3.5, + "cache_read": 0.15 + } }, - "thedrummer/cydonia-24b-v4.1": { - "id": "thedrummer/cydonia-24b-v4.1", - "name": "TheDrummer: Cydonia 24B V4.1", + "moonshotai/Kimi-K2-Instruct": { + "id": "moonshotai/Kimi-K2-Instruct", + "name": "Kimi K2", + "family": "kimi", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2025-09-27", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-07-11", + "last_updated": "2025-07-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 0.5 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.5, + "output": 2 + } }, - "thedrummer/unslopnemo-12b": { - "id": "thedrummer/unslopnemo-12b", - "name": "TheDrummer: UnslopNemo 12B", + "moonshotai/Kimi-K2-Instruct-0905": { + "id": "moonshotai/Kimi-K2-Instruct-0905", + "name": "Kimi K2 0905", + "family": "kimi", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2024-11-09", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.4, "output": 0.4 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.4, + "output": 2, + "cache_read": 0.15 + } }, - "tngtech/deepseek-r1t2-chimera": { - "id": "tngtech/deepseek-r1t2-chimera", - "name": "TNG: DeepSeek R1T2 Chimera", - "attachment": false, + "moonshotai/Kimi-K2.5": { + "id": "moonshotai/Kimi-K2.5", + "name": "Kimi K2.5", + "family": "kimi", + "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-07-08", - "last_updated": "2025-07-08", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.25, "output": 0.85, "cache_read": 0.125 }, - "limit": { "context": 163840, "output": 163840 } + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0.5, + "output": 2.8 + } }, - "deepcogito/cogito-v2.1-671b": { - "id": "deepcogito/cogito-v2.1-671b", - "name": "Deep Cogito: Cogito v2.1 671B", + "MiniMaxAI/MiniMax-M2": { + "id": "MiniMaxAI/MiniMax-M2", + "name": "MiniMax M2", + "family": "minimax", "attachment": false, "reasoning": true, - "tool_call": false, - "temperature": true, - "release_date": "2025-11-14", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1.25, "output": 1.25 }, - "limit": { "context": 128000, "output": 32768 } - }, - "mistralai/mistral-medium-3.1": { - "id": "mistralai/mistral-medium-3.1", - "name": "Mistral: Mistral Medium 3.1", - "attachment": true, - "reasoning": false, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-08-12", - "last_updated": "2025-08-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 131072, "output": 26215 } + "knowledge": "2024-10", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0.254, + "output": 1.02 + } }, - "mistralai/mixtral-8x22b-instruct": { - "id": "mistralai/mixtral-8x22b-instruct", - "name": "Mistral: Mixtral 8x22B Instruct", + "MiniMaxAI/MiniMax-M2.5": { + "id": "MiniMaxAI/MiniMax-M2.5", + "name": "MiniMax M2.5", + "family": "minimax", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2024-04-17", - "last_updated": "2024-04-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-06", + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 2, "output": 6 }, - "limit": { "context": 65536, "output": 13108 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.27, + "output": 0.95, + "cache_read": 0.03, + "cache_write": 0.375 + } }, - "mistralai/devstral-medium": { - "id": "mistralai/devstral-medium", - "name": "Mistral: Devstral Medium", + "MiniMaxAI/MiniMax-M2.1": { + "id": "MiniMaxAI/MiniMax-M2.1", + "name": "MiniMax M2.1", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-07-10", - "last_updated": "2025-07-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-06", + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 131072, "output": 26215 } + "limit": { + "context": 196608, + "output": 196608 + }, + "cost": { + "input": 0.28, + "output": 1.2 + } }, - "mistralai/mistral-7b-instruct-v0.1": { - "id": "mistralai/mistral-7b-instruct-v0.1", - "name": "Mistral: Mistral 7B Instruct v0.1", - "attachment": false, - "reasoning": false, - "tool_call": false, + "anthropic/claude-3-7-sonnet-latest": { + "id": "anthropic/claude-3-7-sonnet-latest", + "name": "Claude Sonnet 3.7 (Latest)", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10-31", + "release_date": "2025-03-13", + "last_updated": "2025-03-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.11, "output": 0.19 }, - "limit": { "context": 2824, "output": 565 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3.3, + "output": 16.5, + "cache_read": 0.33 + } }, - "mistralai/mistral-medium-3": { - "id": "mistralai/mistral-medium-3", - "name": "Mistral: Mistral Medium 3", + "anthropic/claude-4-opus": { + "id": "anthropic/claude-4-opus", + "name": "Claude Opus 4", + "family": "claude-opus", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-05-07", - "last_updated": "2025-05-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-03-31", + "release_date": "2025-06-12", + "last_updated": "2025-06-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 131072, "output": 26215 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 16.5, + "output": 82.5 + } }, - "mistralai/devstral-2512": { - "id": "mistralai/devstral-2512", - "name": "Mistral: Devstral 2 2512", + "deepseek-ai/DeepSeek-V4-Flash": { + "id": "deepseek-ai/DeepSeek-V4-Flash", + "name": "DeepSeek V4 Flash", + "family": "deepseek-flash", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-09-12", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.4, "output": 2, "cache_read": 0.025 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 0.14, + "output": 0.28, + "cache_read": 0.028 + } }, - "mistralai/mixtral-8x7b-instruct": { - "id": "mistralai/mixtral-8x7b-instruct", - "name": "Mistral: Mixtral 8x7B Instruct", + "deepseek-ai/DeepSeek-V4-Pro": { + "id": "deepseek-ai/DeepSeek-V4-Pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2023-12-10", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.54, "output": 0.54 }, - "limit": { "context": 32768, "output": 16384 } + "limit": { + "context": 65536, + "output": 65536 + }, + "cost": { + "input": 1.74, + "output": 3.48, + "cache_read": 0.145 + } }, - "mistralai/mistral-small-24b-instruct-2501": { - "id": "mistralai/mistral-small-24b-instruct-2501", - "name": "Mistral: Mistral Small 3", + "google/gemma-4-26B-A4B-it": { + "id": "google/gemma-4-26B-A4B-it", + "name": "Gemma 4 26B", + "family": "gemma", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-12-29", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.05, "output": 0.08 }, - "limit": { "context": 32768, "output": 16384 } + "limit": { + "context": 256000, + "output": 8192 + }, + "cost": { + "input": 0.07, + "output": 0.34 + } }, - "mistralai/ministral-8b-2512": { - "id": "mistralai/ministral-8b-2512", - "name": "Mistral: Ministral 3 8B 2512", - "attachment": true, - "reasoning": false, + "google/gemma-4-31B-it": { + "id": "google/gemma-4-31B-it", + "name": "Gemma 4 31B", + "family": "gemma", + "attachment": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-12-02", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.15 }, - "limit": { "context": 262144, "output": 32768 } - }, - "mistralai/ministral-14b-2512": { - "id": "mistralai/ministral-14b-2512", - "name": "Mistral: Ministral 3 14B 2512", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2025-12-16", - "last_updated": "2025-12-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 262144, "output": 52429 } - }, - "mistralai/mistral-large-2411": { - "id": "mistralai/mistral-large-2411", - "name": "Mistral Large 2411", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2024-07-24", - "last_updated": "2024-11-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2, "output": 6 }, - "limit": { "context": 131072, "output": 26215 } - }, - "mistralai/codestral-2508": { - "id": "mistralai/codestral-2508", - "name": "Mistral: Codestral 2508", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2025-08-01", - "last_updated": "2025-08-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 0.9 }, - "limit": { "context": 256000, "output": 51200 } + "limit": { + "context": 256000, + "output": 8192 + }, + "cost": { + "input": 0.13, + "output": 0.38 + } }, - "mistralai/mistral-large-2407": { - "id": "mistralai/mistral-large-2407", - "name": "Mistral Large 2407", + "xiaomi/mimo-v2.5-pro": { + "id": "xiaomi/mimo-v2.5-pro", + "name": "MiMo-V2.5-Pro", + "family": "mimo", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2024-11-19", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 2, "output": 6 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 1048576, + "output": 16384 + }, + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.2, + "context_over_200k": { + "input": 2, + "output": 6, + "cache_read": 0.4 + }, + "tiers": [ + { + "input": 2, + "output": 6, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 256000 + } + } + ] + } }, - "mistralai/devstral-small": { - "id": "mistralai/devstral-small", - "name": "Mistral: Devstral Small 1.1", - "attachment": false, - "reasoning": false, + "xiaomi/mimo-v2.5": { + "id": "xiaomi/mimo-v2.5", + "name": "MiMo-V2.5", + "family": "mimo", + "attachment": true, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-05-07", - "last_updated": "2025-07-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 131072, "output": 26215 } - }, - "mistralai/mistral-small-creative": { - "id": "mistralai/mistral-small-creative", - "name": "Mistral: Mistral Small Creative", - "attachment": false, - "reasoning": false, - "tool_call": true, - "release_date": "2025-12-17", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 32768, "output": 32768 } - }, - "mistralai/voxtral-small-24b-2507": { - "id": "mistralai/voxtral-small-24b-2507", - "name": "Mistral: Voxtral Small 24B 2507", + "limit": { + "context": 262144, + "output": 16384 + }, + "cost": { + "input": 0.4, + "output": 2, + "cache_read": 0.08, + "context_over_200k": { + "input": 0.8, + "output": 4, + "cache_read": 0.16 + }, + "tiers": [ + { + "input": 0.8, + "output": 4, + "cache_read": 0.16, + "tier": { + "type": "context", + "size": 256000 + } + } + ] + } + } + } + }, + "qiniu-ai": { + "id": "qiniu-ai", + "env": ["QINIU_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.qnaigc.com/v1", + "name": "Qiniu", + "doc": "https://developer.qiniu.com/aitokenapi", + "models": { + "qwen3-235b-a22b": { + "id": "qwen3-235b-a22b", + "name": "Qwen 3 235B A22B", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": false, "temperature": true, - "release_date": "2025-07-01", - "last_updated": "2025-07-01", - "modalities": { "input": ["text", "audio"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 32000, "output": 6400 } + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 32000 + } }, - "mistralai/mistral-nemo": { - "id": "mistralai/mistral-nemo", - "name": "Mistral: Mistral Nemo", - "attachment": false, - "reasoning": false, + "doubao-seed-1.6-flash": { + "id": "doubao-seed-1.6-flash", + "name": "Doubao-Seed 1.6 Flash", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": false, "temperature": true, - "release_date": "2024-07-01", - "last_updated": "2024-07-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.02, "output": 0.04 }, - "limit": { "context": 131072, "output": 16384 } + "release_date": "2025-08-15", + "last_updated": "2025-08-15", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 32000 + } }, - "mistralai/mistral-large": { - "id": "mistralai/mistral-large", - "name": "Mistral Large", + "qwen3-235b-a22b-instruct-2507": { + "id": "qwen3-235b-a22b-instruct-2507", + "name": "Qwen3 235b A22B Instruct 2507", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": false, "temperature": true, - "release_date": "2024-07-24", - "last_updated": "2025-12-02", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2, "output": 6 }, - "limit": { "context": 128000, "output": 25600 } - }, - "mistralai/ministral-3b-2512": { - "id": "mistralai/ministral-3b-2512", - "name": "Mistral: Ministral 3 3B 2512", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2025-12-02", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.1 }, - "limit": { "context": 131072, "output": 32768 } + "release_date": "2025-08-12", + "last_updated": "2025-08-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 64000 + } }, - "mistralai/mistral-small-3.2-24b-instruct": { - "id": "mistralai/mistral-small-3.2-24b-instruct", - "name": "Mistral: Mistral Small 3.2 24B", + "doubao-seed-2.0-code": { + "id": "doubao-seed-2.0-code", + "name": "Doubao Seed 2.0 Code", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": false, "temperature": true, - "release_date": "2025-06-20", - "last_updated": "2025-06-20", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.06, "output": 0.18, "cache_read": 0.03 }, - "limit": { "context": 131072, "output": 131072 } + "release_date": "2026-02-14", + "last_updated": "2026-02-14", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 128000 + } }, - "mistralai/pixtral-large-2411": { - "id": "mistralai/pixtral-large-2411", - "name": "Mistral: Pixtral Large 2411", - "attachment": true, + "deepseek-v3-0324": { + "id": "deepseek-v3-0324", + "name": "DeepSeek-V3-0324", + "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": false, "temperature": true, - "release_date": "2024-11-19", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2, "output": 6 }, - "limit": { "context": 131072, "output": 32768 } + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16000 + } }, - "mistralai/mistral-saba": { - "id": "mistralai/mistral-saba", - "name": "Mistral: Saba", + "doubao-1.5-thinking-pro": { + "id": "doubao-1.5-thinking-pro", + "name": "Doubao 1.5 Thinking Pro", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": false, "temperature": true, - "release_date": "2025-02-17", - "last_updated": "2026-03-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.6 }, - "limit": { "context": 32768, "output": 32768 } - }, - "mistralai/mistral-small-3.1-24b-instruct": { - "id": "mistralai/mistral-small-3.1-24b-instruct", - "name": "Mistral: Mistral Small 3.1 24B", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2025-03-17", - "last_updated": "2026-03-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.35, "output": 0.56, "cache_read": 0.015 }, - "limit": { "context": 128000, "output": 131072 } + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16000 + } }, - "mistralai/mistral-large-2512": { - "id": "mistralai/mistral-large-2512", - "name": "Mistral: Mistral Large 3 2512", + "claude-3.7-sonnet": { + "id": "claude-3.7-sonnet", + "name": "Claude 3.7 Sonnet", "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2024-11-01", - "last_updated": "2025-12-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.5, "output": 1.5 }, - "limit": { "context": 262144, "output": 52429 } - } - } - }, - "kuae-cloud-coding-plan": { - "id": "kuae-cloud-coding-plan", - "env": ["KUAE_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://coding-plan-endpoint.kuaecloud.net/v1", - "name": "KUAE Cloud Coding Plan", - "doc": "https://docs.mthreads.com/kuaecloud/kuaecloud-doc-online/coding_plan/", - "models": { - "GLM-4.7": { - "id": "GLM-4.7", - "name": "GLM-4.7", - "family": "glm", - "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "structured_output": false, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } - } - } - }, - "modelscope": { - "id": "modelscope", - "env": ["MODELSCOPE_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api-inference.modelscope.cn/v1", - "name": "ModelScope", - "doc": "https://modelscope.cn/docs/model-service/API-Inference/intro", - "models": { - "ZhipuAI/GLM-4.5": { - "id": "ZhipuAI/GLM-4.5", - "name": "GLM-4.5", - "family": "glm", - "attachment": false, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 128000 + } + }, + "qwen3.5-397b-a17b": { + "id": "qwen3.5-397b-a17b", + "name": "Qwen3.5 397B A17B", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 98304 } + "release_date": "2026-02-22", + "last_updated": "2026-02-22", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 64000 + } }, - "ZhipuAI/GLM-4.6": { - "id": "ZhipuAI/GLM-4.6", - "name": "GLM-4.6", - "family": "glm", - "attachment": false, - "reasoning": true, + "qwen-vl-max-2025-01-25": { + "id": "qwen-vl-max-2025-01-25", + "name": "Qwen VL-MAX-2025-01-25", + "attachment": true, + "reasoning": false, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 202752, "output": 98304 } + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 4096 + } }, - "Qwen/Qwen3-30B-A3B-Thinking-2507": { - "id": "Qwen/Qwen3-30B-A3B-Thinking-2507", - "name": "Qwen3 30B A3B Thinking 2507", - "family": "qwen", + "qwen3-32b": { + "id": "qwen3-32b", + "name": "Qwen3 32B", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-30", - "last_updated": "2025-07-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 32768 } + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 40000, + "output": 4096 + } }, - "Qwen/Qwen3-235B-A22B-Thinking-2507": { - "id": "Qwen/Qwen3-235B-A22B-Thinking-2507", - "name": "Qwen3-235B-A22B-Thinking-2507", - "family": "qwen", + "doubao-1.5-pro-32k": { + "id": "doubao-1.5-pro-32k", + "name": "Doubao 1.5 Pro 32k", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-25", - "last_updated": "2025-07-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 131072 } + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 12000 + } }, - "Qwen/Qwen3-Coder-30B-A3B-Instruct": { - "id": "Qwen/Qwen3-Coder-30B-A3B-Instruct", - "name": "Qwen3 Coder 30B A3B Instruct", - "family": "qwen", - "attachment": false, + "qwen2.5-vl-72b-instruct": { + "id": "qwen2.5-vl-72b-instruct", + "name": "Qwen 2.5 VL 72B Instruct", + "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-31", - "last_updated": "2025-07-31", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 65536 } + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 8192 + } }, - "Qwen/Qwen3-30B-A3B-Instruct-2507": { - "id": "Qwen/Qwen3-30B-A3B-Instruct-2507", - "name": "Qwen3 30B A3B Instruct 2507", - "family": "qwen", - "attachment": false, + "gemini-2.0-flash": { + "id": "gemini-2.0-flash", + "name": "Gemini 2.0 Flash", + "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-30", - "last_updated": "2025-07-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 16384 } + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 8192 + } }, - "Qwen/Qwen3-235B-A22B-Instruct-2507": { - "id": "Qwen/Qwen3-235B-A22B-Instruct-2507", - "name": "Qwen3 235B A22B Instruct 2507", - "family": "qwen", - "attachment": false, + "qwen3-vl-30b-a3b-thinking": { + "id": "qwen3-vl-30b-a3b-thinking", + "name": "Qwen3-Vl 30b A3b Thinking", + "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04-28", - "last_updated": "2025-07-21", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 131072 } - } - } - }, - "openrouter": { - "id": "openrouter", - "env": ["OPENROUTER_API_KEY"], - "npm": "@openrouter/ai-sdk-provider", - "api": "https://openrouter.ai/api/v1", - "name": "OpenRouter", - "doc": "https://openrouter.ai/models", - "models": { - "openai/gpt-5.2-codex": { - "id": "openai/gpt-5.2-codex", - "name": "GPT-5.2-Codex", - "family": "gpt-codex", + "release_date": "2026-02-09", + "last_updated": "2026-02-09", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 32000 + } + }, + "gemini-3.0-pro-image-preview": { + "id": "gemini-3.0-pro-image-preview", + "name": "Gemini 3.0 Pro Image Preview", "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, + "reasoning": false, + "tool_call": false, + "structured_output": false, "temperature": true, - "knowledge": "2025-08-31", - "release_date": "2026-01-14", - "last_updated": "2026-01-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-11-20", + "last_updated": "2025-11-20", + "modalities": { + "input": ["text", "image"], + "output": ["text", "image"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 32768, + "output": 8192 + } }, - "openai/gpt-5.1-codex-mini": { - "id": "openai/gpt-5.1-codex-mini", - "name": "GPT-5.1-Codex-Mini", - "family": "gpt-codex", + "gemini-2.5-pro": { + "id": "gemini-2.5-pro", + "name": "Gemini 2.5 Pro", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.025 }, - "limit": { "context": 400000, "output": 100000 } + "limit": { + "context": 1048576, + "output": 65536 + } }, - "openai/gpt-5.4-pro": { - "id": "openai/gpt-5.4-pro", - "name": "GPT-5.4 Pro", - "family": "gpt-pro", + "claude-4.5-opus": { + "id": "claude-4.5-opus", + "name": "Claude 4.5 Opus", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": false, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "release_date": "2025-11-25", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 30, "output": 180, "cache_read": 30 }, - "limit": { "context": 1050000, "input": 922000, "output": 128000 } + "limit": { + "context": 200000, + "output": 200000 + } }, - "openai/gpt-oss-120b:free": { - "id": "openai/gpt-oss-120b:free", - "name": "gpt-oss-120b (free)", - "family": "gpt-oss", + "deepseek-r1": { + "id": "deepseek-r1", + "name": "DeepSeek-R1", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": false, "temperature": true, "release_date": "2025-08-05", "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 32768 } + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 32000 + } }, - "openai/gpt-oss-120b:exacto": { - "id": "openai/gpt-oss-120b:exacto", - "name": "GPT OSS 120B (exacto)", - "family": "gpt-oss", - "attachment": false, + "claude-4.0-opus": { + "id": "claude-4.0-opus", + "name": "Claude 4.0 Opus", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, "release_date": "2025-08-05", "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.05, "output": 0.24 }, - "limit": { "context": 131072, "output": 32768 } + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 32000 + } }, - "openai/gpt-5.4-mini": { - "id": "openai/gpt-5.4-mini", - "name": "GPT-5.4 Mini", - "family": "gpt-mini", + "claude-4.5-haiku": { + "id": "claude-4.5-haiku", + "name": "Claude 4.5 Haiku", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-08-31", - "release_date": "2026-03-17", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-10-16", + "last_updated": "2025-10-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 7.5e-7, "output": 0.0000045, "cache_read": 7.5e-8 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 200000, + "output": 64000 + } }, - "openai/gpt-5-pro": { - "id": "openai/gpt-5-pro", - "name": "GPT-5 Pro", - "family": "gpt-pro", + "qwen3-max": { + "id": "qwen3-max", + "name": "Qwen3 Max", + "attachment": false, + "reasoning": false, + "tool_call": true, + "structured_output": false, + "temperature": true, + "release_date": "2025-09-24", + "last_updated": "2025-09-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 65536 + } + }, + "gemini-3.0-flash-preview": { + "id": "gemini-3.0-flash-preview", + "name": "Gemini 3.0 Flash Preview", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-10-06", - "last_updated": "2025-10-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": false, + "temperature": true, + "release_date": "2025-12-18", + "last_updated": "2025-12-18", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 120 }, - "limit": { "context": 400000, "output": 272000 } + "limit": { + "context": 1000000, + "output": 64000 + } }, - "openai/gpt-5": { - "id": "openai/gpt-5", - "name": "GPT-5", - "family": "gpt", + "gemini-2.5-flash-image": { + "id": "gemini-2.5-flash-image", + "name": "Gemini 2.5 Flash Image", "attachment": true, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "temperature": true, + "release_date": "2025-10-22", + "last_updated": "2025-10-22", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "output": 8192 + } + }, + "glm-4.5": { + "id": "glm-4.5", + "name": "GLM 4.5", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-10-01", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 131072, + "output": 98304 + } }, - "openai/gpt-5.3-codex": { - "id": "openai/gpt-5.3-codex", - "name": "GPT-5.3-Codex", - "family": "gpt-codex", + "claude-3.5-sonnet": { + "id": "claude-3.5-sonnet", + "name": "Claude 3.5 Sonnet", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-02-24", - "last_updated": "2026-02-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": false, + "temperature": true, + "release_date": "2025-09-09", + "last_updated": "2025-09-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 200000, + "output": 8200 + } }, - "openai/gpt-5-mini": { - "id": "openai/gpt-5-mini", - "name": "GPT-5 Mini", - "family": "gpt-mini", + "claude-4.0-sonnet": { + "id": "claude-4.0-sonnet", + "name": "Claude 4.0 Sonnet", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-10-01", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 2 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 200000, + "output": 64000 + } }, - "openai/gpt-4o-mini": { - "id": "openai/gpt-4o-mini", - "name": "GPT-4o-mini", - "family": "gpt-mini", - "attachment": true, + "qwen3-30b-a3b-instruct-2507": { + "id": "qwen3-30b-a3b-instruct-2507", + "name": "Qwen3 30b A3b Instruct 2507", + "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-02-04", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.6, "cache_read": 0.08 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "output": 32000 + } }, - "openai/gpt-5.1-codex-max": { - "id": "openai/gpt-5.1-codex-max", - "name": "GPT-5.1-Codex-Max", - "family": "gpt-codex", + "doubao-seed-1.6-thinking": { + "id": "doubao-seed-1.6-thinking", + "name": "Doubao-Seed 1.6 Thinking", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-15", + "last_updated": "2025-08-15", + "modalities": { + "input": ["image", "text", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 9, "cache_read": 0.11 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 256000, + "output": 32000 + } }, - "openai/gpt-4.1": { - "id": "openai/gpt-4.1", - "name": "GPT-4.1", - "family": "gpt", + "gemini-2.5-flash": { + "id": "gemini-2.5-flash", + "name": "Gemini 2.5 Flash", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 1048576, + "output": 64000 + } }, - "openai/gpt-oss-120b": { - "id": "openai/gpt-oss-120b", - "name": "GPT OSS 120B", - "family": "gpt-oss", + "qwen3-235b-a22b-thinking-2507": { + "id": "qwen3-235b-a22b-thinking-2507", + "name": "Qwen3 235B A22B Thinking 2507", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.072, "output": 0.28 }, - "limit": { "context": 131072, "output": 32768 } + "release_date": "2025-08-12", + "last_updated": "2025-08-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 4096 + } }, - "openai/gpt-5.1-chat": { - "id": "openai/gpt-5.1-chat", - "name": "GPT-5.1 Chat", - "family": "gpt-codex", - "attachment": true, + "qwen3-next-80b-a3b-thinking": { + "id": "qwen3-next-80b-a3b-thinking", + "name": "Qwen3 Next 80B A3B Thinking", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-09-12", + "last_updated": "2025-09-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 131072, + "output": 32768 + } }, - "openai/gpt-5.4": { - "id": "openai/gpt-5.4", - "name": "GPT-5.4", - "family": "gpt", - "attachment": true, + "qwen3-30b-a3b-thinking-2507": { + "id": "qwen3-30b-a3b-thinking-2507", + "name": "Qwen3 30b A3b Thinking 2507", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { - "input": 2.5, - "output": 15, - "cache_read": 0.25, - "context_over_200k": { "input": 5, "output": 22.5, "cache_read": 0.5 } + "structured_output": false, + "temperature": true, + "release_date": "2026-02-04", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] }, - "limit": { "context": 1050000, "input": 922000, "output": 128000 } + "open_weights": false, + "limit": { + "context": 126000, + "output": 32000 + } }, - "openai/gpt-oss-20b:free": { - "id": "openai/gpt-oss-20b:free", - "name": "gpt-oss-20b (free)", - "family": "gpt-oss", + "glm-4.5-air": { + "id": "glm-4.5-air", + "name": "GLM 4.5 Air", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": false, "temperature": true, "release_date": "2025-08-05", - "last_updated": "2026-01-31", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 32768 } - }, - "openai/gpt-5-chat": { - "id": "openai/gpt-5-chat", - "name": "GPT-5 Chat (latest)", - "family": "gpt-codex", - "attachment": true, - "reasoning": true, - "tool_call": false, - "structured_output": true, - "temperature": true, - "knowledge": "2024-09-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 131000, + "output": 4096 + } }, - "openai/gpt-5.4-nano": { - "id": "openai/gpt-5.4-nano", - "name": "GPT-5.4 Nano", - "family": "gpt-nano", - "attachment": true, - "reasoning": false, + "deepseek-v3.1": { + "id": "deepseek-v3.1", + "name": "DeepSeek-V3.1", + "attachment": false, + "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-08-31", - "release_date": "2026-03-17", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-08-19", + "last_updated": "2025-08-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2e-7, "output": 0.00000125, "cache_read": 2e-8 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 128000, + "output": 32000 + } }, - "openai/gpt-5.2-chat": { - "id": "openai/gpt-5.2-chat", - "name": "GPT-5.2 Chat", - "family": "gpt-codex", - "attachment": true, + "qwen3-30b-a3b": { + "id": "qwen3-30b-a3b", + "name": "Qwen3 30B A3B", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": false, + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 40000, + "output": 4096 + } }, - "openai/gpt-5.2": { - "id": "openai/gpt-5.2", - "name": "GPT-5.2", - "family": "gpt", + "claude-4.1-opus": { + "id": "claude-4.1-opus", + "name": "Claude 4.1 Opus", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": false, + "temperature": true, + "release_date": "2025-08-06", + "last_updated": "2025-08-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 200000, + "output": 32000 + } }, - "openai/gpt-5.1": { - "id": "openai/gpt-5.1", - "name": "GPT-5.1", - "family": "gpt", + "doubao-seed-2.0-mini": { + "id": "doubao-seed-2.0-mini", + "name": "Doubao Seed 2.0 Mini", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-02-14", + "last_updated": "2026-02-14", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 256000, + "output": 32000 + } }, - "openai/gpt-4.1-mini": { - "id": "openai/gpt-4.1-mini", - "name": "GPT-4.1 Mini", - "family": "gpt-mini", - "attachment": true, + "qwen3-next-80b-a3b-instruct": { + "id": "qwen3-next-80b-a3b-instruct", + "name": "Qwen3 Next 80B A3B Instruct", + "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-09-12", + "last_updated": "2025-09-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 1.6, "cache_read": 0.1 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 131072, + "output": 32768 + } }, - "openai/gpt-5-nano": { - "id": "openai/gpt-5-nano", - "name": "GPT-5 Nano", - "family": "gpt-nano", + "doubao-seed-1.6": { + "id": "doubao-seed-1.6", + "name": "Doubao-Seed 1.6", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-10-01", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-15", + "last_updated": "2025-08-15", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.05, "output": 0.4 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 256000, + "output": 32000 + } }, - "openai/gpt-oss-safeguard-20b": { - "id": "openai/gpt-oss-safeguard-20b", - "name": "GPT OSS Safeguard 20B", - "family": "gpt-oss", - "attachment": false, - "reasoning": true, + "qwen2.5-vl-7b-instruct": { + "id": "qwen2.5-vl-7b-instruct", + "name": "Qwen 2.5 VL 7B Instruct", + "attachment": true, + "reasoning": false, "tool_call": true, + "structured_output": false, "temperature": true, - "release_date": "2025-10-29", - "last_updated": "2025-10-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.075, "output": 0.3 }, - "limit": { "context": 131072, "output": 65536 } + "limit": { + "context": 128000, + "output": 8192 + } }, - "openai/gpt-oss-20b": { - "id": "openai/gpt-oss-20b", - "name": "GPT OSS 20B", - "family": "gpt-oss", + "kling-v2-6": { + "id": "kling-v2-6", + "name": "Kling-V2 6", + "attachment": true, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "temperature": true, + "release_date": "2026-01-13", + "last_updated": "2026-01-13", + "modalities": { + "input": ["text", "image", "video"], + "output": ["video"] + }, + "open_weights": false, + "limit": { + "context": 99999999, + "output": 99999999 + } + }, + "MiniMax-M1": { + "id": "MiniMax-M1", + "name": "MiniMax M1", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, "release_date": "2025-08-05", "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.05, "output": 0.2 }, - "limit": { "context": 131072, "output": 32768 } + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 80000 + } }, - "openai/gpt-5-image": { - "id": "openai/gpt-5-image", - "name": "GPT-5 Image", - "family": "gpt", + "gemini-3.0-pro-preview": { + "id": "gemini-3.0-pro-preview", + "name": "Gemini 3.0 Pro Preview", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-10-01", - "release_date": "2025-10-14", - "last_updated": "2025-10-14", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text", "image"] }, + "release_date": "2025-11-19", + "last_updated": "2025-11-19", + "modalities": { + "input": ["text", "image", "video", "pdf", "audio"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 10, "cache_read": 1.25 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 1000000, + "output": 64000 + } }, - "openai/gpt-5-codex": { - "id": "openai/gpt-5-codex", - "name": "GPT-5 Codex", - "family": "gpt-codex", + "doubao-seed-2.0-lite": { + "id": "doubao-seed-2.0-lite", + "name": "Doubao Seed 2.0 Lite", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-10-01", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-02-14", + "last_updated": "2026-02-14", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 256000, + "output": 32000 + } }, - "openai/o4-mini": { - "id": "openai/o4-mini", - "name": "o4 Mini", - "family": "o-mini", - "attachment": true, - "reasoning": true, + "qwen3-coder-480b-a35b-instruct": { + "id": "qwen3-coder-480b-a35b-instruct", + "name": "Qwen3 Coder 480B A35B Instruct", + "attachment": false, + "reasoning": false, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-06", - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-14", + "last_updated": "2025-08-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.28 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 262000, + "output": 4096 + } }, - "openai/gpt-5.1-codex": { - "id": "openai/gpt-5.1-codex", - "name": "GPT-5.1-Codex", - "family": "gpt-codex", + "claude-3.5-haiku": { + "id": "claude-3.5-haiku", + "name": "Claude 3.5 Haiku", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-26", + "last_updated": "2025-08-26", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 200000, + "output": 8192 + } }, - "openai/gpt-5.2-pro": { - "id": "openai/gpt-5.2-pro", - "name": "GPT-5.2 Pro", - "family": "gpt-pro", - "attachment": true, + "gpt-oss-20b": { + "id": "gpt-oss-20b", + "name": "gpt-oss-20b", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": false, + "temperature": true, + "release_date": "2025-08-06", + "last_updated": "2025-08-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 21, "output": 168 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 128000, + "output": 4096 + } }, - "prime-intellect/intellect-3": { - "id": "prime-intellect/intellect-3", - "name": "Intellect 3", - "family": "glm", + "qwen-turbo": { + "id": "qwen-turbo", + "name": "Qwen-Turbo", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-01-15", - "last_updated": "2025-01-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 1.1 }, - "limit": { "context": 131072, "output": 8192 } + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 4096 + } }, - "x-ai/grok-4-fast": { - "id": "x-ai/grok-4-fast", - "name": "Grok 4 Fast", - "family": "grok", + "kimi-k2": { + "id": "kimi-k2", + "name": "Kimi K2", + "attachment": false, + "reasoning": false, + "tool_call": true, + "structured_output": false, + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "gemini-2.5-flash-lite": { + "id": "gemini-2.5-flash-lite", + "name": "Gemini 2.5 Flash Lite", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": false, + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 64000 + } + }, + "qwen3-max-preview": { + "id": "qwen3-max-preview", + "name": "Qwen3 Max Preview", + "attachment": false, + "reasoning": false, + "tool_call": true, + "structured_output": false, + "temperature": true, + "release_date": "2025-09-06", + "last_updated": "2025-09-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "gpt-oss-120b": { + "id": "gpt-oss-120b", + "name": "gpt-oss-120b", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-08-19", - "last_updated": "2025-08-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-06", + "last_updated": "2025-08-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05, "cache_write": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } + "limit": { + "context": 128000, + "output": 4096 + } }, - "x-ai/grok-4.20-beta": { - "id": "x-ai/grok-4.20-beta", - "name": "Grok 4.20 Beta", - "family": "grok", + "doubao-1.5-vision-pro": { + "id": "doubao-1.5-vision-pro", + "name": "Doubao 1.5 Vision Pro", + "attachment": true, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16000 + } + }, + "claude-4.5-sonnet": { + "id": "claude-4.5-sonnet", + "name": "Claude 4.5 Sonnet", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": false, "temperature": true, - "release_date": "2026-03-12", - "last_updated": "2026-03-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 6, "cache_read": 0.2, "context_over_200k": { "input": 4, "output": 12 } }, - "limit": { "context": 2000000, "output": 30000 }, - "status": "beta" + "limit": { + "context": 200000, + "output": 64000 + } }, - "x-ai/grok-4.1-fast": { - "id": "x-ai/grok-4.1-fast", - "name": "Grok 4.1 Fast", - "family": "grok", + "deepseek-v3": { + "id": "deepseek-v3", + "name": "DeepSeek-V3", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "temperature": true, + "release_date": "2025-08-13", + "last_updated": "2025-08-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16000 + } + }, + "deepseek-r1-0528": { + "id": "deepseek-r1-0528", + "name": "DeepSeek-R1-0528", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-11-19", - "last_updated": "2025-11-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05, "cache_write": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } + "limit": { + "context": 128000, + "output": 32000 + } }, - "x-ai/grok-4": { - "id": "x-ai/grok-4", - "name": "Grok 4", - "family": "grok", - "attachment": false, + "gemini-2.0-flash-lite": { + "id": "gemini-2.0-flash-lite", + "name": "Gemini 2.0 Flash Lite", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.75, "cache_write": 15 }, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 1048576, + "output": 8192 + } }, - "x-ai/grok-code-fast-1": { - "id": "x-ai/grok-code-fast-1", - "name": "Grok Code Fast 1", - "family": "grok", + "qwen-max-2025-01-25": { + "id": "qwen-max-2025-01-25", + "name": "Qwen2.5-Max-2025-01-25", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-08", - "release_date": "2025-08-26", - "last_updated": "2025-08-26", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 1.5, "cache_read": 0.02 }, - "limit": { "context": 256000, "output": 10000 } + "limit": { + "context": 128000, + "output": 4096 + } }, - "x-ai/grok-4.20-multi-agent-beta": { - "id": "x-ai/grok-4.20-multi-agent-beta", - "name": "Grok 4.20 Multi - Agent Beta", - "family": "grok", + "doubao-seed-2.0-pro": { + "id": "doubao-seed-2.0-pro", + "name": "Doubao Seed 2.0 Pro", "attachment": true, "reasoning": true, + "tool_call": true, + "structured_output": false, + "temperature": true, + "release_date": "2026-02-14", + "last_updated": "2026-02-14", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 128000 + } + }, + "deepseek/deepseek-v3.2-exp-thinking": { + "id": "deepseek/deepseek-v3.2-exp-thinking", + "name": "DeepSeek/DeepSeek-V3.2-Exp-Thinking", + "attachment": false, + "reasoning": true, "tool_call": false, + "structured_output": false, "temperature": true, - "release_date": "2026-03-12", - "last_updated": "2026-03-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 6, "cache_read": 0.2, "context_over_200k": { "input": 4, "output": 12 } }, - "limit": { "context": 2000000, "output": 30000 }, - "status": "beta" + "limit": { + "context": 128000, + "output": 32000 + } }, - "x-ai/grok-3-mini": { - "id": "x-ai/grok-3-mini", - "name": "Grok 3 Mini", - "family": "grok", + "deepseek/deepseek-v3.1-terminus-thinking": { + "id": "deepseek/deepseek-v3.1-terminus-thinking", + "name": "DeepSeek/DeepSeek-V3.1-Terminus-Thinking", "attachment": false, "reasoning": true, - "tool_call": true, - "structured_output": true, + "tool_call": false, + "structured_output": false, "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-09-22", + "last_updated": "2025-09-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 0.5, "cache_read": 0.075, "cache_write": 0.5 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 128000, + "output": 32000 + } }, - "x-ai/grok-3-beta": { - "id": "x-ai/grok-3-beta", - "name": "Grok 3 Beta", - "family": "grok", + "deepseek/deepseek-v3.2-exp": { + "id": "deepseek/deepseek-v3.2-exp", + "name": "DeepSeek/DeepSeek-V3.2-Exp", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.75, "cache_write": 15 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 128000, + "output": 32000 + } }, - "x-ai/grok-3": { - "id": "x-ai/grok-3", - "name": "Grok 3", - "family": "grok", + "deepseek/deepseek-v3.2-251201": { + "id": "deepseek/deepseek-v3.2-251201", + "name": "Deepseek/DeepSeek-V3.2", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.75, "cache_write": 15 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 128000, + "output": 32000 + } }, - "x-ai/grok-3-mini-beta": { - "id": "x-ai/grok-3-mini-beta", - "name": "Grok 3 Mini Beta", - "family": "grok", + "deepseek/deepseek-math-v2": { + "id": "deepseek/deepseek-math-v2", + "name": "Deepseek/Deepseek-Math-V2", "attachment": false, "reasoning": true, + "tool_call": false, + "structured_output": false, + "temperature": true, + "release_date": "2025-12-04", + "last_updated": "2025-12-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 160000, + "output": 160000 + } + }, + "deepseek/deepseek-v3.1-terminus": { + "id": "deepseek/deepseek-v3.1-terminus", + "name": "DeepSeek/DeepSeek-V3.1-Terminus", + "attachment": false, + "reasoning": false, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-09-22", + "last_updated": "2025-09-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 0.5, "cache_read": 0.075, "cache_write": 0.5 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 128000, + "output": 32000 + } }, - "anthropic/claude-opus-4.6": { - "id": "anthropic/claude-opus-4.6", - "name": "Claude Opus 4.6", - "family": "claude-opus", + "stepfun-ai/gelab-zero-4b-preview": { + "id": "stepfun-ai/gelab-zero-4b-preview", + "name": "Stepfun-Ai/Gelab Zero 4b Preview", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-05-30", - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { - "input": 5, - "output": 25, - "cache_read": 0.5, - "cache_write": 6.25, - "context_over_200k": { "input": 10, "output": 37.5, "cache_read": 1, "cache_write": 12.5 } + "limit": { + "context": 8192, + "output": 4096 + } + }, + "stepfun/step-3.5-flash": { + "id": "stepfun/step-3.5-flash", + "name": "Stepfun/Step-3.5 Flash", + "attachment": true, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "temperature": true, + "release_date": "2026-02-02", + "last_updated": "2026-02-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] }, - "limit": { "context": 1000000, "output": 128000 } + "open_weights": false, + "limit": { + "context": 64000, + "output": 4096 + } }, - "anthropic/claude-haiku-4.5": { - "id": "anthropic/claude-haiku-4.5", - "name": "Claude Haiku 4.5", - "family": "claude-haiku", + "x-ai/grok-4-fast": { + "id": "x-ai/grok-4-fast", + "name": "x-AI/Grok-4-Fast", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-02-28", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-09-20", + "last_updated": "2025-09-20", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 2000000, + "output": 2000000 + } }, - "anthropic/claude-opus-4.1": { - "id": "anthropic/claude-opus-4.1", - "name": "Claude Opus 4.1", - "family": "claude-opus", - "attachment": true, + "x-ai/grok-code-fast-1": { + "id": "x-ai/grok-code-fast-1", + "name": "x-AI/Grok-Code-Fast 1", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-09-02", + "last_updated": "2025-09-02", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 256000, + "output": 10000 + } }, - "anthropic/claude-3.7-sonnet": { - "id": "anthropic/claude-3.7-sonnet", - "name": "Claude Sonnet 3.7", - "family": "claude-sonnet", + "x-ai/grok-4-fast-reasoning": { + "id": "x-ai/grok-4-fast-reasoning", + "name": "X-Ai/Grok-4-Fast-Reasoning", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-01", - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-12-18", + "last_updated": "2025-12-18", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 128000 } + "limit": { + "context": 2000000, + "output": 2000000 + } }, - "anthropic/claude-sonnet-4.6": { - "id": "anthropic/claude-sonnet-4.6", - "name": "Claude Sonnet 4.6", - "family": "claude-sonnet", + "x-ai/grok-4.1-fast-non-reasoning": { + "id": "x-ai/grok-4.1-fast-non-reasoning", + "name": "X-Ai/Grok 4.1 Fast Non Reasoning", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "release_date": "2026-02-17", - "last_updated": "2026-02-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75, - "context_over_200k": { "input": 6, "output": 22.5, "cache_read": 0.6, "cache_write": 7.5 } + "release_date": "2025-12-19", + "last_updated": "2025-12-19", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] }, - "limit": { "context": 1000000, "output": 128000 } + "open_weights": false, + "limit": { + "context": 2000000, + "output": 2000000 + } }, - "anthropic/claude-sonnet-4": { - "id": "anthropic/claude-sonnet-4", - "name": "Claude Sonnet 4", - "family": "claude-sonnet", - "attachment": true, + "x-ai/grok-4.1-fast": { + "id": "x-ai/grok-4.1-fast", + "name": "x-AI/Grok-4.1-Fast", + "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75, - "context_over_200k": { "input": 6, "output": 22.5, "cache_read": 0.6, "cache_write": 7.5 } + "release_date": "2025-11-20", + "last_updated": "2025-11-20", + "modalities": { + "input": ["text"], + "output": ["text"] }, - "limit": { "context": 200000, "output": 64000 } + "open_weights": false, + "limit": { + "context": 2000000, + "output": 2000000 + } }, - "anthropic/claude-3.5-haiku": { - "id": "anthropic/claude-3.5-haiku", - "name": "Claude Haiku 3.5", - "family": "claude-haiku", + "x-ai/grok-4-fast-non-reasoning": { + "id": "x-ai/grok-4-fast-non-reasoning", + "name": "X-Ai/Grok-4-Fast-Non-Reasoning", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-07-31", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-12-18", + "last_updated": "2025-12-18", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.8, "output": 4, "cache_read": 0.08, "cache_write": 1 }, - "limit": { "context": 200000, "output": 8192 } + "limit": { + "context": 2000000, + "output": 2000000 + } }, - "anthropic/claude-opus-4.5": { - "id": "anthropic/claude-opus-4.5", - "name": "Claude Opus 4.5", - "family": "claude-opus", + "x-ai/grok-4.1-fast-reasoning": { + "id": "x-ai/grok-4.1-fast-reasoning", + "name": "X-Ai/Grok 4.1 Fast Reasoning", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-05-30", - "release_date": "2025-11-24", - "last_updated": "2025-11-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-12-19", + "last_updated": "2025-12-19", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 20000000, + "output": 2000000 + } }, - "anthropic/claude-sonnet-4.5": { - "id": "anthropic/claude-sonnet-4.5", - "name": "Claude Sonnet 4.5", - "family": "claude-sonnet", + "openai/gpt-5.2": { + "id": "openai/gpt-5.2", + "name": "OpenAI/GPT-5.2", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75, - "context_over_200k": { "input": 6, "output": 22.5, "cache_read": 0.6, "cache_write": 7.5 } + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] }, - "limit": { "context": 1000000, "output": 64000 } + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + } }, - "anthropic/claude-opus-4": { - "id": "anthropic/claude-opus-4", - "name": "Claude Opus 4", - "family": "claude-opus", - "attachment": true, - "reasoning": true, + "openai/gpt-5": { + "id": "openai/gpt-5", + "name": "OpenAI/GPT-5", + "attachment": false, + "reasoning": false, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-09-19", + "last_updated": "2025-09-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 400000, + "output": 128000 + } }, - "deepseek/deepseek-v3.1-terminus": { - "id": "deepseek/deepseek-v3.1-terminus", - "name": "DeepSeek V3.1 Terminus", - "family": "deepseek", + "z-ai/glm-4.7": { + "id": "z-ai/glm-4.7", + "name": "Z-Ai/GLM 4.7", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-09-22", - "last_updated": "2025-09-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.27, "output": 1 }, - "limit": { "context": 131072, "output": 65536 } + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 200000 + } }, - "deepseek/deepseek-r1-distill-llama-70b": { - "id": "deepseek/deepseek-r1-distill-llama-70b", - "name": "DeepSeek R1 Distill Llama 70B", - "family": "deepseek-thinking", + "z-ai/glm-5": { + "id": "z-ai/glm-5", + "name": "Z-Ai/GLM 5", "attachment": false, "reasoning": true, - "tool_call": false, - "structured_output": true, + "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-01-23", - "last_updated": "2025-01-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 8192, "output": 8192 } + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 128000 + } }, - "deepseek/deepseek-v3.1-terminus:exacto": { - "id": "deepseek/deepseek-v3.1-terminus:exacto", - "name": "DeepSeek V3.1 Terminus (exacto)", - "family": "deepseek", - "attachment": false, - "reasoning": true, + "z-ai/autoglm-phone-9b": { + "id": "z-ai/autoglm-phone-9b", + "name": "Z-Ai/Autoglm Phone 9b", + "attachment": true, + "reasoning": false, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-09-22", - "last_updated": "2025-09-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.27, "output": 1 }, - "limit": { "context": 131072, "output": 65536 } + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 12800, + "output": 4096 + } }, - "deepseek/deepseek-chat-v3.1": { - "id": "deepseek/deepseek-chat-v3.1", - "name": "DeepSeek-V3.1", - "family": "deepseek", + "z-ai/glm-4.6": { + "id": "z-ai/glm-4.6", + "name": "Z-AI/GLM 4.6", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-08-21", - "last_updated": "2025-08-21", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 163840, "output": 163840 } + "release_date": "2025-10-11", + "last_updated": "2025-10-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 200000 + } }, - "deepseek/deepseek-v3.2-speciale": { - "id": "deepseek/deepseek-v3.2-speciale", - "name": "DeepSeek V3.2 Speciale", - "family": "deepseek", + "minimax/minimax-m2": { + "id": "minimax/minimax-m2", + "name": "Minimax/Minimax-M2", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.27, "output": 0.41 }, - "limit": { "context": 163840, "output": 65536 } + "release_date": "2025-10-28", + "last_updated": "2025-10-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 128000 + } }, - "deepseek/deepseek-chat-v3-0324": { - "id": "deepseek/deepseek-chat-v3-0324", - "name": "DeepSeek V3 0324", - "family": "deepseek", + "minimax/minimax-m2.1": { + "id": "minimax/minimax-m2.1", + "name": "Minimax/Minimax-M2.1", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": true, + "reasoning": true, + "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-03-24", - "last_updated": "2025-03-24", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 16384, "output": 8192 } + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 204800, + "output": 128000 + } }, - "deepseek/deepseek-v3.2": { - "id": "deepseek/deepseek-v3.2", - "name": "DeepSeek V3.2", - "family": "deepseek", + "minimax/minimax-m2.5": { + "id": "minimax/minimax-m2.5", + "name": "Minimax/Minimax-M2.5", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.28, "output": 0.4 }, - "limit": { "context": 163840, "output": 65536 } + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 204800, + "output": 128000 + } }, - "nvidia/nemotron-nano-9b-v2:free": { - "id": "nvidia/nemotron-nano-9b-v2:free", - "name": "Nemotron Nano 9B V2 (free)", - "family": "nemotron", + "minimax/minimax-m2.5-highspeed": { + "id": "minimax/minimax-m2.5-highspeed", + "name": "Minimax/Minimax-M2.5 Highspeed", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-09", - "release_date": "2025-09-05", - "last_updated": "2025-08-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 128000 } + "release_date": "2026-02-14", + "last_updated": "2026-02-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 204800, + "output": 128000 + } }, - "nvidia/nemotron-3-super-120b-a12b": { - "id": "nvidia/nemotron-3-super-120b-a12b", - "name": "Nemotron 3 Super", - "family": "nemotron", - "attachment": false, - "reasoning": true, + "moonshotai/kimi-k2.5": { + "id": "moonshotai/kimi-k2.5", + "name": "Moonshotai/Kimi-K2.5", + "attachment": true, + "reasoning": false, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-04", - "release_date": "2026-03-11", - "last_updated": "2026-03-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.5 }, - "limit": { "context": 262144, "output": 262144 } + "release_date": "2026-01-28", + "last_updated": "2026-01-28", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 256000 + } }, - "nvidia/nemotron-nano-12b-v2-vl:free": { - "id": "nvidia/nemotron-nano-12b-v2-vl:free", - "name": "Nemotron Nano 12B 2 VL (free)", - "family": "nemotron", + "moonshotai/kimi-k2-0905": { + "id": "moonshotai/kimi-k2-0905", + "name": "Kimi K2 0905", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-11", - "release_date": "2025-10-28", - "last_updated": "2026-01-31", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 128000 } + "release_date": "2025-09-08", + "last_updated": "2025-09-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 100000 + } }, - "nvidia/nemotron-nano-9b-v2": { - "id": "nvidia/nemotron-nano-9b-v2", - "name": "nvidia-nemotron-nano-9b-v2", - "family": "nemotron", + "moonshotai/kimi-k2-thinking": { + "id": "moonshotai/kimi-k2-thinking", + "name": "Kimi K2 Thinking", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-09", - "release_date": "2025-08-18", - "last_updated": "2025-08-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.04, "output": 0.16 }, - "limit": { "context": 131072, "output": 131072 } + "release_date": "2025-11-07", + "last_updated": "2025-11-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 100000 + } }, - "nvidia/nemotron-3-super-120b-a12b:free": { - "id": "nvidia/nemotron-3-super-120b-a12b:free", - "name": "Nemotron 3 Super (free)", - "family": "nemotron", + "meituan/longcat-flash-chat": { + "id": "meituan/longcat-flash-chat", + "name": "Meituan/Longcat-Flash-Chat", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, + "structured_output": false, "temperature": true, - "knowledge": "2024-04", - "release_date": "2026-03-11", - "last_updated": "2026-03-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 262144 } + "release_date": "2025-11-05", + "last_updated": "2025-11-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 131072 + } }, - "nvidia/nemotron-3-nano-30b-a3b:free": { - "id": "nvidia/nemotron-3-nano-30b-a3b:free", - "name": "Nemotron 3 Nano 30B A3B (free)", - "family": "nemotron", + "meituan/longcat-flash-lite": { + "id": "meituan/longcat-flash-lite", + "name": "Meituan/Longcat-Flash-Lite", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-11", - "release_date": "2025-12-14", - "last_updated": "2026-01-31", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 256000 } + "release_date": "2026-02-06", + "last_updated": "2026-02-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 320000 + } }, - "z-ai/glm-4.7-flash": { - "id": "z-ai/glm-4.7-flash", - "name": "GLM-4.7-Flash", - "family": "glm", + "mimo-v2-flash": { + "id": "mimo-v2-flash", + "name": "Mimo-V2-Flash", + "family": "mimo", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_details" }, - "structured_output": true, "temperature": true, - "release_date": "2026-01-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12-01", + "release_date": "2025-12-16", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.07, "output": 0.4 }, - "limit": { "context": 200000, "output": 65535 } + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.1, + "output": 0.3, + "cache_read": 0.01 + } }, - "z-ai/glm-4.5": { - "id": "z-ai/glm-4.5", - "name": "GLM 4.5", - "family": "glm", + "xiaomi/mimo-v2-flash": { + "id": "xiaomi/mimo-v2-flash", + "name": "Xiaomi/Mimo-V2-Flash", + "family": "mimo", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12-01", + "release_date": "2025-12-16", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.2 }, - "limit": { "context": 128000, "output": 96000 } - }, - "z-ai/glm-4.6": { - "id": "z-ai/glm-4.6", - "name": "GLM 4.6", - "family": "glm", - "attachment": false, - "reasoning": true, + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.1, + "output": 0.3, + "cache_read": 0.01 + } + } + } + }, + "kilo": { + "id": "kilo", + "env": ["KILO_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.kilo.ai/api/gateway", + "name": "Kilo Gateway", + "doc": "https://kilo.ai", + "models": { + "rekaai/reka-edge": { + "id": "rekaai/reka-edge", + "name": "Reka Edge", + "attachment": true, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-20", + "last_updated": "2026-04-11", + "modalities": { + "input": ["image", "text", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.2, "cache_read": 0.11 }, - "limit": { "context": 200000, "output": 128000 } + "limit": { + "context": 16384, + "output": 16384 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "z-ai/glm-4.5-air:free": { - "id": "z-ai/glm-4.5-air:free", - "name": "GLM 4.5 Air (free)", - "family": "glm-air", + "rekaai/reka-flash-3": { + "id": "rekaai/reka-flash-3", + "name": "Reka Flash 3", "attachment": false, "reasoning": true, "tool_call": false, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-03-12", + "last_updated": "2026-04-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 96000 } + "limit": { + "context": 65536, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.2 + } }, - "z-ai/glm-5": { - "id": "z-ai/glm-5", - "name": "GLM-5", - "family": "glm", + "ai21/jamba-large-1.7": { + "id": "ai21/jamba-large-1.7", + "name": "AI21: Jamba Large 1.7", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1, "output": 3.2, "cache_read": 0.2 }, - "limit": { "context": 202752, "output": 131000 } + "release_date": "2025-08-09", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 4096 + }, + "cost": { + "input": 2, + "output": 8 + } }, - "z-ai/glm-4.5-air": { - "id": "z-ai/glm-4.5-air", - "name": "GLM 4.5 Air", - "family": "glm-air", + "alibaba/tongyi-deepresearch-30b-a3b": { + "id": "alibaba/tongyi-deepresearch-30b-a3b", + "name": "Tongyi DeepResearch 30B A3B", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 1.1 }, - "limit": { "context": 128000, "output": 96000 } + "release_date": "2025-09-18", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.09, + "output": 0.45 + } }, - "z-ai/glm-4.5v": { - "id": "z-ai/glm-4.5v", - "name": "GLM 4.5V", - "family": "glm", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, + "inflection/inflection-3-pi": { + "id": "inflection/inflection-3-pi", + "name": "Inflection: Inflection 3 Pi", + "attachment": false, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-08-11", - "last_updated": "2025-08-11", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 1.8 }, - "limit": { "context": 64000, "output": 16384 } + "release_date": "2024-10-11", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8000, + "output": 1024 + }, + "cost": { + "input": 2.5, + "output": 10 + } }, - "z-ai/glm-4.7": { - "id": "z-ai/glm-4.7", - "name": "GLM-4.7", - "family": "glm", + "inflection/inflection-3-productivity": { + "id": "inflection/inflection-3-productivity", + "name": "Inflection: Inflection 3 Productivity", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_details" }, - "structured_output": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.2, "cache_read": 0.11 }, - "limit": { "context": 204800, "output": 131072 } + "release_date": "2024-10-11", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8000, + "output": 1024 + }, + "cost": { + "input": 2.5, + "output": 10 + } }, - "z-ai/glm-4.6:exacto": { - "id": "z-ai/glm-4.6:exacto", - "name": "GLM 4.6 (exacto)", - "family": "glm", + "liquid/lfm-2-24b-a2b": { + "id": "liquid/lfm-2-24b-a2b", + "name": "LiquidAI: LFM2-24B-A2B", "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-26", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 1.9, "cache_read": 0.11 }, - "limit": { "context": 200000, "output": 128000 } + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.03, + "output": 0.12 + } }, - "sourceful/riverflow-v2-standard-preview": { - "id": "sourceful/riverflow-v2-standard-preview", - "name": "Riverflow V2 Standard Preview", - "family": "sourceful", + "writer/palmyra-x5": { + "id": "writer/palmyra-x5", + "name": "Writer: Palmyra X5", "attachment": false, "reasoning": false, "tool_call": false, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-12-08", - "last_updated": "2026-01-28", - "modalities": { "input": ["text", "image"], "output": ["image"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 8192, "output": 8192 } + "release_date": "2025-04-28", + "last_updated": "2025-04-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1040000, + "output": 8192 + }, + "cost": { + "input": 0.6, + "output": 6 + } }, - "sourceful/riverflow-v2-fast-preview": { - "id": "sourceful/riverflow-v2-fast-preview", - "name": "Riverflow V2 Fast Preview", - "family": "sourceful", + "ibm-granite/granite-4.1-8b": { + "id": "ibm-granite/granite-4.1-8b", + "name": "IBM: Granite 4.1 8B", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-12-08", - "last_updated": "2026-01-28", - "modalities": { "input": ["text", "image"], "output": ["image"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 8192, "output": 8192 } + "release_date": "2026-04-30", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.05, + "output": 0.1, + "cache_read": 0.05 + } }, - "sourceful/riverflow-v2-max-preview": { - "id": "sourceful/riverflow-v2-max-preview", - "name": "Riverflow V2 Max Preview", - "family": "sourceful", + "ibm-granite/granite-4.0-h-micro": { + "id": "ibm-granite/granite-4.0-h-micro", + "name": "IBM: Granite 4.0 Micro", "attachment": false, "reasoning": false, "tool_call": false, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-12-08", - "last_updated": "2026-01-28", - "modalities": { "input": ["text", "image"], "output": ["image"] }, + "release_date": "2025-10-20", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 8192, "output": 8192 } + "limit": { + "context": 131000, + "output": 32768 + }, + "cost": { + "input": 0.017, + "output": 0.11 + } }, - "cognitivecomputations/dolphin-mistral-24b-venice-edition:free": { - "id": "cognitivecomputations/dolphin-mistral-24b-venice-edition:free", - "name": "Uncensored (free)", - "family": "mistral", + "essentialai/rnj-1-instruct": { + "id": "essentialai/rnj-1-instruct", + "name": "EssentialAI: Rnj 1 Instruct", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-12-05", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 6554 + }, + "cost": { + "input": 0.15, + "output": 0.15 + } + }, + "perplexity/sonar-pro": { + "id": "perplexity/sonar-pro", + "name": "Perplexity: Sonar Pro", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-01-01", + "last_updated": "2025-09-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 8000 + }, + "cost": { + "input": 3, + "output": 15 + } + }, + "perplexity/sonar-deep-research": { + "id": "perplexity/sonar-deep-research", + "name": "Perplexity: Sonar Deep Research", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2025-01-27", + "last_updated": "2025-01-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 25600 + }, + "cost": { + "input": 2, + "output": 8 + } + }, + "perplexity/sonar": { + "id": "perplexity/sonar", + "name": "Perplexity: Sonar", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-01-01", + "last_updated": "2025-09-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 127072, + "output": 25415 + }, + "cost": { + "input": 1, + "output": 1 + } + }, + "perplexity/sonar-pro-search": { + "id": "perplexity/sonar-pro-search", + "name": "Perplexity: Sonar Pro Search", + "attachment": true, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2025-10-31", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 8000 + }, + "cost": { + "input": 3, + "output": 15 + } + }, + "perplexity/sonar-reasoning-pro": { + "id": "perplexity/sonar-reasoning-pro", + "name": "Perplexity: Sonar Reasoning Pro", + "attachment": true, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2024-01-01", + "last_updated": "2025-09-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 25600 + }, + "cost": { + "input": 2, + "output": 8 + } + }, + "deepseek/deepseek-chat-v3.1": { + "id": "deepseek/deepseek-chat-v3.1", + "name": "DeepSeek: DeepSeek V3.1", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-08-21", + "last_updated": "2025-08-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 7168 + }, + "cost": { + "input": 0.15, + "output": 0.75 + } + }, + "deepseek/deepseek-chat": { + "id": "deepseek/deepseek-chat", + "name": "DeepSeek: DeepSeek V3", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-12-01", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 163840 + }, + "cost": { + "input": 0.32, + "output": 0.89, + "cache_read": 0.15 + } + }, + "deepseek/deepseek-r1-distill-llama-70b": { + "id": "deepseek/deepseek-r1-distill-llama-70b", + "name": "DeepSeek: R1 Distill Llama 70B", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2025-01-23", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.7, + "output": 0.8, + "cache_read": 0.015 + } + }, + "deepseek/deepseek-r1": { + "id": "deepseek/deepseek-r1", + "name": "DeepSeek: R1", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 64000, + "output": 16000 + }, + "cost": { + "input": 0.7, + "output": 2.5 + } + }, + "deepseek/deepseek-v3.2-speciale": { + "id": "deepseek/deepseek-v3.2-speciale", + "name": "DeepSeek: DeepSeek V3.2 Speciale", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2025-12-01", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 163840 + }, + "cost": { + "input": 0.4, + "output": 1.2, + "cache_read": 0.135 + } + }, + "deepseek/deepseek-r1-distill-qwen-32b": { + "id": "deepseek/deepseek-r1-distill-qwen-32b", + "name": "DeepSeek: R1 Distill Qwen 32B", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2025-01-01", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.29, + "output": 0.29 + } + }, + "deepseek/deepseek-v3.2-exp": { + "id": "deepseek/deepseek-v3.2-exp", + "name": "DeepSeek: DeepSeek V3.2 Exp", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-01-01", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 65536 + }, + "cost": { + "input": 0.27, + "output": 0.41 + } + }, + "deepseek/deepseek-v4-flash": { + "id": "deepseek/deepseek-v4-flash", + "name": "DeepSeek: DeepSeek V4 Flash", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-24", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 384000 + }, + "cost": { + "input": 0.14, + "output": 0.28, + "cache_read": 0.0028 + } + }, + "deepseek/deepseek-v4-pro": { + "id": "deepseek/deepseek-v4-pro", + "name": "DeepSeek: DeepSeek V4 Pro", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-24", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 384000 + }, + "cost": { + "input": 0.435, + "output": 0.87, + "cache_read": 0.003625 + } + }, + "deepseek/deepseek-v3.2": { + "id": "deepseek/deepseek-v3.2", + "name": "DeepSeek: DeepSeek V3.2", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-12-01", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 65536 + }, + "cost": { + "input": 0.26, + "output": 0.38, + "cache_read": 0.125 + } + }, + "deepseek/deepseek-chat-v3-0324": { + "id": "deepseek/deepseek-chat-v3-0324", + "name": "DeepSeek: DeepSeek V3 0324", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-03-24", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 65536 + }, + "cost": { + "input": 0.2, + "output": 0.77, + "cache_read": 0.095 + } + }, + "deepseek/deepseek-r1-0528": { + "id": "deepseek/deepseek-r1-0528", + "name": "DeepSeek: R1 0528", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-05-28", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 65536 + }, + "cost": { + "input": 0.45, + "output": 2.15, + "cache_read": 0.2 + } + }, + "deepseek/deepseek-v3.1-terminus": { + "id": "deepseek/deepseek-v3.1-terminus", + "name": "DeepSeek: DeepSeek V3.1 Terminus", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-09-22", + "last_updated": "2025-09-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 32768 + }, + "cost": { + "input": 0.21, + "output": 0.79, + "cache_read": 0.13 + } + }, + "openrouter/auto": { + "id": "openrouter/auto", + "name": "Auto Router", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-15", + "last_updated": "2026-03-15", + "modalities": { + "input": ["audio", "image", "pdf", "text", "video"], + "output": ["image", "text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "openrouter/bodybuilder": { + "id": "openrouter/bodybuilder", + "name": "Body Builder (beta)", "attachment": false, "reasoning": false, "tool_call": false, + "release_date": "2026-03-15", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 32768 + }, + "status": "beta", + "cost": { + "input": 0, + "output": 0 + } + }, + "openrouter/owl-alpha": { + "id": "openrouter/owl-alpha", + "name": "Owl Alpha", + "attachment": false, + "reasoning": true, + "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-07-09", - "last_updated": "2026-01-31", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-28", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048756, + "output": 262144 + }, + "status": "alpha", + "cost": { + "input": 0, + "output": 0 + } + }, + "openrouter/pareto-code": { + "id": "openrouter/pareto-code", + "name": "Pareto Code Router", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2026-04-21", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "openrouter/free": { + "id": "openrouter/free", + "name": "Free Models Router", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-01", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "inclusionai/ling-2.6-1t:free": { + "id": "inclusionai/ling-2.6-1t:free", + "name": "inclusionAI: Ling-2.6-1T (free)", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-23", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "inclusionai/ling-2.6-flash": { + "id": "inclusionai/ling-2.6-flash", + "name": "inclusionAI: Ling-2.6 Flash", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-21", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0.08, + "output": 0.24, + "cache_read": 0.016 + } + }, + "arcee-ai/trinity-mini": { + "id": "arcee-ai/trinity-mini", + "name": "Arcee AI: Trinity Mini", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-12", + "last_updated": "2026-01-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.045, + "output": 0.15 + } }, - "google/gemini-2.5-flash-lite": { - "id": "google/gemini-2.5-flash-lite", - "name": "Gemini 2.5 Flash Lite", - "family": "gemini-flash-lite", + "arcee-ai/virtuoso-large": { + "id": "arcee-ai/virtuoso-large", + "name": "Arcee AI: Virtuoso Large", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-05-06", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 64000 + }, + "cost": { + "input": 0.75, + "output": 1.2 + } + }, + "arcee-ai/trinity-large-thinking": { + "id": "arcee-ai/trinity-large-thinking", + "name": "Arcee AI: Trinity Large Thinking", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-01", + "last_updated": "2026-04-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.22, + "output": 0.85 + } + }, + "arcee-ai/spotlight": { + "id": "arcee-ai/spotlight", + "name": "Arcee AI: Spotlight", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-05-06", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 65537 + }, + "cost": { + "input": 0.18, + "output": 0.18 + } + }, + "arcee-ai/maestro-reasoning": { + "id": "arcee-ai/maestro-reasoning", + "name": "Arcee AI: Maestro Reasoning", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-05-06", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32000 + }, + "cost": { + "input": 0.9, + "output": 3.3 + } + }, + "arcee-ai/coder-large": { + "id": "arcee-ai/coder-large", + "name": "Arcee AI: Coder Large", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-05-06", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.5, + "output": 0.8 + } + }, + "arcee-ai/trinity-large-preview": { + "id": "arcee-ai/trinity-large-preview", + "name": "Arcee AI: Trinity Large Preview", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2026-01-28", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 32768 + }, + "cost": { + "input": 0.15, + "output": 0.45 + } + }, + "deepcogito/cogito-v2.1-671b": { + "id": "deepcogito/cogito-v2.1-671b", + "name": "Deep Cogito: Cogito v2.1 671B", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2025-11-14", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 1.25, + "output": 1.25 + } + }, + "upstage/solar-pro-3": { + "id": "upstage/solar-pro-3", + "name": "Upstage: Solar Pro 3", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-01-27", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } + }, + "nex-agi/deepseek-v3.1-nex-n1": { + "id": "nex-agi/deepseek-v3.1-nex-n1", + "name": "Nex AGI: DeepSeek V3.1 Nex N1", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-01-01", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 163840 + }, + "cost": { + "input": 0.27, + "output": 1 + } + }, + "bytedance-seed/seed-1.6": { + "id": "bytedance-seed/seed-1.6", + "name": "ByteDance Seed: Seed 1.6", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-09", + "last_updated": "2025-09", + "modalities": { + "input": ["image", "text", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0.25, + "output": 2 + } + }, + "bytedance-seed/seed-2.0-lite": { + "id": "bytedance-seed/seed-2.0-lite", + "name": "ByteDance Seed: Seed-2.0-Lite", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-10", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0.25, + "output": 2 + } + }, + "bytedance-seed/seed-1.6-flash": { + "id": "bytedance-seed/seed-1.6-flash", + "name": "ByteDance Seed: Seed 1.6 Flash", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-12-23", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0.075, + "output": 0.3 + } + }, + "bytedance-seed/seed-2.0-mini": { + "id": "bytedance-seed/seed-2.0-mini", + "name": "ByteDance Seed: Seed-2.0-Mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-27", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } + }, + "mancer/weaver": { + "id": "mancer/weaver", + "name": "Mancer: Weaver (alpha)", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2023-08-02", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8000, + "output": 2000 + }, + "cost": { + "input": 0.75, + "output": 1 + } + }, + "anthracite-org/magnum-v4-72b": { + "id": "anthracite-org/magnum-v4-72b", + "name": "Magnum v4 72B", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-10-22", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 16384, + "output": 2048 + }, + "cost": { + "input": 3, + "output": 5 + } + }, + "~google/gemini-pro-latest": { + "id": "~google/gemini-pro-latest", + "name": "Google: Gemini Pro Latest", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2, + "cache_write": 0.375 + } + }, + "~google/gemini-flash-latest": { + "id": "~google/gemini-flash-latest", + "name": "Google: Gemini Flash Latest", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.05, + "cache_write": 0.08333333333333334 + } + }, + "kilo-auto/balanced": { + "id": "kilo-auto/balanced", + "name": "Kilo Auto Balanced", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-15", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 3 + } + }, + "kilo-auto/frontier": { + "id": "kilo-auto/frontier", + "name": "Kilo Auto Frontier", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-15", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25 + } + }, + "kilo-auto/small": { + "id": "kilo-auto/small", + "name": "Kilo Auto Small", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-15", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.05, + "output": 0.4 + } + }, + "kilo-auto/free": { + "id": "kilo-auto/free", + "name": "Kilo Auto Free", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-15", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "undi95/remm-slerp-l2-13b": { + "id": "undi95/remm-slerp-l2-13b", + "name": "ReMM SLERP 13B", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2023-07-22", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 6144, + "output": 4096 + }, + "cost": { + "input": 0.45, + "output": 0.65 + } + }, + "allenai/olmo-3-32b-think": { + "id": "allenai/olmo-3-32b-think", + "name": "AllenAI: Olmo 3 32B Think", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2025-11-22", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 65536, + "output": 65536 + }, + "cost": { + "input": 0.15, + "output": 0.5 + } + }, + "nousresearch/hermes-2-pro-llama-3-8b": { + "id": "nousresearch/hermes-2-pro-llama-3-8b", + "name": "NousResearch: Hermes 2 Pro - Llama-3 8B", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-05-27", + "last_updated": "2024-06-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 0.14, + "output": 0.14 + } + }, + "nousresearch/hermes-4-405b": { + "id": "nousresearch/hermes-4-405b", + "name": "Nous: Hermes 4 405B", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2025-08-25", + "last_updated": "2025-08-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 26215 + }, + "cost": { + "input": 1, + "output": 3 + } + }, + "nousresearch/hermes-3-llama-3.1-70b": { + "id": "nousresearch/hermes-3-llama-3.1-70b", + "name": "Nous: Hermes 3 70B Instruct", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-08-18", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.3, + "output": 0.3 + } + }, + "nousresearch/hermes-4-70b": { + "id": "nousresearch/hermes-4-70b", + "name": "Nous: Hermes 4 70B", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2025-08-25", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.13, + "output": 0.4, + "cache_read": 0.055 + } + }, + "nousresearch/hermes-3-llama-3.1-405b": { + "id": "nousresearch/hermes-3-llama-3.1-405b", + "name": "Nous: Hermes 3 405B Instruct", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-08-16", + "last_updated": "2024-08-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 1, + "output": 1 + } + }, + "morph/morph-v3-fast": { + "id": "morph/morph-v3-fast", + "name": "Morph: Morph V3 Fast", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-08-15", + "last_updated": "2024-08-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 81920, + "output": 38000 + }, + "cost": { + "input": 0.8, + "output": 1.2 + } + }, + "morph/morph-v3-large": { + "id": "morph/morph-v3-large", + "name": "Morph: Morph V3 Large", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-08-15", + "last_updated": "2024-08-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0.9, + "output": 1.9 + } + }, + "stepfun/step-3.5-flash:free": { + "id": "stepfun/step-3.5-flash:free", + "name": "StepFun: Step 3.5 Flash (free)", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-08-26", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "stepfun/step-3.5-flash": { + "id": "stepfun/step-3.5-flash", + "name": "StepFun: Step 3.5 Flash", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-01-29", + "last_updated": "2026-01-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.1, + "output": 0.3, + "cache_read": 0.02 + } + }, + "alpindale/goliath-120b": { + "id": "alpindale/goliath-120b", + "name": "Goliath 120B", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2023-11-10", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 6144, + "output": 1024 + }, + "cost": { + "input": 3.75, + "output": 7.5 + } + }, + "mistralai/mistral-nemo": { + "id": "mistralai/mistral-nemo", + "name": "Mistral: Mistral Nemo", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-07-01", + "last_updated": "2024-07-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.02, + "output": 0.04 + } + }, + "mistralai/mistral-saba": { + "id": "mistralai/mistral-saba", + "name": "Mistral: Saba", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-02-17", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.2, + "output": 0.6 + } + }, + "mistralai/mistral-large-2512": { + "id": "mistralai/mistral-large-2512", + "name": "Mistral: Mistral Large 3 2512", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-11-01", + "last_updated": "2025-12-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 52429 + }, + "cost": { + "input": 0.5, + "output": 1.5 + } + }, + "mistralai/devstral-medium": { + "id": "mistralai/devstral-medium", + "name": "Mistral: Devstral Medium", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-07-10", + "last_updated": "2025-07-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 26215 + }, + "cost": { + "input": 0.4, + "output": 2 + } + }, + "mistralai/mistral-small-3.1-24b-instruct": { + "id": "mistralai/mistral-small-3.1-24b-instruct", + "name": "Mistral: Mistral Small 3.1 24B", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-03-17", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 131072 + }, + "cost": { + "input": 0.35, + "output": 0.56, + "cache_read": 0.015 + } + }, + "mistralai/mistral-medium-3-5": { + "id": "mistralai/mistral-medium-3-5", + "name": "Mistral: Mistral Medium 3.5", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2026-04-30", + "last_updated": "2026-05-07", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 1.5, + "output": 7.5 + } + }, + "mistralai/pixtral-large-2411": { + "id": "mistralai/pixtral-large-2411", + "name": "Mistral: Pixtral Large 2411", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-11-19", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 6 + } + }, + "mistralai/devstral-2512": { + "id": "mistralai/devstral-2512", + "name": "Mistral: Devstral 2 2512", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-09-12", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.4, + "output": 2, + "cache_read": 0.025 + } + }, + "mistralai/codestral-2508": { + "id": "mistralai/codestral-2508", + "name": "Mistral: Codestral 2508", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-08-01", + "last_updated": "2025-08-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 51200 + }, + "cost": { + "input": 0.3, + "output": 0.9 + } + }, + "mistralai/mistral-small-24b-instruct-2501": { + "id": "mistralai/mistral-small-24b-instruct-2501", + "name": "Mistral: Mistral Small 3", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-12-29", + "last_updated": "2026-01-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 16384 + }, + "cost": { + "input": 0.05, + "output": 0.08 + } + }, + "mistralai/mistral-large-2411": { + "id": "mistralai/mistral-large-2411", + "name": "Mistral Large 2411", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-07-24", + "last_updated": "2024-11-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 26215 + }, + "cost": { + "input": 2, + "output": 6 + } + }, + "mistralai/mixtral-8x22b-instruct": { + "id": "mistralai/mixtral-8x22b-instruct", + "name": "Mistral: Mixtral 8x22B Instruct", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-04-17", + "last_updated": "2024-04-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 65536, + "output": 13108 + }, + "cost": { + "input": 2, + "output": 6 + } + }, + "mistralai/mistral-large-2407": { + "id": "mistralai/mistral-large-2407", + "name": "Mistral Large 2407", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-11-19", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 6 + } + }, + "mistralai/ministral-8b-2512": { + "id": "mistralai/ministral-8b-2512", + "name": "Mistral: Ministral 3 8B 2512", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-12-02", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0.15, + "output": 0.15 + } + }, + "mistralai/mistral-medium-3.1": { + "id": "mistralai/mistral-medium-3.1", + "name": "Mistral: Mistral Medium 3.1", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-08-12", + "last_updated": "2025-08-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 26215 + }, + "cost": { + "input": 0.4, + "output": 2 + } + }, + "mistralai/mistral-small-2603": { + "id": "mistralai/mistral-small-2603", + "name": "Mistral: Mistral Small 4", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-16", + "last_updated": "2026-04-11", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.015 + } + }, + "mistralai/ministral-3b-2512": { + "id": "mistralai/ministral-3b-2512", + "name": "Mistral: Ministral 3 3B 2512", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-12-02", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } + }, + "mistralai/voxtral-small-24b-2507": { + "id": "mistralai/voxtral-small-24b-2507", + "name": "Mistral: Voxtral Small 24B 2507", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-07-01", + "last_updated": "2025-07-01", + "modalities": { + "input": ["text", "audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32000, + "output": 6400 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } + }, + "mistralai/mixtral-8x7b-instruct": { + "id": "mistralai/mixtral-8x7b-instruct", + "name": "Mistral: Mixtral 8x7B Instruct", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2023-12-10", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 16384 + }, + "cost": { + "input": 0.54, + "output": 0.54 + } + }, + "mistralai/mistral-medium-3": { + "id": "mistralai/mistral-medium-3", + "name": "Mistral: Mistral Medium 3", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-05-07", + "last_updated": "2025-05-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 26215 + }, + "cost": { + "input": 0.4, + "output": 2 + } + }, + "mistralai/mistral-small-3.2-24b-instruct": { + "id": "mistralai/mistral-small-3.2-24b-instruct", + "name": "Mistral: Mistral Small 3.2 24B", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-06-20", + "last_updated": "2025-06-20", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.06, + "output": 0.18, + "cache_read": 0.03 + } + }, + "mistralai/devstral-small": { + "id": "mistralai/devstral-small", + "name": "Mistral: Devstral Small 1.1", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-05-07", + "last_updated": "2025-07-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 26215 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } + }, + "mistralai/mistral-large": { + "id": "mistralai/mistral-large", + "name": "Mistral Large", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-07-24", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 25600 + }, + "cost": { + "input": 2, + "output": 6 + } + }, + "mistralai/mistral-7b-instruct-v0.1": { + "id": "mistralai/mistral-7b-instruct-v0.1", + "name": "Mistral: Mistral 7B Instruct v0.1", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2824, + "output": 565 + }, + "cost": { + "input": 0.11, + "output": 0.19 + } + }, + "mistralai/ministral-14b-2512": { + "id": "mistralai/ministral-14b-2512", + "name": "Mistral: Ministral 3 14B 2512", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-12-16", + "last_updated": "2025-12-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 52429 + }, + "cost": { + "input": 0.2, + "output": 0.2 + } + }, + "~anthropic/claude-haiku-latest": { + "id": "~anthropic/claude-haiku-latest", + "name": "Anthropic: Claude Haiku Latest", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } + }, + "~anthropic/claude-sonnet-latest": { + "id": "~anthropic/claude-sonnet-latest", + "name": "Anthropic: Claude Sonnet Latest", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + "~anthropic/claude-opus-latest": { + "id": "~anthropic/claude-opus-latest", + "name": "Anthropic: Claude Opus Latest", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-04-16", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } + }, + "meta-llama/llama-3.3-70b-instruct": { + "id": "meta-llama/llama-3.3-70b-instruct", + "name": "Meta: Llama 3.3 70B Instruct", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-08-01", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.1, + "output": 0.32 + } + }, + "meta-llama/llama-4-scout": { + "id": "meta-llama/llama-4-scout", + "name": "Meta: Llama 4 Scout", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 327680, + "output": 16384 + }, + "cost": { + "input": 0.08, + "output": 0.3 + } + }, + "meta-llama/llama-guard-3-8b": { + "id": "meta-llama/llama-guard-3-8b", + "name": "Llama Guard 3 8B", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-04-18", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 26215 + }, + "cost": { + "input": 0.02, + "output": 0.06 + } + }, + "meta-llama/llama-4-maverick": { + "id": "meta-llama/llama-4-maverick", + "name": "Meta: Llama 4 Maverick", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-04-05", + "last_updated": "2025-12-24", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } + }, + "meta-llama/llama-3.2-11b-vision-instruct": { + "id": "meta-llama/llama-3.2-11b-vision-instruct", + "name": "Meta: Llama 3.2 11B Vision Instruct", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-09-25", + "last_updated": "2024-09-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.049, + "output": 0.049 + } + }, + "meta-llama/llama-guard-4-12b": { + "id": "meta-llama/llama-guard-4-12b", + "name": "Meta: Llama Guard 4 12B", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 32768 + }, + "cost": { + "input": 0.18, + "output": 0.18 + } + }, + "meta-llama/llama-3.1-70b-instruct": { + "id": "meta-llama/llama-3.1-70b-instruct", + "name": "Meta: Llama 3.1 70B Instruct", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-07-16", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 26215 + }, + "cost": { + "input": 0.4, + "output": 0.4 + } + }, + "meta-llama/llama-3.2-1b-instruct": { + "id": "meta-llama/llama-3.2-1b-instruct", + "name": "Meta: Llama 3.2 1B Instruct", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-09-18", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 60000, + "output": 12000 + }, + "cost": { + "input": 0.027, + "output": 0.2 + } + }, + "meta-llama/llama-3.2-3b-instruct": { + "id": "meta-llama/llama-3.2-3b-instruct", + "name": "Meta: Llama 3.2 3B Instruct", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-09-18", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 80000, + "output": 16384 + }, + "cost": { + "input": 0.051, + "output": 0.34 + } + }, + "meta-llama/llama-3-8b-instruct": { + "id": "meta-llama/llama-3-8b-instruct", + "name": "Meta: Llama 3 8B Instruct", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-04-25", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 16384 + }, + "cost": { + "input": 0.03, + "output": 0.04 + } + }, + "meta-llama/llama-3.1-8b-instruct": { + "id": "meta-llama/llama-3.1-8b-instruct", + "name": "Meta: Llama 3.1 8B Instruct", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-07-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 16384, + "output": 16384 + }, + "cost": { + "input": 0.02, + "output": 0.05 + } + }, + "meta-llama/llama-3-70b-instruct": { + "id": "meta-llama/llama-3-70b-instruct", + "name": "Meta: Llama 3 70B Instruct", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 8000 + }, + "cost": { + "input": 0.51, + "output": 0.74 + } + }, + "x-ai/grok-4.20": { + "id": "x-ai/grok-4.20", + "name": "xAI: Grok 4.20", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-31", + "last_updated": "2026-04-11", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 2000000 + }, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.2 + } + }, + "x-ai/grok-code-fast-1:optimized:free": { + "id": "x-ai/grok-code-fast-1:optimized:free", + "name": "xAI: Grok Code Fast 1 Optimized (experimental, free)", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-08-27", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 10000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "x-ai/grok-4.3": { + "id": "x-ai/grok-4.3", + "name": "xAI: Grok 4.3", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-01", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 4096 + }, + "cost": { + "input": 1.25, + "output": 2.5, + "cache_read": 0.2 + } + }, + "x-ai/grok-4-fast": { + "id": "x-ai/grok-4-fast", + "name": "xAI: Grok 4 Fast", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-08-19", + "last_updated": "2025-08-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } + }, + "x-ai/grok-code-fast-1": { + "id": "x-ai/grok-code-fast-1", + "name": "xAI: Grok Code Fast 1", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-08-26", + "last_updated": "2025-08-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 10000 + }, + "cost": { + "input": 0.2, + "output": 1.5, + "cache_read": 0.02 + } + }, + "x-ai/grok-3-beta": { + "id": "x-ai/grok-3-beta", + "name": "xAI: Grok 3 Beta", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 26215 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75 + } + }, + "x-ai/grok-4": { + "id": "x-ai/grok-4", + "name": "xAI: Grok 4", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 51200 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75 + } + }, + "x-ai/grok-3-mini": { + "id": "x-ai/grok-3-mini", + "name": "xAI: Grok 3 Mini", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 26215 + }, + "cost": { + "input": 0.3, + "output": 0.5, + "cache_read": 0.075 + } + }, + "x-ai/grok-4.1-fast": { + "id": "x-ai/grok-4.1-fast", + "name": "xAI: Grok 4.1 Fast", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-11-19", + "last_updated": "2025-11-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } + }, + "x-ai/grok-3-mini-beta": { + "id": "x-ai/grok-3-mini-beta", + "name": "xAI: Grok 3 Mini Beta", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 26215 + }, + "cost": { + "input": 0.3, + "output": 0.5, + "cache_read": 0.075 + } + }, + "x-ai/grok-4.20-multi-agent": { + "id": "x-ai/grok-4.20-multi-agent", + "name": "xAI: Grok 4.20 Multi-Agent", + "attachment": true, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2026-03-31", + "last_updated": "2026-04-11", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 2000000 + }, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.2 + } + }, + "x-ai/grok-3": { + "id": "x-ai/grok-3", + "name": "xAI: Grok 3", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 26215 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75 + } + }, + "tencent/hy3-preview:free": { + "id": "tencent/hy3-preview:free", + "name": "Tencent: Hy3 Preview (free)", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-22", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "tencent/hunyuan-a13b-instruct": { + "id": "tencent/hunyuan-a13b-instruct", + "name": "Tencent: Hunyuan A13B Instruct", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2025-06-30", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.14, + "output": 0.57 + } + }, + "gryphe/mythomax-l2-13b": { + "id": "gryphe/mythomax-l2-13b", + "name": "MythoMax 13B", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-04-25", + "last_updated": "2024-04-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 4096, + "output": 4096 + }, + "cost": { + "input": 0.06, + "output": 0.06 + } + }, + "sao10k/l3-euryale-70b": { + "id": "sao10k/l3-euryale-70b", + "name": "Sao10k: Llama 3 Euryale 70B v2.1", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-06-18", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 1.48, + "output": 1.48 + } + }, + "sao10k/l3-lunaris-8b": { + "id": "sao10k/l3-lunaris-8b", + "name": "Sao10K: Llama 3 8B Lunaris", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-08-13", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 0.04, + "output": 0.05 + } + }, + "sao10k/l3.3-euryale-70b": { + "id": "sao10k/l3.3-euryale-70b", + "name": "Sao10K: Llama 3.3 Euryale 70B", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-12-18", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.65, + "output": 0.75 + } + }, + "sao10k/l3.1-70b-hanami-x1": { + "id": "sao10k/l3.1-70b-hanami-x1", + "name": "Sao10K: Llama 3.1 70B Hanami x1", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-01-08", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 16000, + "output": 16000 + }, + "cost": { + "input": 3, + "output": 3 + } + }, + "sao10k/l3.1-euryale-70b": { + "id": "sao10k/l3.1-euryale-70b", + "name": "Sao10K: Llama 3.1 Euryale 70B v2.2", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-08-28", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.85, + "output": 0.85 + } + }, + "microsoft/wizardlm-2-8x22b": { + "id": "microsoft/wizardlm-2-8x22b", + "name": "WizardLM-2 8x22B", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-04-24", + "last_updated": "2024-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 65535, + "output": 8000 + }, + "cost": { + "input": 0.62, + "output": 0.62 + } + }, + "microsoft/phi-4-mini-instruct": { + "id": "microsoft/phi-4-mini-instruct", + "name": "Microsoft: Phi 4 Mini Instruct", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": true, + "temperature": true, + "release_date": "2025-10-17", + "last_updated": "2026-05-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.08, + "output": 0.35, + "cache_read": 0.08 + } + }, + "microsoft/phi-4": { + "id": "microsoft/phi-4", + "name": "Microsoft: Phi 4", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 16384, + "output": 16384 + }, + "cost": { + "input": 0.06, + "output": 0.14 + } + }, + "poolside/laguna-m.1:free": { + "id": "poolside/laguna-m.1:free", + "name": "Poolside: Laguna M.1 (free)", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-28", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "poolside/laguna-xs.2:free": { + "id": "poolside/laguna-xs.2:free", + "name": "Poolside: Laguna XS.2 (free)", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-28", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "cohere/command-r7b-12-2024": { + "id": "cohere/command-r7b-12-2024", + "name": "Cohere: Command R7B (12-2024)", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-02-27", + "last_updated": "2024-02-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4000 + }, + "cost": { + "input": 0.0375, + "output": 0.15 + } + }, + "cohere/command-a": { + "id": "cohere/command-a", + "name": "Cohere: Command A", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-03-13", + "last_updated": "2025-03-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 8192 + }, + "cost": { + "input": 2.5, + "output": 10 + } + }, + "cohere/command-r-plus-08-2024": { + "id": "cohere/command-r-plus-08-2024", + "name": "Cohere: Command R+ (08-2024)", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-08-30", + "last_updated": "2024-08-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4000 + }, + "cost": { + "input": 2.5, + "output": 10 + } + }, + "cohere/command-r-08-2024": { + "id": "cohere/command-r-08-2024", + "name": "Cohere: Command R (08-2024)", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-08-30", + "last_updated": "2024-08-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4000 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } + }, + "prime-intellect/intellect-3": { + "id": "prime-intellect/intellect-3", + "name": "Prime Intellect: INTELLECT-3", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-11-26", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.2, + "output": 1.1 + } + }, + "nvidia/llama-3.3-nemotron-super-49b-v1.5": { + "id": "nvidia/llama-3.3-nemotron-super-49b-v1.5", + "name": "NVIDIA: Llama 3.3 Nemotron Super 49B V1.5", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-03-16", + "last_updated": "2025-03-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 26215 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } + }, + "nvidia/nemotron-3-super-120b-a12b": { + "id": "nvidia/nemotron-3-super-120b-a12b", + "name": "NVIDIA: Nemotron 3 Super", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-11", + "last_updated": "2026-04-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.1, + "output": 0.5, + "cache_read": 0.1 + } + }, + "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free": { + "id": "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free", + "name": "NVIDIA: Nemotron 3 Nano Omni (free)", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-28", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "audio", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "nvidia/nemotron-3-nano-30b-a3b": { + "id": "nvidia/nemotron-3-nano-30b-a3b", + "name": "NVIDIA: Nemotron 3 Nano 30B A3B", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2024-12", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 52429 + }, + "cost": { + "input": 0.05, + "output": 0.2 + } + }, + "nvidia/nemotron-3-super-120b-a12b:free": { + "id": "nvidia/nemotron-3-super-120b-a12b:free", + "name": "NVIDIA: Nemotron 3 Super (free)", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-12", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "nvidia/nemotron-nano-9b-v2": { + "id": "nvidia/nemotron-nano-9b-v2", + "name": "NVIDIA: Nemotron Nano 9B V2", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-08-18", + "last_updated": "2025-08-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 26215 + }, + "cost": { + "input": 0.04, + "output": 0.16 + } + }, + "nvidia/llama-3.1-nemotron-70b-instruct": { + "id": "nvidia/llama-3.1-nemotron-70b-instruct", + "name": "NVIDIA: Llama 3.1 Nemotron 70B Instruct", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-10-12", + "last_updated": "2024-10-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 1.2, + "output": 1.2 + } + }, + "inception/mercury-2": { + "id": "inception/mercury-2", + "name": "Inception: Mercury 2", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-24", + "last_updated": "2026-02-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 50000 + }, + "cost": { + "input": 0.25, + "output": 0.75, + "cache_read": 0.025 + } + }, + "openai/gpt-5.1-codex-max": { + "id": "openai/gpt-5.1-codex-max", + "name": "OpenAI: GPT-5.1-Codex-Max", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } + }, + "openai/gpt-5.2-chat": { + "id": "openai/gpt-5.2-chat", + "name": "OpenAI: GPT-5.2 Chat", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2025-12-11", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } + }, + "openai/gpt-4o-mini-search-preview": { + "id": "openai/gpt-4o-mini-search-preview", + "name": "OpenAI: GPT-4o-mini Search Preview", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2025-01", + "last_updated": "2025-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } + }, + "openai/gpt-5-chat": { + "id": "openai/gpt-5-chat", + "name": "OpenAI: GPT-5 Chat", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2025-08-07", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } + }, + "openai/gpt-4o-2024-05-13": { + "id": "openai/gpt-4o-2024-05-13", + "name": "OpenAI: GPT-4o (2024-05-13)", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-05-13", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 5, + "output": 15 + } + }, + "openai/gpt-5.3-chat": { + "id": "openai/gpt-5.3-chat", + "name": "OpenAI: GPT-5.3 Chat", + "attachment": true, + "reasoning": false, + "tool_call": true, + "release_date": "2026-03-04", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.75, + "output": 14 + } + }, + "openai/gpt-5.2-pro": { + "id": "openai/gpt-5.2-pro", + "name": "OpenAI: GPT-5.2 Pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-12-11", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 21, + "output": 168 + } + }, + "openai/gpt-4-1106-preview": { + "id": "openai/gpt-4-1106-preview", + "name": "OpenAI: GPT-4 Turbo (older v1106)", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2023-11-06", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 10, + "output": 30 + } + }, + "openai/gpt-chat-latest": { + "id": "openai/gpt-chat-latest", + "name": "OpenAI: GPT Chat Latest", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "release_date": "2026-05-05", + "last_updated": "2026-05-07", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5 + } + }, + "openai/gpt-4o-audio-preview": { + "id": "openai/gpt-4o-audio-preview", + "name": "OpenAI: GPT-4o Audio", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-08-15", + "last_updated": "2026-03-15", + "modalities": { + "input": ["audio", "text"], + "output": ["audio", "text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10 + } + }, + "openai/gpt-5.5": { + "id": "openai/gpt-5.5", + "name": "OpenAI: GPT-5.5", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-04-24", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5 + } + }, + "openai/gpt-5-mini": { + "id": "openai/gpt-5-mini", + "name": "OpenAI: GPT-5 Mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-08-07", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.025 + } + }, + "openai/gpt-5-nano": { + "id": "openai/gpt-5-nano", + "name": "OpenAI: GPT-5 Nano", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-08-07", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.05, + "output": 0.4, + "cache_read": 0.005 + } + }, + "openai/gpt-5.3-codex": { + "id": "openai/gpt-5.3-codex", + "name": "OpenAI: GPT-5.3-Codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "release_date": "2026-02-25", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14 + } + }, + "openai/gpt-3.5-turbo-16k": { + "id": "openai/gpt-3.5-turbo-16k", + "name": "OpenAI: GPT-3.5 Turbo 16k", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2023-08-28", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16385, + "output": 4096 + }, + "cost": { + "input": 3, + "output": 4 + } + }, + "openai/gpt-4-turbo": { + "id": "openai/gpt-4-turbo", + "name": "OpenAI: GPT-4 Turbo", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2023-09-13", + "last_updated": "2024-04-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 10, + "output": 30 + } + }, + "openai/gpt-5.2": { + "id": "openai/gpt-5.2", + "name": "OpenAI: GPT-5.2", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-12-11", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } + }, + "openai/o3-pro": { + "id": "openai/o3-pro", + "name": "OpenAI: o3 Pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-04-16", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 20, + "output": 80 + } + }, + "openai/o3-mini-high": { + "id": "openai/o3-mini-high", + "name": "OpenAI: o3 Mini High", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2025-01-31", + "last_updated": "2026-03-15", + "modalities": { + "input": ["pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.55 + } + }, + "openai/gpt-4o-mini": { + "id": "openai/gpt-4o-mini", + "name": "OpenAI: GPT-4o-mini", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-07-18", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.075 + } + }, + "openai/o4-mini-deep-research": { + "id": "openai/o4-mini-deep-research", + "name": "OpenAI: o4 Mini Deep Research", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2024-06-26", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } + }, + "openai/gpt-5.4-mini": { + "id": "openai/gpt-5.4-mini", + "name": "OpenAI: GPT-5.4 Mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-03-17", + "last_updated": "2026-04-11", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.75, + "output": 4.5, + "cache_read": 0.075 + } + }, + "openai/gpt-5.1-chat": { + "id": "openai/gpt-5.1-chat", + "name": "OpenAI: GPT-5.1 Chat", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2025-11-13", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } + }, + "openai/o4-mini": { + "id": "openai/o4-mini", + "name": "OpenAI: o4 Mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-04-16", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.275 + } + }, + "openai/gpt-5.4-nano": { + "id": "openai/gpt-5.4-nano", + "name": "OpenAI: GPT-5.4 Nano", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-03-17", + "last_updated": "2026-04-11", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.2, + "output": 1.25, + "cache_read": 0.02 + } + }, + "openai/gpt-5.2-codex": { + "id": "openai/gpt-5.2-codex", + "name": "OpenAI: GPT-5.2-Codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-01-14", + "last_updated": "2026-01-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } + }, + "openai/gpt-4o-mini-2024-07-18": { + "id": "openai/gpt-4o-mini-2024-07-18", + "name": "OpenAI: GPT-4o-mini (2024-07-18)", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-07-18", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } + }, + "openai/gpt-5.1-codex-mini": { + "id": "openai/gpt-5.1-codex-mini", + "name": "OpenAI: GPT-5.1-Codex-Mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 100000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.025 + } + }, + "openai/gpt-4o-2024-08-06": { + "id": "openai/gpt-4o-2024-08-06", + "name": "OpenAI: GPT-4o (2024-08-06)", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-08-06", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10, + "cache_read": 1.25 + } + }, + "openai/gpt-5-image": { + "id": "openai/gpt-5-image", + "name": "OpenAI: GPT-5 Image", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-10-14", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["image", "text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 10, + "output": 10 + } + }, + "openai/gpt-5.1": { + "id": "openai/gpt-5.1", + "name": "OpenAI: GPT-5.1", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-11-13", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } + }, + "openai/o1": { + "id": "openai/o1", + "name": "OpenAI: o1", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2024-12-05", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 15, + "output": 60, + "cache_read": 7.5 + } + }, + "openai/gpt-5.4-pro": { + "id": "openai/gpt-5.4-pro", + "name": "OpenAI: GPT-5.4 Pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "release_date": "2026-03-06", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "output": 128000 + }, + "cost": { + "input": 30, + "output": 180 + } + }, + "openai/gpt-3.5-turbo": { + "id": "openai/gpt-3.5-turbo", + "name": "OpenAI: GPT-3.5 Turbo", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2023-03-01", + "last_updated": "2023-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16385, + "output": 4096 + }, + "cost": { + "input": 0.5, + "output": 1.5 + } + }, + "openai/o3-deep-research": { + "id": "openai/o3-deep-research", + "name": "OpenAI: o3 Deep Research", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2024-06-26", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 10, + "output": 40, + "cache_read": 2.5 + } + }, + "openai/o3-mini": { + "id": "openai/o3-mini", + "name": "OpenAI: o3 Mini", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2024-12-20", + "last_updated": "2026-03-15", + "modalities": { + "input": ["pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.55 + } + }, + "openai/gpt-4-turbo-preview": { + "id": "openai/gpt-4-turbo-preview", + "name": "OpenAI: GPT-4 Turbo Preview", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-01-25", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 10, + "output": 30 + } + }, + "openai/o1-pro": { + "id": "openai/o1-pro", + "name": "OpenAI: o1-pro", + "attachment": true, + "reasoning": true, + "tool_call": false, + "temperature": false, + "release_date": "2025-03-19", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 150, + "output": 600 + } + }, + "openai/gpt-5.4-image-2": { + "id": "openai/gpt-5.4-image-2", + "name": "OpenAI: GPT-5.4 Image 2", + "attachment": true, + "reasoning": true, + "tool_call": false, + "temperature": false, + "release_date": "2026-04-21", + "last_updated": "2026-05-01", + "modalities": { + "input": ["image", "text", "pdf"], + "output": ["image", "text"] + }, + "open_weights": false, + "limit": { + "context": 272000, + "output": 128000 + }, + "cost": { + "input": 8, + "output": 15, + "cache_read": 2 + } + }, + "openai/gpt-4": { + "id": "openai/gpt-4", + "name": "OpenAI: GPT-4", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2023-03-14", + "last_updated": "2024-04-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8191, + "output": 4096 + }, + "cost": { + "input": 30, + "output": 60 + } + }, + "openai/gpt-4-0314": { + "id": "openai/gpt-4-0314", + "name": "OpenAI: GPT-4 (older v0314)", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2023-05-28", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8191, + "output": 4096 + }, + "cost": { + "input": 30, + "output": 60 + } + }, + "openai/gpt-5-codex": { + "id": "openai/gpt-5-codex", + "name": "OpenAI: GPT-5 Codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-09-15", + "last_updated": "2025-09-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } + }, + "openai/gpt-5.4": { + "id": "openai/gpt-5.4", + "name": "OpenAI: GPT-5.4", + "attachment": true, + "reasoning": true, + "tool_call": true, + "release_date": "2026-03-06", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "output": 128000 + }, + "cost": { + "input": 2.5, + "output": 15 + } + }, + "openai/gpt-audio": { + "id": "openai/gpt-audio", + "name": "OpenAI: GPT Audio", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2026-01-20", + "last_updated": "2026-03-15", + "modalities": { + "input": ["audio", "text"], + "output": ["audio", "text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10 + } + }, + "openai/gpt-4o-search-preview": { + "id": "openai/gpt-4o-search-preview", + "name": "OpenAI: GPT-4o Search Preview", + "attachment": false, + "reasoning": false, + "tool_call": false, + "release_date": "2025-03-13", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10 + } + }, + "openai/gpt-4.1-nano": { + "id": "openai/gpt-4.1-nano", + "name": "OpenAI: GPT-4.1 Nano", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-04-14", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.025 + } + }, + "openai/o4-mini-high": { + "id": "openai/o4-mini-high", + "name": "OpenAI: o4 Mini High", + "attachment": true, + "reasoning": true, + "tool_call": true, + "release_date": "2025-04-17", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4 + } + }, + "openai/o3": { + "id": "openai/o3", + "name": "OpenAI: o3", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-04-16", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } + }, + "openai/gpt-oss-20b": { + "id": "openai/gpt-oss-20b", + "name": "OpenAI: gpt-oss-20b", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 26215 + }, + "cost": { + "input": 0.03, + "output": 0.14 + } + }, + "openai/gpt-5-pro": { + "id": "openai/gpt-5-pro", + "name": "OpenAI: GPT-5 Pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-10-06", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 15, + "output": 120 + } + }, + "openai/gpt-audio-mini": { + "id": "openai/gpt-audio-mini", + "name": "OpenAI: GPT Audio Mini", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2026-01-20", + "last_updated": "2026-03-15", + "modalities": { + "input": ["audio", "text"], + "output": ["audio", "text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.6, + "output": 2.4 + } + }, + "openai/gpt-4o": { + "id": "openai/gpt-4o", + "name": "OpenAI: GPT-4o", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-05-13", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10, + "cache_read": 1.25 + } + }, + "openai/gpt-3.5-turbo-0613": { + "id": "openai/gpt-3.5-turbo-0613", + "name": "OpenAI: GPT-3.5 Turbo (older v0613)", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2023-06-13", + "last_updated": "2023-06-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 4095, + "output": 4096 + }, + "cost": { + "input": 1, + "output": 2 + } + }, + "openai/gpt-5-image-mini": { + "id": "openai/gpt-5-image-mini", + "name": "OpenAI: GPT-5 Image Mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-10-16", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["image", "text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 2.5, + "output": 2 + } + }, + "openai/gpt-5": { + "id": "openai/gpt-5", + "name": "OpenAI: GPT-5", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-08-07", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } + }, + "openai/gpt-oss-safeguard-20b": { + "id": "openai/gpt-oss-safeguard-20b", + "name": "OpenAI: gpt-oss-safeguard-20b", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-10-29", + "last_updated": "2025-10-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.075, + "output": 0.3, + "cache_read": 0.037 + } + }, + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "name": "OpenAI: gpt-oss-120b", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 26215 + }, + "cost": { + "input": 0.039, + "output": 0.19 + } + }, + "openai/gpt-5.5-pro": { + "id": "openai/gpt-5.5-pro", + "name": "OpenAI: GPT-5.5 Pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-04-24", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "output": 128000 + }, + "cost": { + "input": 30, + "output": 180 + } + }, + "openai/gpt-3.5-turbo-instruct": { + "id": "openai/gpt-3.5-turbo-instruct", + "name": "OpenAI: GPT-3.5 Turbo Instruct", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2023-03-01", + "last_updated": "2023-09-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 4095, + "output": 4096 + }, + "cost": { + "input": 1.5, + "output": 2 + } + }, + "openai/gpt-4.1": { + "id": "openai/gpt-4.1", + "name": "OpenAI: GPT-4.1", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-04-14", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } + }, + "openai/gpt-4.1-mini": { + "id": "openai/gpt-4.1-mini", + "name": "OpenAI: GPT-4.1 Mini", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-04-14", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 1.6, + "cache_read": 0.1 + } + }, + "openai/gpt-5.1-codex": { + "id": "openai/gpt-5.1-codex", + "name": "OpenAI: GPT-5.1-Codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } + }, + "openai/gpt-4o-2024-11-20": { + "id": "openai/gpt-4o-2024-11-20", + "name": "OpenAI: GPT-4o (2024-11-20)", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-11-20", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10, + "cache_read": 1.25 + } + }, + "amazon/nova-lite-v1": { + "id": "amazon/nova-lite-v1", + "name": "Amazon: Nova Lite 1.0", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-12-06", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 300000, + "output": 5120 + }, + "cost": { + "input": 0.06, + "output": 0.24 + } + }, + "amazon/nova-pro-v1": { + "id": "amazon/nova-pro-v1", + "name": "Amazon: Nova Pro 1.0", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-12-03", + "last_updated": "2024-12-03", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 300000, + "output": 5120 + }, + "cost": { + "input": 0.8, + "output": 3.2 + } + }, + "amazon/nova-premier-v1": { + "id": "amazon/nova-premier-v1", + "name": "Amazon: Nova Premier 1.0", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-11-01", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 32000 + }, + "cost": { + "input": 2.5, + "output": 12.5 + } + }, + "amazon/nova-2-lite-v1": { + "id": "amazon/nova-2-lite-v1", + "name": "Amazon: Nova 2 Lite", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2024-12-01", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 65535 + }, + "cost": { + "input": 0.3, + "output": 2.5 + } + }, + "amazon/nova-micro-v1": { + "id": "amazon/nova-micro-v1", + "name": "Amazon: Nova Micro 1.0", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-12-06", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 5120 + }, + "cost": { + "input": 0.035, + "output": 0.14 + } + }, + "z-ai/glm-5v-turbo": { + "id": "z-ai/glm-5v-turbo", + "name": "Z.ai: GLM 5V Turbo", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-01", + "last_updated": "2026-04-11", + "modalities": { + "input": ["image", "text", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 131072 + }, + "cost": { + "input": 1.2, + "output": 4, + "cache_read": 0.24 + } + }, + "z-ai/glm-4.7": { + "id": "z-ai/glm-4.7", + "name": "Z.ai: GLM 4.7", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-12-22", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 65535 + }, + "cost": { + "input": 0.38, + "output": 1.98, + "cache_read": 0.2 + } + }, + "z-ai/glm-5": { + "id": "z-ai/glm-5", + "name": "Z.ai: GLM 5", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-12", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 131072 + }, + "cost": { + "input": 0.72, + "output": 2.3 + } + }, + "z-ai/glm-4-32b": { + "id": "z-ai/glm-4-32b", + "name": "Z.ai: GLM 4 32B ", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-07-25", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } + }, + "z-ai/glm-5.1": { + "id": "z-ai/glm-5.1", + "name": "Z.ai: GLM 5.1", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-27", + "last_updated": "2026-03-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 131072 + }, + "cost": { + "input": 1.26, + "output": 3.96 + } + }, + "z-ai/glm-4.5": { + "id": "z-ai/glm-4.5", + "name": "Z.ai: GLM 4.5", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-07-28", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 98304 + }, + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.175 + } + }, + "z-ai/glm-4.5-air": { + "id": "z-ai/glm-4.5-air", + "name": "Z.ai: GLM 4.5 Air", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 98304 + }, + "cost": { + "input": 0.13, + "output": 0.85, + "cache_read": 0.025 + } + }, + "z-ai/glm-5-turbo": { + "id": "z-ai/glm-5-turbo", + "name": "Z.ai: GLM 5 Turbo", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-15", + "last_updated": "2026-04-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 131072 + }, + "cost": { + "input": 1.2, + "output": 4, + "cache_read": 0.24 + } + }, + "z-ai/glm-4.5v": { + "id": "z-ai/glm-4.5v", + "name": "Z.ai: GLM 4.5V", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-08-11", + "last_updated": "2025-08-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 65536, + "output": 16384 + }, + "cost": { + "input": 0.6, + "output": 1.8, + "cache_read": 0.11 + } + }, + "z-ai/glm-4.6": { + "id": "z-ai/glm-4.6", + "name": "Z.ai: GLM 4.6", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-09-30", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 204800 + }, + "cost": { + "input": 0.39, + "output": 1.9, + "cache_read": 0.175 + } + }, + "z-ai/glm-4.6v": { + "id": "z-ai/glm-4.6v", + "name": "Z.ai: GLM 4.6V", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-09-30", + "last_updated": "2026-01-10", + "modalities": { + "input": ["image", "text", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 0.9 + } + }, + "z-ai/glm-4.7-flash": { + "id": "z-ai/glm-4.7-flash", + "name": "Z.ai: GLM 4.7 Flash", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-01-19", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 40551 + }, + "cost": { + "input": 0.06, + "output": 0.4, + "cache_read": 0.01 + } + }, + "baidu/ernie-4.5-vl-424b-a47b": { + "id": "baidu/ernie-4.5-vl-424b-a47b", + "name": "Baidu: ERNIE 4.5 VL 424B A47B ", + "attachment": true, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2025-06-30", + "last_updated": "2026-01", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 123000, + "output": 16000 + }, + "cost": { + "input": 0.42, + "output": 1.25 + } + }, + "baidu/qianfan-ocr-fast:free": { + "id": "baidu/qianfan-ocr-fast:free", + "name": "Baidu: Qianfan-OCR-Fast (free)", + "attachment": true, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2026-04-20", + "last_updated": "2026-05-01", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 65536, + "output": 28672 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "baidu/cobuddy:free": { + "id": "baidu/cobuddy:free", + "name": "Baidu: CoBuddy (free)", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-05-06", + "last_updated": "2026-05-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "baidu/ernie-4.5-vl-28b-a3b": { + "id": "baidu/ernie-4.5-vl-28b-a3b", + "name": "Baidu: ERNIE 4.5 VL 28B A3B", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-06-30", + "last_updated": "2025-06-30", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 30000, + "output": 8000 + }, + "cost": { + "input": 0.14, + "output": 0.56 + } + }, + "baidu/ernie-4.5-21b-a3b": { + "id": "baidu/ernie-4.5-21b-a3b", + "name": "Baidu: ERNIE 4.5 21B A3B", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-06-30", + "last_updated": "2025-06-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 120000, + "output": 8000 + }, + "cost": { + "input": 0.07, + "output": 0.28 + } + }, + "baidu/ernie-4.5-300b-a47b": { + "id": "baidu/ernie-4.5-300b-a47b", + "name": "Baidu: ERNIE 4.5 300B A47B ", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-06-30", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 123000, + "output": 12000 + }, + "cost": { + "input": 0.28, + "output": 1.1 + } + }, + "baidu/ernie-4.5-21b-a3b-thinking": { + "id": "baidu/ernie-4.5-21b-a3b-thinking", + "name": "Baidu: ERNIE 4.5 21B A3B Thinking", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2025-09-19", + "last_updated": "2025-09-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.07, + "output": 0.28 + } + }, + "relace/relace-apply-3": { + "id": "relace/relace-apply-3", + "name": "Relace: Relace Apply 3", + "attachment": false, + "reasoning": false, + "tool_call": false, + "release_date": "2025-09-26", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 128000 + }, + "cost": { + "input": 0.85, + "output": 1.25 + } + }, + "relace/relace-search": { + "id": "relace/relace-search", + "name": "Relace: Relace Search", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-12-09", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 128000 + }, + "cost": { + "input": 1, + "output": 3 + } + }, + "minimax/minimax-m2.7": { + "id": "minimax/minimax-m2.7", + "name": "MiniMax: MiniMax M2.7", + "family": "minimax-m2.7", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06 + } + }, + "minimax/minimax-m2": { + "id": "minimax/minimax-m2", + "name": "MiniMax: MiniMax M2", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-10-23", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 196608, + "output": 196608 + }, + "cost": { + "input": 0.255, + "output": 1, + "cache_read": 0.03 + } + }, + "minimax/minimax-01": { + "id": "minimax/minimax-01", + "name": "MiniMax: MiniMax-01", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-01-15", + "last_updated": "2025-01-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000192, + "output": 1000192 + }, + "cost": { + "input": 0.2, + "output": 1.1 + } + }, + "minimax/minimax-m2.1": { + "id": "minimax/minimax-m2.1", + "name": "MiniMax: MiniMax M2.1", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 196608, + "output": 39322 + }, + "cost": { + "input": 0.27, + "output": 0.95, + "cache_read": 0.03 + } + }, + "minimax/minimax-m1": { + "id": "minimax/minimax-m1", + "name": "MiniMax: MiniMax M1", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 40000 + }, + "cost": { + "input": 0.4, + "output": 2.2 + } + }, + "minimax/minimax-m2-her": { + "id": "minimax/minimax-m2-her", + "name": "MiniMax: MiniMax M2-her", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2026-01-23", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 65536, + "output": 2048 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } + }, + "minimax/minimax-m2.5": { + "id": "minimax/minimax-m2.5", + "name": "MiniMax: MiniMax M2.5", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-12", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 196608, + "output": 196608 + }, + "cost": { + "input": 0.25, + "output": 1.2, + "cache_read": 0.029 + } + }, + "~openai/gpt-latest": { + "id": "~openai/gpt-latest", + "name": "OpenAI: GPT Latest", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5 + } + }, + "~openai/gpt-mini-latest": { + "id": "~openai/gpt-mini-latest", + "name": "OpenAI: GPT Mini Latest", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.75, + "output": 4.5, + "cache_read": 0.075 + } + }, + "qwen/qwen3-235b-a22b": { + "id": "qwen/qwen3-235b-a22b", + "name": "Qwen: Qwen3 235B A22B", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2024-12-01", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.455, + "output": 1.82, + "cache_read": 0.15 + } + }, + "qwen/qwen3.5-122b-a10b": { + "id": "qwen/qwen3.5-122b-a10b", + "name": "Qwen: Qwen3.5-122B-A10B", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-26", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.26, + "output": 2.08 + } + }, + "qwen/qwen3-coder-plus": { + "id": "qwen/qwen3-coder-plus", + "name": "Qwen: Qwen3 Coder Plus", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-07-01", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.65, + "output": 3.25, + "cache_read": 0.2 + } + }, + "qwen/qwen3.6-27b": { + "id": "qwen/qwen3.6-27b", + "name": "Qwen: Qwen3.6 27B", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 65536 + }, + "cost": { + "input": 0.325, + "output": 3.25 + } + }, + "qwen/qwen3.5-27b": { + "id": "qwen/qwen3.5-27b", + "name": "Qwen: Qwen3.5-27B", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-26", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.195, + "output": 1.56 + } + }, + "qwen/qwen3-235b-a22b-2507": { + "id": "qwen/qwen3-235b-a22b-2507", + "name": "Qwen: Qwen3 235B A22B Instruct 2507", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-04", + "last_updated": "2026-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 52429 + }, + "cost": { + "input": 0.071, + "output": 0.1 + } + }, + "qwen/qwen3-8b": { + "id": "qwen/qwen3-8b", + "name": "Qwen: Qwen3 8B", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-04", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 40960, + "output": 8192 + }, + "cost": { + "input": 0.05, + "output": 0.4, + "cache_read": 0.05 + } + }, + "qwen/qwen3.5-397b-a17b": { + "id": "qwen/qwen3.5-397b-a17b", + "name": "Qwen: Qwen3.5 397B A17B", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-15", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.39, + "output": 2.34 + } + }, + "qwen/qwen-vl-plus": { + "id": "qwen/qwen-vl-plus", + "name": "Qwen: Qwen VL Plus", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-01-25", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.1365, + "output": 0.4095, + "cache_read": 0.042 + } + }, + "qwen/qwen3-32b": { + "id": "qwen/qwen3-32b", + "name": "Qwen: Qwen3 32B", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2024-12-01", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 40960, + "output": 40960 + }, + "cost": { + "input": 0.08, + "output": 0.24, + "cache_read": 0.04 + } + }, + "qwen/qwen2.5-vl-72b-instruct": { + "id": "qwen/qwen2.5-vl-72b-instruct", + "name": "Qwen: Qwen2.5 VL 72B Instruct", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-02-01", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.8, + "output": 0.8, + "cache_read": 0.075 + } + }, + "qwen/qwen-max": { + "id": "qwen/qwen-max", + "name": "Qwen: Qwen-Max ", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-04-03", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "output": 8192 + }, + "cost": { + "input": 1.04, + "output": 4.16, + "cache_read": 0.32 + } + }, + "qwen/qwen-plus": { + "id": "qwen/qwen-plus", + "name": "Qwen: Qwen-Plus", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-01-25", + "last_updated": "2025-09-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 1.2, + "cache_read": 0.08 + } + }, + "qwen/qwen3.6-35b-a3b": { + "id": "qwen/qwen3.6-35b-a3b", + "name": "Qwen: Qwen3.6 35B A3B", + "attachment": true, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.1612, + "output": 0.96525, + "cache_read": 0.1612 + } + }, + "qwen/qwen3-vl-235b-a22b-thinking": { + "id": "qwen/qwen3-vl-235b-a22b-thinking", + "name": "Qwen: Qwen3 VL 235B A22B Thinking", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-09-24", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.26, + "output": 2.6 + } + }, + "qwen/qwen3-vl-30b-a3b-thinking": { + "id": "qwen/qwen3-vl-30b-a3b-thinking", + "name": "Qwen: Qwen3 VL 30B A3B Thinking", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-10-11", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.13, + "output": 1.56 + } + }, + "qwen/qwen3-vl-8b-instruct": { + "id": "qwen/qwen3-vl-8b-instruct", + "name": "Qwen: Qwen3 VL 8B Instruct", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-10-15", + "last_updated": "2025-11-25", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.08, + "output": 0.5 + } + }, + "qwen/qwen3.5-flash-02-23": { + "id": "qwen/qwen3.5-flash-02-23", + "name": "Qwen: Qwen3.5-Flash", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-26", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } + }, + "qwen/qwen3.6-plus": { + "id": "qwen/qwen3.6-plus", + "name": "Qwen: Qwen3.6 Plus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-08-26", + "last_updated": "2026-04-11", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.325, + "output": 1.95, + "cache_read": 0.0325, + "cache_write": 0.40625 + } + }, + "qwen/qwen3-max": { + "id": "qwen/qwen3-max", + "name": "Qwen: Qwen3 Max", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-09-05", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 1.2, + "output": 6, + "cache_read": 0.24 + } + }, + "qwen/qwen-plus-2025-07-28": { + "id": "qwen/qwen-plus-2025-07-28", + "name": "Qwen: Qwen Plus 0728", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-09-09", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 32768 + }, + "cost": { + "input": 0.26, + "output": 0.78 + } + }, + "qwen/qwen3-30b-a3b-instruct-2507": { + "id": "qwen/qwen3-30b-a3b-instruct-2507", + "name": "Qwen: Qwen3 30B A3B Instruct 2507", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-07-29", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.09, + "output": 0.3, + "cache_read": 0.04 + } + }, + "qwen/qwen3-vl-32b-instruct": { + "id": "qwen/qwen3-vl-32b-instruct", + "name": "Qwen: Qwen3 VL 32B Instruct", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-10-21", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.104, + "output": 0.416 + } + }, + "qwen/qwen3-235b-a22b-thinking-2507": { + "id": "qwen/qwen3-235b-a22b-thinking-2507", + "name": "Qwen: Qwen3 235B A22B Thinking 2507", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-07-25", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.11, + "output": 0.6 + } + }, + "qwen/qwen3-next-80b-a3b-thinking": { + "id": "qwen/qwen3-next-80b-a3b-thinking", + "name": "Qwen: Qwen3 Next 80B A3B Thinking", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-09-11", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.0975, + "output": 0.78 + } + }, + "qwen/qwen3-30b-a3b-thinking-2507": { + "id": "qwen/qwen3-30b-a3b-thinking-2507", + "name": "Qwen: Qwen3 30B A3B Thinking 2507", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-07-29", + "last_updated": "2025-07-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 6554 + }, + "cost": { + "input": 0.051, + "output": 0.34 + } + }, + "qwen/qwen-2.5-7b-instruct": { + "id": "qwen/qwen-2.5-7b-instruct", + "name": "Qwen: Qwen2.5 7B Instruct", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-09", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 6554 + }, + "cost": { + "input": 0.04, + "output": 0.1 + } + }, + "qwen/qwen-vl-max": { + "id": "qwen/qwen-vl-max", + "name": "Qwen: Qwen VL Max", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-04-08", + "last_updated": "2025-08-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.8, + "output": 3.2 + } + }, + "qwen/qwen3-coder-flash": { + "id": "qwen/qwen3-coder-flash", + "name": "Qwen: Qwen3 Coder Flash", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-07-23", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.195, + "output": 0.975, + "cache_read": 0.06 + } + }, + "qwen/qwen3-30b-a3b": { + "id": "qwen/qwen3-30b-a3b", + "name": "Qwen: Qwen3 30B A3B", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-04", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 40960, + "output": 40960 + }, + "cost": { + "input": 0.08, + "output": 0.28, + "cache_read": 0.03 + } + }, + "qwen/qwen3-next-80b-a3b-instruct": { + "id": "qwen/qwen3-next-80b-a3b-instruct", + "name": "Qwen: Qwen3 Next 80B A3B Instruct", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-09-11", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 52429 + }, + "cost": { + "input": 0.09, + "output": 1.1 + } + }, + "qwen/qwen3.5-plus-20260420": { + "id": "qwen/qwen3.5-plus-20260420", + "name": "Qwen: Qwen3.5 Plus 2026-04-20", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.4, + "output": 2.4 + } + }, + "qwen/qwen3-coder-next": { + "id": "qwen/qwen3-coder-next", + "name": "Qwen: Qwen3 Coder Next", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-02", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.12, + "output": 0.75, + "cache_read": 0.035 + } + }, + "qwen/qwen-2.5-coder-32b-instruct": { + "id": "qwen/qwen-2.5-coder-32b-instruct", + "name": "Qwen2.5 Coder 32B Instruct", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-11-11", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 8192 + }, + "cost": { + "input": 0.2, + "output": 0.2, + "cache_read": 0.015 + } + }, + "qwen/qwen3-vl-30b-a3b-instruct": { + "id": "qwen/qwen3-vl-30b-a3b-instruct", + "name": "Qwen: Qwen3 VL 30B A3B Instruct", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-10-05", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.13, + "output": 0.52 + } + }, + "qwen/qwen3-coder-30b-a3b-instruct": { + "id": "qwen/qwen3-coder-30b-a3b-instruct", + "name": "Qwen: Qwen3 Coder 30B A3B Instruct", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-07-31", + "last_updated": "2025-07-31", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 160000, + "output": 32768 + }, + "cost": { + "input": 0.07, + "output": 0.27 + } + }, + "qwen/qwen3-max-thinking": { + "id": "qwen/qwen3-max-thinking", + "name": "Qwen: Qwen3 Max Thinking", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-01-23", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0.78, + "output": 3.9 + } + }, + "qwen/qwen-turbo": { + "id": "qwen/qwen-turbo", + "name": "Qwen: Qwen-Turbo", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-11-01", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.0325, + "output": 0.13, + "cache_read": 0.01 + } + }, + "qwen/qwen3-vl-235b-a22b-instruct": { + "id": "qwen/qwen3-vl-235b-a22b-instruct", + "name": "Qwen: Qwen3 VL 235B A22B Instruct", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-09-23", + "last_updated": "2026-01-10", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 52429 + }, + "cost": { + "input": 0.2, + "output": 0.88, + "cache_read": 0.11 + } + }, + "qwen/qwen3-coder": { + "id": "qwen/qwen3-coder", + "name": "Qwen: Qwen3 Coder 480B A35B", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 52429 + }, + "cost": { + "input": 0.22, + "output": 1, + "cache_read": 0.022 + } + }, + "qwen/qwen3.5-9b": { + "id": "qwen/qwen3.5-9b", + "name": "Qwen: Qwen3.5-9B", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-10", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 32768 + }, + "cost": { + "input": 0.05, + "output": 0.15 + } + }, + "qwen/qwen3-vl-8b-thinking": { + "id": "qwen/qwen3-vl-8b-thinking", + "name": "Qwen: Qwen3 VL 8B Thinking", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-10-15", + "last_updated": "2025-11-25", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.117, + "output": 1.365 + } + }, + "qwen/qwen3.6-max-preview": { + "id": "qwen/qwen3.6-max-preview", + "name": "Qwen: Qwen3.6 Max Preview", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 1.04, + "output": 6.24, + "cache_write": 1.3 + } + }, + "qwen/qwen-plus-2025-07-28:thinking": { + "id": "qwen/qwen-plus-2025-07-28:thinking", + "name": "Qwen: Qwen Plus 0728 (thinking)", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-09-09", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 32768 + }, + "cost": { + "input": 0.26, + "output": 0.78 + } + }, + "qwen/qwen-2.5-72b-instruct": { + "id": "qwen/qwen-2.5-72b-instruct", + "name": "Qwen2.5 72B Instruct", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-09", + "last_updated": "2026-01-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 16384 + }, + "cost": { + "input": 0.12, + "output": 0.39 + } + }, + "qwen/qwen3-14b": { + "id": "qwen/qwen3-14b", + "name": "Qwen: Qwen3 14B", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-04", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 40960, + "output": 40960 + }, + "cost": { + "input": 0.06, + "output": 0.24, + "cache_read": 0.025 + } + }, + "qwen/qwen3.5-35b-a3b": { + "id": "qwen/qwen3.5-35b-a3b", + "name": "Qwen: Qwen3.5-35B-A3B", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-26", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.1625, + "output": 1.3 + } + }, + "qwen/qwen3.5-plus-02-15": { + "id": "qwen/qwen3.5-plus-02-15", + "name": "Qwen: Qwen3.5 Plus 2026-02-15", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-15", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.26, + "output": 1.56 + } + }, + "qwen/qwen3.6-flash": { + "id": "qwen/qwen3.6-flash", + "name": "Qwen: Qwen3.6 Flash", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.25, + "output": 1.5, + "cache_write": 0.3125 + } + }, + "alfredpros/codellama-7b-instruct-solidity": { + "id": "alfredpros/codellama-7b-instruct-solidity", + "name": "AlfredPros: CodeLLaMa 7B Instruct Solidity", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-14", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 4096, + "output": 4096 + }, + "cost": { + "input": 0.8, + "output": 1.2 + } + }, + "kwaipilot/kat-coder-pro-v2": { + "id": "kwaipilot/kat-coder-pro-v2", + "name": "Kwaipilot: KAT-Coder-Pro V2", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-27", + "last_updated": "2026-04-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 80000 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06 + } + }, + "google/gemini-2.5-pro-preview-05-06": { + "id": "google/gemini-2.5-pro-preview-05-06", + "name": "Google: Gemini 2.5 Pro Preview 05-06", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-05-06", + "last_updated": "2026-03-15", + "modalities": { + "input": ["audio", "image", "pdf", "text", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65535 + }, + "cost": { + "input": 1.25, + "output": 10, + "reasoning": 10, + "cache_read": 0.125, + "cache_write": 0.375 + } + }, + "google/lyria-3-clip-preview": { + "id": "google/lyria-3-clip-preview", + "name": "Google: Lyria 3 Clip Preview", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2026-03-30", + "last_updated": "2026-04-11", + "modalities": { + "input": ["image", "text"], + "output": ["audio", "text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "google/gemini-3.1-pro-preview-customtools": { + "id": "google/gemini-3.1-pro-preview-customtools", + "name": "Google: Gemini 3.1 Pro Preview Custom Tools", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-26", + "last_updated": "2026-03-15", + "modalities": { + "input": ["audio", "image", "pdf", "text", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12, + "reasoning": 12 + } + }, + "google/gemini-2.5-flash-lite-preview-09-2025": { + "id": "google/gemini-2.5-flash-lite-preview-09-2025", + "name": "Google: Gemini 2.5 Flash Lite Preview 09-2025", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-09-25", + "last_updated": "2026-03-15", + "modalities": { + "input": ["audio", "image", "pdf", "text", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "reasoning": 0.4, + "cache_read": 0.01, + "cache_write": 0.083333 + } + }, + "google/gemini-2.0-flash-001": { + "id": "google/gemini-2.0-flash-001", + "name": "Google: Gemini 2.0 Flash", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-12-11", + "last_updated": "2026-03-15", + "modalities": { + "input": ["audio", "image", "pdf", "text", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.025, + "cache_write": 0.083333 + } + }, + "google/lyria-3-pro-preview": { + "id": "google/lyria-3-pro-preview", + "name": "Google: Lyria 3 Pro Preview", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2026-03-30", + "last_updated": "2026-04-11", + "modalities": { + "input": ["image", "text"], + "output": ["audio", "text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "google/gemma-3n-e4b-it": { + "id": "google/gemma-3n-e4b-it", + "name": "Google: Gemma 3n 4B", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-05-20", + "last_updated": "2025-05-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 6554 + }, + "cost": { + "input": 0.02, + "output": 0.04 + } + }, + "google/gemini-3.1-flash-lite-preview": { + "id": "google/gemini-3.1-flash-lite-preview", + "name": "Google: Gemini 3.1 Flash Lite Preview", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-03", + "last_updated": "2026-03-15", + "modalities": { + "input": ["audio", "image", "pdf", "text", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.25, + "output": 1.5, + "reasoning": 1.5 + } + }, + "google/gemini-3.1-pro-preview": { + "id": "google/gemini-3.1-pro-preview", + "name": "Google: Gemini 3.1 Pro Preview", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-19", + "last_updated": "2026-03-15", + "modalities": { + "input": ["audio", "image", "pdf", "text", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12, + "reasoning": 12 + } + }, + "google/gemini-3-flash-preview": { + "id": "google/gemini-3-flash-preview", + "name": "Google: Gemini 3 Flash Preview", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-12-17", + "last_updated": "2026-03-15", + "modalities": { + "input": ["audio", "image", "pdf", "text", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 3, + "reasoning": 3, + "cache_read": 0.05, + "cache_write": 0.083333 + } + }, + "google/gemini-2.5-pro": { + "id": "google/gemini-2.5-pro", + "name": "Google: Gemini 2.5 Pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-03-20", + "last_updated": "2026-03-15", + "modalities": { + "input": ["audio", "image", "pdf", "text", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "reasoning": 10, + "cache_read": 0.125, + "cache_write": 0.375 + } + }, + "google/gemini-3-pro-image-preview": { + "id": "google/gemini-3-pro-image-preview", + "name": "Google: Nano Banana Pro (Gemini 3 Pro Image Preview)", + "attachment": true, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2025-11-20", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["image", "text"] + }, + "open_weights": false, + "limit": { + "context": 65536, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 12, + "reasoning": 12 + } + }, + "google/gemma-4-31b-it": { + "id": "google/gemma-4-31b-it", + "name": "Google: Gemma 4 31B", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-02", + "last_updated": "2026-04-11", + "modalities": { + "input": ["image", "text", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0.14, + "output": 0.4 + } + }, + "google/gemini-2.5-flash-image": { + "id": "google/gemini-2.5-flash-image", + "name": "Google: Nano Banana (Gemini 2.5 Flash Image)", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-10-08", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["image", "text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.3, + "output": 2.5 + } + }, + "google/gemma-3-12b-it": { + "id": "google/gemma-3-12b-it", + "name": "Google: Gemma 3 12B", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-03-13", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.04, + "output": 0.13, + "cache_read": 0.015 + } + }, + "google/gemini-2.5-flash": { + "id": "google/gemini-2.5-flash", + "name": "Google: Gemini 2.5 Flash", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-07-17", + "last_updated": "2026-03-15", + "modalities": { + "input": ["audio", "image", "pdf", "text", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65535 + }, + "cost": { + "input": 0.3, + "output": 2.5, + "reasoning": 2.5, + "cache_read": 0.03, + "cache_write": 0.083333 + } + }, + "google/gemini-3.1-flash-image-preview": { + "id": "google/gemini-3.1-flash-image-preview", + "name": "Google: Nano Banana 2 (Gemini 3.1 Flash Image Preview)", + "attachment": true, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2026-02-26", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["image", "text"] + }, + "open_weights": false, + "limit": { + "context": 65536, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 3 + } + }, + "google/gemma-3-4b-it": { + "id": "google/gemma-3-4b-it", + "name": "Google: Gemma 3 4B", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-03-13", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 19200 + }, + "cost": { + "input": 0.04, + "output": 0.08 + } + }, + "google/gemini-2.5-pro-preview": { + "id": "google/gemini-2.5-pro-preview", + "name": "Google: Gemini 2.5 Pro Preview 06-05", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-06-05", + "last_updated": "2026-03-15", + "modalities": { + "input": ["audio", "image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "reasoning": 10, + "cache_read": 0.125, + "cache_write": 0.375 + } + }, + "google/gemma-2-27b-it": { + "id": "google/gemma-2-27b-it", + "name": "Google: Gemma 2 27B", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-06-24", + "last_updated": "2024-06-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 2048 + }, + "cost": { + "input": 0.65, + "output": 0.65 + } + }, + "google/gemma-3-27b-it": { + "id": "google/gemma-3-27b-it", + "name": "Google: Gemma 3 27B", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-03-12", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 65536 + }, + "cost": { + "input": 0.03, + "output": 0.11, + "cache_read": 0.02 + } + }, + "google/gemma-4-26b-a4b-it": { + "id": "google/gemma-4-26b-a4b-it", + "name": "Google: Gemma 4 26B A4B", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-03", + "last_updated": "2026-04-11", + "modalities": { + "input": ["image", "text", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.12, + "output": 0.4 + } + }, + "google/gemini-2.5-flash-lite": { + "id": "google/gemini-2.5-flash-lite", + "name": "Google: Gemini 2.5 Flash Lite", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-06-17", + "last_updated": "2026-03-15", + "modalities": { + "input": ["audio", "image", "pdf", "text", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65535 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "reasoning": 0.4, + "cache_read": 0.01, + "cache_write": 0.083333 + } + }, + "google/gemini-2.0-flash-lite-001": { + "id": "google/gemini-2.0-flash-lite-001", + "name": "Google: Gemini 2.0 Flash Lite", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-12-11", + "last_updated": "2026-03-15", + "modalities": { + "input": ["audio", "image", "pdf", "text", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 8192 + }, + "cost": { + "input": 0.075, + "output": 0.3 + } + }, + "moonshotai/kimi-k2.5": { + "id": "moonshotai/kimi-k2.5", + "name": "MoonshotAI: Kimi K2.5", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-01-27", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65535 + }, + "cost": { + "input": 0.45, + "output": 2.2 + } + }, + "moonshotai/kimi-k2-0905": { + "id": "moonshotai/kimi-k2-0905", + "name": "MoonshotAI: Kimi K2 0905", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 26215 + }, + "cost": { + "input": 0.4, + "output": 2, + "cache_read": 0.15 + } + }, + "moonshotai/kimi-k2.6": { + "id": "moonshotai/kimi-k2.6", + "name": "MoonshotAI: Kimi K2.6", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2026-04-20", + "last_updated": "2026-05-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65535 + }, + "cost": { + "input": 0.75, + "output": 3.5, + "cache_read": 0.375 + } + }, + "moonshotai/kimi-k2": { + "id": "moonshotai/kimi-k2", + "name": "MoonshotAI: Kimi K2 0711", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-07-11", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131000, + "output": 26215 + }, + "cost": { + "input": 0.55, + "output": 2.2 + } + }, + "moonshotai/kimi-k2-thinking": { + "id": "moonshotai/kimi-k2-thinking", + "name": "MoonshotAI: Kimi K2 Thinking", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-11-06", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 65535 + }, + "cost": { + "input": 0.47, + "output": 2, + "cache_read": 0.2 + } + }, + "aion-labs/aion-1.0": { + "id": "aion-labs/aion-1.0", + "name": "AionLabs: Aion-1.0", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2025-02-05", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 4, + "output": 8 + } + }, + "aion-labs/aion-rp-llama-3.1-8b": { + "id": "aion-labs/aion-rp-llama-3.1-8b", + "name": "AionLabs: Aion-RP 1.0 (8B)", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-02-05", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.8, + "output": 1.6 + } + }, + "aion-labs/aion-2.0": { + "id": "aion-labs/aion-2.0", + "name": "AionLabs: Aion-2.0", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2026-02-24", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.8, + "output": 1.6 + } + }, + "aion-labs/aion-1.0-mini": { + "id": "aion-labs/aion-1.0-mini", + "name": "AionLabs: Aion-1.0-Mini", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2025-02-05", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.7, + "output": 1.4 + } + }, + "~moonshotai/kimi-latest": { + "id": "~moonshotai/kimi-latest", + "name": "MoonshotAI: Kimi Latest", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262142, + "output": 262142 + }, + "cost": { + "input": 0.74, + "output": 3.49, + "cache_read": 0.14 + } + }, + "thedrummer/unslopnemo-12b": { + "id": "thedrummer/unslopnemo-12b", + "name": "TheDrummer: UnslopNemo 12B", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-11-09", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 0.4 + } + }, + "thedrummer/cydonia-24b-v4.1": { + "id": "thedrummer/cydonia-24b-v4.1", + "name": "TheDrummer: Cydonia 24B V4.1", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-09-27", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 0.5 + } + }, + "thedrummer/skyfall-36b-v2": { + "id": "thedrummer/skyfall-36b-v2", + "name": "TheDrummer: Skyfall 36B V2", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-03-11", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.55, + "output": 0.8 + } + }, + "thedrummer/rocinante-12b": { + "id": "thedrummer/rocinante-12b", + "name": "TheDrummer: Rocinante 12B", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-09-30", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.17, + "output": 0.43 + } + }, + "anthropic/claude-opus-4.1": { + "id": "anthropic/claude-opus-4.1", + "name": "Anthropic: Claude Opus 4.1", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } + }, + "anthropic/claude-3.7-sonnet:thinking": { + "id": "anthropic/claude-3.7-sonnet:thinking", + "name": "Anthropic: Claude 3.7 Sonnet (thinking)", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-02-19", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + "anthropic/claude-opus-4.6-fast": { + "id": "anthropic/claude-opus-4.6-fast", + "name": "Anthropic: Claude Opus 4.6 (Fast)", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-05-31", + "release_date": "2026-04-07", + "last_updated": "2026-04-11", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 30, + "output": 150, + "cache_read": 3, + "cache_write": 37.5 + } + }, + "anthropic/claude-3.7-sonnet": { + "id": "anthropic/claude-3.7-sonnet", + "name": "Anthropic: Claude 3.7 Sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-02-19", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + "anthropic/claude-opus-4.6": { + "id": "anthropic/claude-opus-4.6", + "name": "Anthropic: Claude Opus 4.6", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } + }, + "anthropic/claude-opus-4.7": { + "id": "anthropic/claude-opus-4.7", + "name": "Anthropic: Claude Opus 4.7", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-04-16", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } + }, + "anthropic/claude-sonnet-4": { + "id": "anthropic/claude-sonnet-4", + "name": "Anthropic: Claude Sonnet 4", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-05-22", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + "anthropic/claude-sonnet-4.5": { + "id": "anthropic/claude-sonnet-4.5", + "name": "Anthropic: Claude Sonnet 4.5", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-09-29", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + "anthropic/claude-opus-4.5": { + "id": "anthropic/claude-opus-4.5", + "name": "Anthropic: Claude Opus 4.5", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-11-24", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } + }, + "anthropic/claude-3-haiku": { + "id": "anthropic/claude-3-haiku", + "name": "Anthropic: Claude 3 Haiku", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-03-07", + "last_updated": "2024-03-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 4096 + }, + "cost": { + "input": 0.25, + "output": 1.25, + "cache_read": 0.03, + "cache_write": 0.3 + } + }, + "anthropic/claude-opus-4": { + "id": "anthropic/claude-opus-4", + "name": "Anthropic: Claude Opus 4", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-05-22", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "pdf", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } + }, + "anthropic/claude-3.5-haiku": { + "id": "anthropic/claude-3.5-haiku", + "name": "Anthropic: Claude 3.5 Haiku", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 4, + "cache_read": 0.08, + "cache_write": 1 + } + }, + "anthropic/claude-haiku-4.5": { + "id": "anthropic/claude-haiku-4.5", + "name": "Anthropic: Claude Haiku 4.5", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } + }, + "anthropic/claude-sonnet-4.6": { + "id": "anthropic/claude-sonnet-4.6", + "name": "Anthropic: Claude Sonnet 4.6", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 3, + "output": 15 + } + }, + "switchpoint/router": { + "id": "switchpoint/router", + "name": "Switchpoint Router", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2025-07-12", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.85, + "output": 3.4 + } + }, + "bytedance/ui-tars-1.5-7b": { + "id": "bytedance/ui-tars-1.5-7b", + "name": "ByteDance: UI-TARS 7B ", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-07-23", + "last_updated": "2026-03-15", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 2048 + }, + "cost": { + "input": 0.1, + "output": 0.2 + } + }, + "tngtech/deepseek-r1t2-chimera": { + "id": "tngtech/deepseek-r1t2-chimera", + "name": "TNG: DeepSeek R1T2 Chimera", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-07-08", + "last_updated": "2025-07-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 163840 + }, + "cost": { + "input": 0.25, + "output": 0.85, + "cache_read": 0.125 + } + }, + "xiaomi/mimo-v2.5-pro": { + "id": "xiaomi/mimo-v2.5-pro", + "name": "Xiaomi: MiMo V2.5 Pro", + "family": "mimo", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.2, + "context_over_200k": { + "input": 2, + "output": 6, + "cache_read": 0.4 + }, + "tiers": [ + { + "input": 2, + "output": 6, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 256000 + } + } + ] + } + }, + "xiaomi/mimo-v2-omni": { + "id": "xiaomi/mimo-v2-omni", + "name": "Xiaomi: MiMo-V2-Omni", + "family": "mimo", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.4, + "output": 2, + "cache_read": 0.08 + } + }, + "xiaomi/mimo-v2.5": { + "id": "xiaomi/mimo-v2.5", + "name": "Xiaomi: MiMo-V2.5", + "family": "mimo", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 0.4, + "output": 2, + "cache_read": 0.08, + "context_over_200k": { + "input": 0.8, + "output": 4, + "cache_read": 0.16 + }, + "tiers": [ + { + "input": 0.8, + "output": 4, + "cache_read": 0.16, + "tier": { + "type": "context", + "size": 256000 + } + } + ] + } + }, + "xiaomi/mimo-v2-pro": { + "id": "xiaomi/mimo-v2-pro", + "name": "Xiaomi: MiMo-V2-Pro", + "family": "mimo", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.2, + "context_over_200k": { + "input": 2, + "output": 6, + "cache_read": 0.4 + }, + "tiers": [ + { + "input": 2, + "output": 6, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 256000 + } + } + ] + } + }, + "xiaomi/mimo-v2-flash": { + "id": "xiaomi/mimo-v2-flash", + "name": "Xiaomi: MiMo-V2-Flash", + "family": "mimo", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-12-01", + "release_date": "2025-12-16", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.09, + "output": 0.29, + "cache_read": 0.045 + } + } + } + }, + "sap-ai-core": { + "id": "sap-ai-core", + "env": ["AICORE_SERVICE_KEY"], + "npm": "@jerome-benoit/sap-ai-provider-v2", + "name": "SAP AI Core", + "doc": "https://help.sap.com/docs/sap-ai-core", + "models": { + "anthropic--claude-4.6-opus": { + "id": "anthropic--claude-4.6-opus", + "name": "anthropic--claude-4.6-opus", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-05", + "release_date": "2026-02-05", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } + }, + "anthropic--claude-3-haiku": { + "id": "anthropic--claude-3-haiku", + "name": "anthropic--claude-3-haiku", + "family": "claude-haiku", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2023-08-31", + "release_date": "2024-03-13", + "last_updated": "2024-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 4096 + }, + "cost": { + "input": 0.25, + "output": 1.25, + "cache_read": 0.03, + "cache_write": 0.3 + } + }, + "anthropic--claude-3-opus": { + "id": "anthropic--claude-3-opus", + "name": "anthropic--claude-3-opus", + "family": "claude-opus", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2023-08-31", + "release_date": "2024-02-29", + "last_updated": "2024-02-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 4096 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } + }, + "gpt-5-mini": { + "id": "gpt-5-mini", + "name": "gpt-5-mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.025 + } + }, + "gpt-5-nano": { + "id": "gpt-5-nano", + "name": "gpt-5-nano", + "family": "gpt-nano", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.05, + "output": 0.4, + "cache_read": 0.005 + } + }, + "gemini-2.5-pro": { + "id": "gemini-2.5-pro", + "name": "gemini-2.5-pro", + "family": "gemini-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-03-25", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } + }, + "anthropic--claude-3.7-sonnet": { + "id": "anthropic--claude-3.7-sonnet", + "name": "anthropic--claude-3.7-sonnet", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-10-31", + "release_date": "2025-02-24", + "last_updated": "2025-02-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + "sonar-pro": { + "id": "sonar-pro", + "name": "sonar-pro", + "family": "sonar-pro", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "knowledge": "2025-09-01", + "release_date": "2024-01-01", + "last_updated": "2025-09-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15 + } + }, + "anthropic--claude-4.5-sonnet": { + "id": "anthropic--claude-4.5-sonnet", + "name": "anthropic--claude-4.5-sonnet", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + "anthropic--claude-4.6-sonnet": { + "id": "anthropic--claude-4.6-sonnet", + "name": "anthropic--claude-4.6-sonnet", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-08", + "release_date": "2026-02-17", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + "sonar-deep-research": { + "id": "sonar-deep-research", + "name": "sonar-deep-research", + "family": "sonar-deep-research", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2025-02-01", + "last_updated": "2025-09-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 8, + "reasoning": 3 + } + }, + "gemini-2.5-flash": { + "id": "gemini-2.5-flash", + "name": "gemini-2.5-flash", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-03-25", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.03, + "input_audio": 1 + } + }, + "anthropic--claude-4.5-opus": { + "id": "anthropic--claude-4.5-opus", + "name": "anthropic--claude-4.5-opus", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-05", + "release_date": "2025-11-24", + "last_updated": "2025-11-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } + }, + "sonar": { + "id": "sonar", + "name": "sonar", + "family": "sonar", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "knowledge": "2025-09-01", + "release_date": "2024-01-01", + "last_updated": "2025-09-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 1, + "output": 1 + } + }, + "anthropic--claude-4-opus": { + "id": "anthropic--claude-4-opus", + "name": "anthropic--claude-4-opus", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } + }, + "anthropic--claude-3-sonnet": { + "id": "anthropic--claude-3-sonnet", + "name": "anthropic--claude-3-sonnet", + "family": "claude-sonnet", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2023-08-31", + "release_date": "2024-03-04", + "last_updated": "2024-03-04", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 4096 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + "anthropic--claude-4-sonnet": { + "id": "anthropic--claude-4-sonnet", + "name": "anthropic--claude-4-sonnet", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + "gemini-2.5-flash-lite": { + "id": "gemini-2.5-flash-lite", + "name": "gemini-2.5-flash-lite", + "family": "gemini-flash-lite", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.025 + } + }, + "anthropic--claude-4.5-haiku": { + "id": "anthropic--claude-4.5-haiku", + "name": "anthropic--claude-4.5-haiku", + "family": "claude-haiku", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } + }, + "gpt-5": { + "id": "gpt-5", + "name": "gpt-5", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } + }, + "gpt-4.1": { + "id": "gpt-4.1", + "name": "gpt-4.1", + "family": "gpt", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } + }, + "gpt-4.1-mini": { + "id": "gpt-4.1-mini", + "name": "gpt-4.1-mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 1.6, + "cache_read": 0.1 + } + }, + "anthropic--claude-3.5-sonnet": { + "id": "anthropic--claude-3.5-sonnet", + "name": "anthropic--claude-3.5-sonnet", + "family": "claude-sonnet", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04-30", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + } + } + }, + "morph": { + "id": "morph", + "env": ["MORPH_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.morphllm.com/v1", + "name": "Morph", + "doc": "https://docs.morphllm.com/api-reference/introduction", + "models": { + "auto": { + "id": "auto", + "name": "Auto", + "family": "auto", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2024-06-01", + "last_updated": "2024-06-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32000, + "output": 32000 + }, + "cost": { + "input": 0.85, + "output": 1.55 + } + }, + "morph-v3-fast": { + "id": "morph-v3-fast", + "name": "Morph v3 Fast", + "family": "morph", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2024-08-15", + "last_updated": "2024-08-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16000, + "output": 16000 + }, + "cost": { + "input": 0.8, + "output": 1.2 + } + }, + "morph-v3-large": { + "id": "morph-v3-large", + "name": "Morph v3 Large", + "family": "morph", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2024-08-15", + "last_updated": "2024-08-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32000, + "output": 32000 + }, + "cost": { + "input": 0.9, + "output": 1.9 + } + } + } + }, + "cloudflare-ai-gateway": { + "id": "cloudflare-ai-gateway", + "env": ["CLOUDFLARE_API_TOKEN", "CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_GATEWAY_ID"], + "npm": "ai-gateway-provider", + "name": "Cloudflare AI Gateway", + "doc": "https://developers.cloudflare.com/ai-gateway/", + "models": { + "workers-ai/@cf/myshell-ai/melotts": { + "id": "workers-ai/@cf/myshell-ai/melotts", + "name": "MyShell MeloTTS", + "family": "melotts", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-11-14", + "last_updated": "2025-11-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "workers-ai/@cf/ibm-granite/granite-4.0-h-micro": { + "id": "workers-ai/@cf/ibm-granite/granite-4.0-h-micro", + "name": "IBM Granite 4.0 H Micro", + "family": "granite", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.017, + "output": 0.11 + } + }, + "workers-ai/@cf/huggingface/distilbert-sst-2-int8": { + "id": "workers-ai/@cf/huggingface/distilbert-sst-2-int8", + "name": "DistilBERT SST-2 INT8", + "family": "distilbert", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.026, + "output": 0 + } + }, + "workers-ai/@cf/zai-org/glm-4.7-flash": { + "id": "workers-ai/@cf/zai-org/glm-4.7-flash", + "name": "GLM-4.7-Flash", + "family": "glm-flash", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-01-19", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.06, + "output": 0.4 + } + }, + "workers-ai/@cf/pipecat-ai/smart-turn-v2": { + "id": "workers-ai/@cf/pipecat-ai/smart-turn-v2", + "name": "Pipecat Smart Turn v2", + "family": "smart-turn", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-11-14", + "last_updated": "2025-11-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "workers-ai/@cf/mistralai/mistral-small-3.1-24b-instruct": { + "id": "workers-ai/@cf/mistralai/mistral-small-3.1-24b-instruct", + "name": "Mistral Small 3.1 24B Instruct", + "family": "mistral-small", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-11", + "last_updated": "2025-04-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.35, + "output": 0.56 + } + }, + "workers-ai/@cf/facebook/bart-large-cnn": { + "id": "workers-ai/@cf/facebook/bart-large-cnn", + "name": "BART Large CNN", + "family": "bart", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-09", + "last_updated": "2025-04-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "workers-ai/@cf/aisingapore/gemma-sea-lion-v4-27b-it": { + "id": "workers-ai/@cf/aisingapore/gemma-sea-lion-v4-27b-it", + "name": "Gemma SEA-LION v4 27B IT", + "family": "gemma", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.35, + "output": 0.56 + } + }, + "workers-ai/@cf/nvidia/nemotron-3-120b-a12b": { + "id": "workers-ai/@cf/nvidia/nemotron-3-120b-a12b", + "name": "Nemotron 3 Super 120B", + "family": "nemotron", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-03-11", + "last_updated": "2026-03-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.5, + "output": 1.5 + } + }, + "workers-ai/@cf/deepseek-ai/deepseek-r1-distill-qwen-32b": { + "id": "workers-ai/@cf/deepseek-ai/deepseek-r1-distill-qwen-32b", + "name": "DeepSeek R1 Distill Qwen 32B", + "family": "deepseek-thinking", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.5, + "output": 4.88 + } + }, + "workers-ai/@cf/openai/gpt-oss-20b": { + "id": "workers-ai/@cf/openai/gpt-oss-20b", + "name": "GPT OSS 20B", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.2, + "output": 0.3 + } + }, + "workers-ai/@cf/openai/gpt-oss-120b": { + "id": "workers-ai/@cf/openai/gpt-oss-120b", + "name": "GPT OSS 120B", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.35, + "output": 0.75 + } + }, + "workers-ai/@cf/mistral/mistral-7b-instruct-v0.1": { + "id": "workers-ai/@cf/mistral/mistral-7b-instruct-v0.1", + "name": "Mistral 7B Instruct v0.1", + "family": "mistral", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.11, + "output": 0.19 + } + }, + "workers-ai/@cf/meta/llama-4-scout-17b-16e-instruct": { + "id": "workers-ai/@cf/meta/llama-4-scout-17b-16e-instruct", + "name": "Llama 4 Scout 17B 16E Instruct", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.27, + "output": 0.85 + } + }, + "workers-ai/@cf/meta/llama-3-8b-instruct-awq": { + "id": "workers-ai/@cf/meta/llama-3-8b-instruct-awq", + "name": "Llama 3 8B Instruct AWQ", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.12, + "output": 0.27 + } + }, + "workers-ai/@cf/meta/llama-guard-3-8b": { + "id": "workers-ai/@cf/meta/llama-guard-3-8b", + "name": "Llama Guard 3 8B", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.48, + "output": 0.03 + } + }, + "workers-ai/@cf/meta/m2m100-1.2b": { + "id": "workers-ai/@cf/meta/m2m100-1.2b", + "name": "M2M100 1.2B", + "family": "m2m", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.34, + "output": 0.34 + } + }, + "workers-ai/@cf/meta/llama-2-7b-chat-fp16": { + "id": "workers-ai/@cf/meta/llama-2-7b-chat-fp16", + "name": "Llama 2 7B Chat FP16", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.56, + "output": 6.67 + } + }, + "workers-ai/@cf/meta/llama-3.2-11b-vision-instruct": { + "id": "workers-ai/@cf/meta/llama-3.2-11b-vision-instruct", + "name": "Llama 3.2 11B Vision Instruct", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.049, + "output": 0.68 + } + }, + "workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast": { + "id": "workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast", + "name": "Llama 3.3 70B Instruct FP8 Fast", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.29, + "output": 2.25 + } + }, + "workers-ai/@cf/meta/llama-3.2-1b-instruct": { + "id": "workers-ai/@cf/meta/llama-3.2-1b-instruct", + "name": "Llama 3.2 1B Instruct", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.027, + "output": 0.2 + } + }, + "workers-ai/@cf/meta/llama-3.1-8b-instruct-fp8": { + "id": "workers-ai/@cf/meta/llama-3.1-8b-instruct-fp8", + "name": "Llama 3.1 8B Instruct FP8", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.29 + } + }, + "workers-ai/@cf/meta/llama-3.2-3b-instruct": { + "id": "workers-ai/@cf/meta/llama-3.2-3b-instruct", + "name": "Llama 3.2 3B Instruct", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.051, + "output": 0.34 + } + }, + "workers-ai/@cf/meta/llama-3.1-8b-instruct-awq": { + "id": "workers-ai/@cf/meta/llama-3.1-8b-instruct-awq", + "name": "Llama 3.1 8B Instruct AWQ", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.12, + "output": 0.27 + } + }, + "workers-ai/@cf/meta/llama-3-8b-instruct": { + "id": "workers-ai/@cf/meta/llama-3-8b-instruct", + "name": "Llama 3 8B Instruct", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.28, + "output": 0.83 + } + }, + "workers-ai/@cf/meta/llama-3.1-8b-instruct": { + "id": "workers-ai/@cf/meta/llama-3.1-8b-instruct", + "name": "Llama 3.1 8B Instruct", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.28, + "output": 0.8299999999999998 + } + }, + "workers-ai/@cf/qwen/qwen2.5-coder-32b-instruct": { + "id": "workers-ai/@cf/qwen/qwen2.5-coder-32b-instruct", + "name": "Qwen 2.5 Coder 32B Instruct", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-11", + "last_updated": "2025-04-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.66, + "output": 1 + } + }, + "workers-ai/@cf/qwen/qwen3-embedding-0.6b": { + "id": "workers-ai/@cf/qwen/qwen3-embedding-0.6b", + "name": "Qwen3 Embedding 0.6B", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-11-14", + "last_updated": "2025-11-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.012, + "output": 0 + } + }, + "workers-ai/@cf/qwen/qwq-32b": { + "id": "workers-ai/@cf/qwen/qwq-32b", + "name": "QwQ 32B", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-11", + "last_updated": "2025-04-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.66, + "output": 1 + } + }, + "workers-ai/@cf/qwen/qwen3-30b-a3b-fp8": { + "id": "workers-ai/@cf/qwen/qwen3-30b-a3b-fp8", + "name": "Qwen3 30B A3B FP8", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-11-14", + "last_updated": "2025-11-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.051, + "output": 0.34 + } + }, + "workers-ai/@cf/google/gemma-3-12b-it": { + "id": "workers-ai/@cf/google/gemma-3-12b-it", + "name": "Gemma 3 12B IT", + "family": "gemma", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-11", + "last_updated": "2025-04-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.35, + "output": 0.56 + } + }, + "workers-ai/@cf/moonshotai/kimi-k2.5": { + "id": "workers-ai/@cf/moonshotai/kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.6, + "output": 3, + "cache_read": 0.1 + } + }, + "workers-ai/@cf/moonshotai/kimi-k2.6": { + "id": "workers-ai/@cf/moonshotai/kimi-k2.6", + "name": "Kimi K2.6", + "family": "kimi", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-04-20", + "last_updated": "2026-04-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.95, + "output": 4, + "cache_read": 0.16 + } + }, + "workers-ai/@cf/ai4bharat/indictrans2-en-indic-1B": { + "id": "workers-ai/@cf/ai4bharat/indictrans2-en-indic-1B", + "name": "IndicTrans2 EN-Indic 1B", + "family": "indictrans", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.34, + "output": 0.34 + } + }, + "workers-ai/@cf/pfnet/plamo-embedding-1b": { + "id": "workers-ai/@cf/pfnet/plamo-embedding-1b", + "name": "PLaMo Embedding 1B", + "family": "plamo", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.019, + "output": 0 + } + }, + "workers-ai/@cf/baai/bge-small-en-v1.5": { + "id": "workers-ai/@cf/baai/bge-small-en-v1.5", + "name": "BGE Small EN v1.5", + "family": "bge", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.02, + "output": 0 + } + }, + "workers-ai/@cf/baai/bge-large-en-v1.5": { + "id": "workers-ai/@cf/baai/bge-large-en-v1.5", + "name": "BGE Large EN v1.5", + "family": "bge", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.2, + "output": 0 + } + }, + "workers-ai/@cf/baai/bge-reranker-base": { + "id": "workers-ai/@cf/baai/bge-reranker-base", + "name": "BGE Reranker Base", + "family": "bge", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-09", + "last_updated": "2025-04-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.0031, + "output": 0 + } + }, + "workers-ai/@cf/baai/bge-base-en-v1.5": { + "id": "workers-ai/@cf/baai/bge-base-en-v1.5", + "name": "BGE Base EN v1.5", + "family": "bge", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.067, + "output": 0 + } + }, + "workers-ai/@cf/baai/bge-m3": { + "id": "workers-ai/@cf/baai/bge-m3", + "name": "BGE M3", + "family": "bge", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.012, + "output": 0 + } + }, + "workers-ai/@cf/deepgram/aura-2-en": { + "id": "workers-ai/@cf/deepgram/aura-2-en", + "name": "Deepgram Aura 2 (EN)", + "family": "aura", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-11-14", + "last_updated": "2025-11-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "workers-ai/@cf/deepgram/aura-2-es": { + "id": "workers-ai/@cf/deepgram/aura-2-es", + "name": "Deepgram Aura 2 (ES)", + "family": "aura", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-11-14", + "last_updated": "2025-11-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "workers-ai/@cf/deepgram/nova-3": { + "id": "workers-ai/@cf/deepgram/nova-3", + "name": "Deepgram Nova 3", + "family": "nova", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-11-14", + "last_updated": "2025-11-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "openai/gpt-5.3-codex": { + "id": "openai/gpt-5.3-codex", + "name": "GPT-5.3 Codex", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "provider": { + "npm": "ai-gateway-provider" + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } + }, + "openai/gpt-4-turbo": { + "id": "openai/gpt-4-turbo", + "name": "GPT-4 Turbo", + "family": "gpt", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": false, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2023-11-06", + "last_updated": "2024-04-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 10, + "output": 30 + } + }, + "openai/gpt-5.2": { + "id": "openai/gpt-5.2", + "name": "GPT-5.2", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } + }, + "openai/o3-pro": { + "id": "openai/o3-pro", + "name": "o3-pro", + "family": "o-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2025-06-10", + "last_updated": "2025-06-10", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 20, + "output": 80 + } + }, + "openai/gpt-4o-mini": { + "id": "openai/gpt-4o-mini", + "name": "GPT-4o mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2023-09", + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.08 + } + }, + "openai/o4-mini": { + "id": "openai/o4-mini", + "name": "o4-mini", + "family": "o-mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.28 + } + }, + "openai/gpt-5.2-codex": { + "id": "openai/gpt-5.2-codex", + "name": "GPT-5.2 Codex", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "provider": { + "npm": "ai-gateway-provider" + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } + }, + "openai/gpt-5.1": { + "id": "openai/gpt-5.1", + "name": "GPT-5.1", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.13 + } + }, + "openai/o1": { + "id": "openai/o1", + "name": "o1", + "family": "o", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2023-09", + "release_date": "2024-12-05", + "last_updated": "2024-12-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 15, + "output": 60, + "cache_read": 7.5 + } + }, + "openai/gpt-3.5-turbo": { + "id": "openai/gpt-3.5-turbo", + "name": "GPT-3.5-turbo", + "family": "gpt", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "temperature": true, + "knowledge": "2021-09-01", + "release_date": "2023-03-01", + "last_updated": "2023-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16385, + "output": 4096 + }, + "cost": { + "input": 0.5, + "output": 1.5, + "cache_read": 1.25 + } + }, + "openai/o3-mini": { + "id": "openai/o3-mini", + "name": "o3-mini", + "family": "o-mini", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2024-12-20", + "last_updated": "2025-01-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.55 + } + }, + "openai/gpt-4": { + "id": "openai/gpt-4", + "name": "GPT-4", + "family": "gpt", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": false, + "temperature": true, + "knowledge": "2023-11", + "release_date": "2023-11-06", + "last_updated": "2024-04-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 30, + "output": 60 + } + }, + "openai/gpt-5.4": { + "id": "openai/gpt-5.4", + "name": "GPT-5.4", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "provider": { + "npm": "ai-gateway-provider" + }, + "cost": { + "input": 2.5, + "output": 15, + "cache_read": 0.25 + } + }, + "openai/o3": { + "id": "openai/o3", + "name": "o3", + "family": "o", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } + }, + "openai/gpt-4o": { + "id": "openai/gpt-4o", + "name": "GPT-4o", + "family": "gpt", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2023-09", + "release_date": "2024-05-13", + "last_updated": "2024-08-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10, + "cache_read": 1.25 + } + }, + "openai/gpt-5.1-codex": { + "id": "openai/gpt-5.1-codex", + "name": "GPT-5.1 Codex", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } + }, + "anthropic/claude-haiku-4-5": { + "id": "anthropic/claude-haiku-4-5", + "name": "Claude Haiku 4.5 (latest)", + "family": "claude-haiku", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } + }, + "anthropic/claude-sonnet-4-6": { + "id": "anthropic/claude-sonnet-4-6", + "name": "Claude Sonnet 4.6", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": true, + "temperature": true, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-02-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 64000 + }, + "provider": { + "npm": "ai-gateway-provider" + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75, + "tiers": [ + { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5 + } + } + }, + "anthropic/claude-opus-4-7": { + "id": "anthropic/claude-opus-4-7", + "name": "Claude Opus 4.7", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2026-01", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/anthropic" + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } + }, + "anthropic/claude-opus-4-1": { + "id": "anthropic/claude-opus-4-1", + "name": "Claude Opus 4.1 (latest)", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } + }, + "anthropic/claude-3-5-haiku": { + "id": "anthropic/claude-3-5-haiku", + "name": "Claude Haiku 3.5 (latest)", + "family": "claude-haiku", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-07-31", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 4, + "cache_read": 0.08, + "cache_write": 1 + } + }, + "anthropic/claude-3.5-sonnet": { + "id": "anthropic/claude-3.5-sonnet", + "name": "Claude Sonnet 3.5 v2", + "family": "claude-sonnet", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04-30", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + "anthropic/claude-sonnet-4": { + "id": "anthropic/claude-sonnet-4", + "name": "Claude Sonnet 4 (latest)", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + "anthropic/claude-opus-4-5": { + "id": "anthropic/claude-opus-4-5", + "name": "Claude Opus 4.5 (latest)", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-11-24", + "last_updated": "2025-11-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } + }, + "anthropic/claude-3-haiku": { + "id": "anthropic/claude-3-haiku", + "name": "Claude Haiku 3", + "family": "claude-haiku", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2023-08-31", + "release_date": "2024-03-13", + "last_updated": "2024-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 4096 + }, + "cost": { + "input": 0.25, + "output": 1.25, + "cache_read": 0.03, + "cache_write": 0.3 + } + }, + "anthropic/claude-opus-4": { + "id": "anthropic/claude-opus-4", + "name": "Claude Opus 4 (latest)", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } + }, + "anthropic/claude-opus-4-6": { + "id": "anthropic/claude-opus-4-6", + "name": "Claude Opus 4.6 (latest)", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25, + "tiers": [ + { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5 + } + } + }, + "anthropic/claude-3.5-haiku": { + "id": "anthropic/claude-3.5-haiku", + "name": "Claude Haiku 3.5 (latest)", + "family": "claude-haiku", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-07-31", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 4, + "cache_read": 0.08, + "cache_write": 1 + } + }, + "anthropic/claude-sonnet-4-5": { + "id": "anthropic/claude-sonnet-4-5", + "name": "Claude Sonnet 4.5 (latest)", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + "anthropic/claude-3-sonnet": { + "id": "anthropic/claude-3-sonnet", + "name": "Claude Sonnet 3", + "family": "claude-sonnet", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2023-08-31", + "release_date": "2024-03-04", + "last_updated": "2024-03-04", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 4096 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 0.3 + } + }, + "anthropic/claude-3-opus": { + "id": "anthropic/claude-3-opus", + "name": "Claude Opus 3", + "family": "claude-opus", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2023-08-31", + "release_date": "2024-02-29", + "last_updated": "2024-02-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 4096 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } + }, + "openai/gpt-5.5": { + "id": "openai/gpt-5.5", + "name": "GPT-5.5", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-12-01", + "release_date": "2026-04-23", + "last_updated": "2026-04-23", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5, + "context_over_200k": { + "input": 10, + "output": 45, + "cache_read": 1 + }, + "tiers": [ + { + "input": 10, + "output": 45, + "cache_read": 1, + "tier": { + "type": "context", + "size": 272000 + } + } + ] + } + } + } + }, + "github-copilot": { + "id": "github-copilot", + "env": ["GITHUB_TOKEN"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.githubcopilot.com", + "name": "GitHub Copilot", + "doc": "https://docs.github.com/en/copilot", + "models": { + "gpt-5.1-codex-max": { + "id": "gpt-5.1-codex-max", + "name": "GPT-5.1-Codex-max", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-12-04", + "last_updated": "2025-12-04", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 128000, + "output": 128000 + }, + "status": "deprecated", + "cost": { + "input": 0, + "output": 0 + } + }, + "claude-opus-4.6": { + "id": "claude-opus-4.6", + "name": "Claude Opus 4.6", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 144000, + "input": 128000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "gemini-3.1-pro-preview": { + "id": "gemini-3.1-pro-preview", + "name": "Gemini 3.1 Pro Preview", + "family": "gemini-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-02-19", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "gemini-3-flash-preview": { + "id": "gemini-3-flash-preview", + "name": "Gemini 3 Flash", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "gpt-5.5": { + "id": "gpt-5.5", + "name": "GPT-5.5", + "family": "gpt", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "gpt-5-mini": { + "id": "gpt-5-mini", + "name": "GPT-5-mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-06", + "release_date": "2025-08-13", + "last_updated": "2025-08-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 264000, + "input": 128000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "gemini-3-pro-preview": { + "id": "gemini-3-pro-preview", + "name": "Gemini 3 Pro Preview", + "family": "gemini-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 64000 + }, + "status": "deprecated", + "cost": { + "input": 0, + "output": 0 + } + }, + "gpt-5.3-codex": { + "id": "gpt-5.3-codex", + "name": "GPT-5.3-Codex", + "family": "gpt-codex", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-02-24", + "last_updated": "2026-02-24", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "gemini-2.5-pro": { + "id": "gemini-2.5-pro", + "name": "Gemini 2.5 Pro", + "family": "gemini-pro", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "gpt-5.2": { + "id": "gpt-5.2", + "name": "GPT-5.2", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 264000, + "input": 128000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "gpt-5.4-mini": { + "id": "gpt-5.4-mini", + "name": "GPT-5.4 Mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "claude-opus-4.7": { + "id": "claude-opus-4.7", + "name": "Claude Opus 4.7", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 144000, + "input": 128000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "gpt-5.2-codex": { + "id": "gpt-5.2-codex", + "name": "GPT-5.2-Codex", + "family": "gpt-codex", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "gpt-5.1-codex-mini": { + "id": "gpt-5.1-codex-mini", + "name": "GPT-5.1-Codex-mini", + "family": "gpt-codex", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 128000, + "output": 128000 + }, + "status": "deprecated", + "cost": { + "input": 0, + "output": 0 + } + }, + "claude-sonnet-4": { + "id": "claude-sonnet-4", + "name": "Claude Sonnet 4", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 216000, + "input": 128000, + "output": 16000 + }, + "status": "deprecated", + "cost": { + "input": 0, + "output": 0 + } + }, + "grok-code-fast-1": { + "id": "grok-code-fast-1", + "name": "Grok Code Fast 1", + "family": "grok", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-08", + "release_date": "2025-08-27", + "last_updated": "2025-08-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "gpt-5.1": { + "id": "gpt-5.1", + "name": "GPT-5.1", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 264000, + "input": 128000, + "output": 64000 + }, + "status": "deprecated", + "cost": { + "input": 0, + "output": 0 + } + }, + "claude-sonnet-4.5": { + "id": "claude-sonnet-4.5", + "name": "Claude Sonnet 4.5", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 144000, + "input": 128000, + "output": 32000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "claude-opus-41": { + "id": "claude-opus-41", + "name": "Claude Opus 4.1", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": false, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 80000, + "output": 16000 + }, + "status": "deprecated", + "cost": { + "input": 0, + "output": 0 + } + }, + "claude-opus-4.5": { + "id": "claude-opus-4.5", + "name": "Claude Opus 4.5", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-11-24", + "last_updated": "2025-08-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 160000, + "input": 128000, + "output": 32000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "gpt-5.4": { + "id": "gpt-5.4", + "name": "GPT-5.4", + "family": "gpt", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "gpt-4o": { + "id": "gpt-4o", + "name": "GPT-4o", + "family": "gpt", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2023-09", + "release_date": "2024-05-13", + "last_updated": "2024-05-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 64000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "gpt-5": { + "id": "gpt-5", + "name": "GPT-5", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 128000 + }, + "status": "deprecated", + "cost": { + "input": 0, + "output": 0 + } + }, + "claude-haiku-4.5": { + "id": "claude-haiku-4.5", + "name": "Claude Haiku 4.5", + "family": "claude-haiku", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 144000, + "input": 128000, + "output": 32000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "gpt-4.1": { + "id": "gpt-4.1", + "name": "GPT-4.1", + "family": "gpt", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 64000, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "claude-sonnet-4.6": { + "id": "claude-sonnet-4.6", + "name": "Claude Sonnet 4.6", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-02-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "input": 128000, + "output": 32000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "gpt-5.1-codex": { + "id": "gpt-5.1-codex", + "name": "GPT-5.1-Codex", + "family": "gpt-codex", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 128000, + "output": 128000 + }, + "status": "deprecated", + "cost": { + "input": 0, + "output": 0 + } + } + } + }, + "mixlayer": { + "id": "mixlayer", + "env": ["MIXLAYER_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://models.mixlayer.ai/v1", + "name": "Mixlayer", + "doc": "https://docs.mixlayer.com", + "models": { + "qwen/qwen3.5-122b-a10b": { + "id": "qwen/qwen3.5-122b-a10b", + "name": "Qwen3.5 122B A10B", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.4, + "output": 3.2 + } + }, + "qwen/qwen3.5-27b": { + "id": "qwen/qwen3.5-27b", + "name": "Qwen3.5 27B", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.3, + "output": 2.4 + } + }, + "qwen/qwen3.5-397b-a17b": { + "id": "qwen/qwen3.5-397b-a17b", + "name": "Qwen3.5 397B A17B", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 3.6 + } + }, + "qwen/qwen3.5-9b": { + "id": "qwen/qwen3.5-9b", + "name": "Qwen3.5 9B", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } + }, + "qwen/qwen3.5-35b-a3b": { + "id": "qwen/qwen3.5-35b-a3b", + "name": "Qwen3.5 35B A3B", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.25, + "output": 1.3 + } + } + } + }, + "xiaomi-token-plan-sgp": { + "id": "xiaomi-token-plan-sgp", + "env": ["XIAOMI_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://token-plan-sgp.xiaomimimo.com/v1", + "name": "Xiaomi Token Plan (Singapore)", + "doc": "https://platform.xiaomimimo.com/#/docs", + "models": { + "mimo-v2-tts": { + "id": "mimo-v2-tts", + "name": "MiMo-V2-TTS", + "family": "mimo", + "attachment": false, + "reasoning": false, + "tool_call": false, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["audio"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "mimo-v2-flash": { + "id": "mimo-v2-flash", + "name": "MiMo-V2-Flash", + "family": "mimo", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12-01", + "release_date": "2025-12-16", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, + "mimo-v2-pro": { + "id": "mimo-v2-pro", + "name": "MiMo-V2-Pro", + "family": "mimo", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, + "mimo-v2.5": { + "id": "mimo-v2.5", + "name": "MiMo-V2.5", + "family": "mimo", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, + "mimo-v2-omni": { + "id": "mimo-v2-omni", + "name": "MiMo-V2-Omni", + "family": "mimo", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, + "mimo-v2.5-pro": { + "id": "mimo-v2.5-pro", + "name": "MiMo-V2.5-Pro", + "family": "mimo", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + } + } + }, + "zai": { + "id": "zai", + "env": ["ZHIPU_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.z.ai/api/paas/v4", + "name": "Z.AI", + "doc": "https://docs.z.ai/guides/overview/pricing", + "models": { + "glm-5v-turbo": { + "id": "glm-5v-turbo", + "name": "GLM-5V-Turbo", + "family": "glm", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-04-01", + "last_updated": "2026-04-01", + "modalities": { + "input": ["text", "image", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 1.2, + "output": 4, + "cache_read": 0.24, + "cache_write": 0 + } + }, + "glm-4.7": { + "id": "glm-4.7", + "name": "GLM-4.7", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.11, + "cache_write": 0 + } + }, + "glm-5": { + "id": "glm-5", + "name": "GLM-5", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3.2, + "cache_read": 0.2, + "cache_write": 0 + } + }, + "glm-4.7-flashx": { + "id": "glm-4.7-flashx", + "name": "GLM-4.7-FlashX", + "family": "glm-flash", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-01-19", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 0.07, + "output": 0.4, + "cache_read": 0.01, + "cache_write": 0 + } + }, + "glm-5.1": { + "id": "glm-5.1", + "name": "GLM-5.1", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "release_date": "2026-03-27", + "last_updated": "2026-03-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 1.4, + "output": 4.4, + "cache_read": 0.26, + "cache_write": 0 + } + }, + "glm-4.5": { + "id": "glm-4.5", + "name": "GLM-4.5", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 98304 + }, + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.11, + "cache_write": 0 + } + }, + "glm-4.5-air": { + "id": "glm-4.5-air", + "name": "GLM-4.5-Air", + "family": "glm-air", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 98304 + }, + "cost": { + "input": 0.2, + "output": 1.1, + "cache_read": 0.03, + "cache_write": 0 + } + }, + "glm-5-turbo": { + "id": "glm-5-turbo", + "name": "GLM-5-Turbo", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "release_date": "2026-03-16", + "last_updated": "2026-03-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 1.2, + "output": 4, + "cache_read": 0.24, + "cache_write": 0 + } + }, + "glm-4.5v": { + "id": "glm-4.5v", + "name": "GLM-4.5V", + "family": "glm", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-08-11", + "last_updated": "2025-08-11", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 64000, + "output": 16384 + }, + "cost": { + "input": 0.6, + "output": 1.8 + } + }, + "glm-4.6": { + "id": "glm-4.6", + "name": "GLM-4.6", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.11, + "cache_write": 0 + } + }, + "glm-4.6v": { + "id": "glm-4.6v", + "name": "GLM-4.6V", + "family": "glm", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-12-08", + "last_updated": "2025-12-08", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.3, + "output": 0.9 + } + }, + "glm-4.5-flash": { + "id": "glm-4.5-flash", + "name": "GLM-4.5-Flash", + "family": "glm-flash", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 98304 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } + }, + "glm-4.7-flash": { + "id": "glm-4.7-flash", + "name": "GLM-4.7-Flash", + "family": "glm-flash", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-01-19", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } + } + } + }, + "opencode": { + "id": "opencode", + "env": ["OPENCODE_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://opencode.ai/zen/v1", + "name": "OpenCode Zen", + "doc": "https://opencode.ai/docs/zen", + "models": { + "minimax-m2.7": { + "id": "minimax-m2.7", + "name": "MiniMax M2.7", + "family": "minimax", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06 + } + }, + "gpt-5.1-codex-max": { + "id": "gpt-5.1-codex-max", + "name": "GPT-5.1 Codex Max", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai" + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } + }, + "claude-haiku-4-5": { + "id": "claude-haiku-4-5", + "name": "Claude Haiku 4.5", + "family": "claude-haiku", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic" + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } + }, + "kimi-k2.5": { + "id": "kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.6, + "output": 3, + "cache_read": 0.08 + } + }, + "glm-4.7": { + "id": "glm-4.7", + "name": "GLM-4.7", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "status": "deprecated", + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.1 + } + }, + "glm-5": { + "id": "glm-5", + "name": "GLM-5", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3.2, + "cache_read": 0.2 + } + }, + "glm-4.7-free": { + "id": "glm-4.7-free", + "name": "GLM-4.7 Free", + "family": "glm-free", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "status": "deprecated", + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, + "gemini-3.1-pro": { + "id": "gemini-3.1-pro", + "name": "Gemini 3.1 Pro Preview", + "family": "gemini-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-02-19", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "provider": { + "npm": "@ai-sdk/google" + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2, + "tiers": [ + { + "input": 4, + "output": 18, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 18, + "cache_read": 0.4 + } + } + }, + "claude-sonnet-4-6": { + "id": "claude-sonnet-4-6", + "name": "Claude Sonnet 4.6", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": true, + "temperature": true, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-02-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic" + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + "kimi-k2.5-free": { + "id": "kimi-k2.5-free", + "name": "Kimi K2.5 Free", + "family": "kimi-free", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "status": "deprecated", + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, + "claude-opus-4-7": { + "id": "claude-opus-4-7", + "name": "Claude Opus 4.7", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/anthropic" + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } + }, + "gpt-5-nano": { + "id": "gpt-5-nano", + "name": "GPT-5 Nano", + "family": "gpt-nano", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai" + }, + "cost": { + "input": 0.05, + "output": 0.4, + "cache_read": 0.005 + } + }, + "gpt-5.3-codex": { + "id": "gpt-5.3-codex", + "name": "GPT-5.3 Codex", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-02-24", + "last_updated": "2026-02-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai" + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } + }, + "minimax-m2.5-free": { + "id": "minimax-m2.5-free", + "name": "MiniMax M2.5 Free", + "family": "minimax-free", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "provider": { + "npm": "@ai-sdk/anthropic" + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, + "ring-2.6-1t-free": { + "id": "ring-2.6-1t-free", + "name": "Ring 2.6 1T Free", + "family": "ring-1t-free", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2025-06", + "release_date": "2026-05-08", + "last_updated": "2026-05-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262000, + "output": 66000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "gpt-5.2": { + "id": "gpt-5.2", + "name": "GPT-5.2", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai" + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } + }, + "deepseek-v4-flash-free": { + "id": "deepseek-v4-flash-free", + "name": "DeepSeek V4 Flash Free", + "family": "deepseek-flash-free", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, + "big-pickle": { + "id": "big-pickle", + "name": "Big Pickle", + "family": "big-pickle", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-10-17", + "last_updated": "2025-10-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } + }, + "claude-opus-4-1": { + "id": "claude-opus-4-1", + "name": "Claude Opus 4.1", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 32000 + }, + "provider": { + "npm": "@ai-sdk/anthropic" + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } + }, + "qwen3.6-plus": { + "id": "qwen3.6-plus", + "name": "Qwen3.6 Plus", + "family": "qwen3.6", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 65536 + }, + "provider": { + "npm": "@ai-sdk/anthropic" + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.05, + "cache_write": 0.625 + } + }, + "gpt-5.4-mini": { + "id": "gpt-5.4-mini", + "name": "GPT-5.4 Mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai" + }, + "cost": { + "input": 0.75, + "output": 4.5, + "cache_read": 0.075 + } + }, + "claude-3-5-haiku": { + "id": "claude-3-5-haiku", + "name": "Claude Haiku 3.5", + "family": "claude-haiku", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-07-31", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "status": "deprecated", + "provider": { + "npm": "@ai-sdk/anthropic" + }, + "cost": { + "input": 0.8, + "output": 4, + "cache_read": 0.08, + "cache_write": 1 + } + }, + "minimax-m2.1": { + "id": "minimax-m2.1", + "name": "MiniMax M2.1", + "family": "minimax", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "status": "deprecated", + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.1 + } + }, + "glm-5.1": { + "id": "glm-5.1", + "name": "GLM-5.1", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-04-07", + "last_updated": "2026-04-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 1.4, + "output": 4.4, + "cache_read": 0.26 + } + }, + "gpt-5.4-nano": { + "id": "gpt-5.4-nano", + "name": "GPT-5.4 Nano", + "family": "gpt-nano", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai" + }, + "cost": { + "input": 0.2, + "output": 1.25, + "cache_read": 0.02 + } + }, + "gpt-5.2-codex": { + "id": "gpt-5.2-codex", + "name": "GPT-5.2 Codex", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-01-14", + "last_updated": "2026-01-14", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai" + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } + }, + "gpt-5.1-codex-mini": { + "id": "gpt-5.1-codex-mini", + "name": "GPT-5.1 Codex Mini", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai" + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.025 + } + }, + "claude-sonnet-4": { + "id": "claude-sonnet-4", + "name": "Claude Sonnet 4", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic" + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75, + "tiers": [ + { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5 + } + } + }, + "gemini-3-flash": { + "id": "gemini-3-flash", + "name": "Gemini 3 Flash", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "provider": { + "npm": "@ai-sdk/google" + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.05 + } + }, + "trinity-large-preview-free": { + "id": "trinity-large-preview-free", + "name": "Trinity Large Preview", + "family": "trinity", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2025-06", + "release_date": "2026-01-28", + "last_updated": "2026-01-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "status": "deprecated", + "cost": { + "input": 0, + "output": 0 + } + }, + "gpt-5.1": { + "id": "gpt-5.1", + "name": "GPT-5.1", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai" + }, + "cost": { + "input": 1.07, + "output": 8.5, + "cache_read": 0.107 + } + }, + "gpt-5.4-pro": { + "id": "gpt-5.4-pro", + "name": "GPT-5.4 Pro", + "family": "gpt-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": false, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai" + }, + "cost": { + "input": 30, + "output": 180, + "cache_read": 30 + } + }, + "glm-5-free": { + "id": "glm-5-free", + "name": "GLM-5 Free", + "family": "glm-free", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "status": "deprecated", + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, + "claude-opus-4-5": { + "id": "claude-opus-4-5", + "name": "Claude Opus 4.5", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-11-24", + "last_updated": "2025-11-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic" + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } + }, + "minimax-m2.1-free": { + "id": "minimax-m2.1-free", + "name": "MiniMax M2.1 Free", + "family": "minimax-free", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "status": "deprecated", + "provider": { + "npm": "@ai-sdk/anthropic" + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, + "qwen3.6-plus-free": { + "id": "qwen3.6-plus-free", + "name": "Qwen3.6 Plus Free", + "family": "qwen-free", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-03-30", + "last_updated": "2026-03-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 64000 + }, + "status": "deprecated", + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, + "glm-4.6": { + "id": "glm-4.6", + "name": "GLM-4.6", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "status": "deprecated", + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.1 + } + }, + "ling-2.6-flash-free": { + "id": "ling-2.6-flash-free", + "name": "Ling 2.6 Flash Free", + "family": "ling-flash-free", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2025-06", + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262100, + "output": 32800 + }, + "status": "deprecated", + "cost": { + "input": 0, + "output": 0 + } + }, + "gemini-3-pro": { + "id": "gemini-3-pro", + "name": "Gemini 3 Pro", + "family": "gemini-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "status": "deprecated", + "provider": { + "npm": "@ai-sdk/google" + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2, + "tiers": [ + { + "input": 4, + "output": 18, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 18, + "cache_read": 0.4 + } + } + }, + "kimi-k2.6": { + "id": "kimi-k2.6", + "name": "Kimi K2.6", + "family": "kimi", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.95, + "output": 4, + "cache_read": 0.16 + } + }, + "gpt-5-codex": { + "id": "gpt-5-codex", + "name": "GPT-5 Codex", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-09-15", + "last_updated": "2025-09-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai" + }, + "cost": { + "input": 1.07, + "output": 8.5, + "cache_read": 0.107 + } + }, + "grok-code": { + "id": "grok-code", + "name": "Grok Code Fast 1", + "family": "grok", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-08-20", + "last_updated": "2025-08-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 256000 + }, + "status": "deprecated", + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } + }, + "mimo-v2-flash-free": { + "id": "mimo-v2-flash-free", + "name": "MiMo V2 Flash Free", + "family": "mimo-flash-free", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2025-12-16", + "last_updated": "2025-12-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "status": "deprecated", + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, + "gpt-5.3-codex-spark": { + "id": "gpt-5.3-codex-spark", + "name": "GPT-5.3 Codex Spark", + "family": "gpt-codex-spark", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 128000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai" + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } + }, + "hy3-preview-free": { + "id": "hy3-preview-free", + "name": "Hy3 preview Free", + "family": "hy3-free", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-06", + "release_date": "2026-04-20", + "last_updated": "2026-04-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 64000 + }, + "status": "deprecated", + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, + "minimax-m2.5": { + "id": "minimax-m2.5", + "name": "MiniMax M2.5", + "family": "minimax", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06 + } + }, + "kimi-k2": { + "id": "kimi-k2", + "name": "Kimi K2", + "family": "kimi", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "status": "deprecated", + "cost": { + "input": 0.4, + "output": 2.5, + "cache_read": 0.4 + } + }, + "claude-sonnet-4-5": { + "id": "claude-sonnet-4-5", + "name": "Claude Sonnet 4.5", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": true, + "temperature": true, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic" + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75, + "tiers": [ + { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5 + } + } + }, + "qwen3-coder": { + "id": "qwen3-coder", + "name": "Qwen3 Coder", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "status": "deprecated", + "cost": { + "input": 0.45, + "output": 1.8 + } + }, + "gpt-5": { + "id": "gpt-5", + "name": "GPT-5", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai" + }, + "cost": { + "input": 1.07, + "output": 8.5, + "cache_read": 0.107 + } + }, + "qwen3.5-plus": { + "id": "qwen3.5-plus", + "name": "Qwen3.5 Plus", + "family": "qwen3.5", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-02-16", + "last_updated": "2026-02-16", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 65536 + }, + "provider": { + "npm": "@ai-sdk/anthropic" + }, + "cost": { + "input": 0.2, + "output": 1.2, + "cache_read": 0.02, + "cache_write": 0.25 + } + }, + "mimo-v2-pro-free": { + "id": "mimo-v2-pro-free", + "name": "MiMo V2 Pro Free", + "family": "mimo-pro-free", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 64000 + }, + "status": "deprecated", + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, + "nemotron-3-super-free": { + "id": "nemotron-3-super-free", + "name": "Nemotron 3 Super Free", + "family": "nemotron-free", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2026-02", + "release_date": "2026-03-11", + "last_updated": "2026-03-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, + "gpt-5.5-pro": { + "id": "gpt-5.5-pro", + "name": "GPT-5.5 Pro", + "family": "gpt-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": false, + "temperature": false, + "knowledge": "2025-12-01", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai" + }, + "cost": { + "input": 30, + "output": 180, + "cache_read": 30 + } + }, + "kimi-k2-thinking": { + "id": "kimi-k2-thinking", + "name": "Kimi K2 Thinking", + "family": "kimi-thinking", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "status": "deprecated", + "cost": { + "input": 0.4, + "output": 2.5, + "cache_read": 0.4 + } + }, + "gpt-5.1-codex": { + "id": "gpt-5.1-codex", + "name": "GPT-5.1 Codex", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai" + }, + "cost": { + "input": 1.07, + "output": 8.5, + "cache_read": 0.107 + } + }, + "mimo-v2-omni-free": { + "id": "mimo-v2-omni-free", + "name": "MiMo V2 Omni Free", + "family": "mimo-omni-free", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text", "image", "audio", "pdf"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 64000 + }, + "status": "deprecated", + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, + "gpt-5.5": { + "id": "gpt-5.5", + "name": "GPT-5.5", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-12-01", + "release_date": "2026-04-23", + "last_updated": "2026-04-23", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai" + }, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5, + "context_over_200k": { + "input": 10, + "output": 45, + "cache_read": 1 + }, + "tiers": [ + { + "input": 10, + "output": 45, + "cache_read": 1, + "tier": { + "type": "context", + "size": 272000 + } + } + ] + } + }, + "gpt-5.4": { + "id": "gpt-5.4", + "name": "GPT-5.4", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai" + }, + "cost": { + "input": 2.5, + "output": 15, + "cache_read": 0.25, + "context_over_200k": { + "input": 5, + "output": 22.5, + "cache_read": 0.5 + }, + "tiers": [ + { + "input": 5, + "output": 22.5, + "cache_read": 0.5, + "tier": { + "type": "context", + "size": 272000 + } + } + ] + } + }, + "claude-opus-4-6": { + "id": "claude-opus-4-6", + "name": "Claude Opus 4.6", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/anthropic" + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } + } + } + }, + "stepfun": { + "id": "stepfun", + "env": ["STEPFUN_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.stepfun.com/v1", + "name": "StepFun", + "doc": "https://platform.stepfun.com/docs/zh/overview/concept", + "models": { + "step-3.5-flash-2603": { + "id": "step-3.5-flash-2603", + "name": "Step 3.5 Flash 2603", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "input": 256000, + "output": 256000 + }, + "cost": { + "input": 0.1, + "output": 0.3, + "cache_read": 0.02 + } + }, + "step-1-32k": { + "id": "step-1-32k", + "name": "Step 1 (32K)", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-06", + "release_date": "2025-01-01", + "last_updated": "2026-02-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + }, + "cost": { + "input": 2.05, + "output": 9.59, + "cache_read": 0.41 + } + }, + "step-3.5-flash": { + "id": "step-3.5-flash", + "name": "Step 3.5 Flash", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-01-29", + "last_updated": "2026-02-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "input": 256000, + "output": 256000 + }, + "cost": { + "input": 0.096, + "output": 0.288, + "cache_read": 0.019 + } + }, + "step-2-16k": { + "id": "step-2-16k", + "name": "Step 2 (16K)", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-06", + "release_date": "2025-01-01", + "last_updated": "2026-02-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + }, + "cost": { + "input": 5.21, + "output": 16.44, + "cache_read": 1.04 + } + } + } + }, + "nebius": { + "id": "nebius", + "env": ["NEBIUS_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.tokenfactory.nebius.com/v1", + "name": "Nebius Token Factory", + "doc": "https://docs.tokenfactory.nebius.com/", + "models": { + "NousResearch/Hermes-4-70B": { + "id": "NousResearch/Hermes-4-70B", + "name": "Hermes-4-70B", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-11", + "release_date": "2026-01-30", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "input": 120000, + "output": 8192 + }, + "cost": { + "input": 0.13, + "output": 0.4, + "reasoning": 0.4, + "cache_read": 0.013, + "cache_write": 0.16 + } + }, + "NousResearch/Hermes-4-405B": { + "id": "NousResearch/Hermes-4-405B", + "name": "Hermes-4-405B", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-11", + "release_date": "2026-01-30", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "input": 120000, + "output": 8192 + }, + "cost": { + "input": 1, + "output": 3, + "reasoning": 3, + "cache_read": 0.1, + "cache_write": 1.25 + } + }, + "Qwen/Qwen2.5-VL-72B-Instruct": { + "id": "Qwen/Qwen2.5-VL-72B-Instruct", + "name": "Qwen2.5-VL-72B-Instruct", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2025-01-20", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "input": 120000, + "output": 8192 + }, + "cost": { + "input": 0.25, + "output": 0.75, + "cache_read": 0.025, + "cache_write": 0.31 + } + }, + "Qwen/Qwen3.5-397B-A17B": { + "id": "Qwen/Qwen3.5-397B-A17B", + "name": "Qwen3.5-397B-A17B", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-07", + "release_date": "2025-07-15", + "last_updated": "2026-05-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "input": 250000, + "output": 8192 + }, + "cost": { + "input": 0.6, + "output": 3.6, + "cache_read": 0.06, + "cache_write": 0.75 + } + }, + "Qwen/Qwen3-Embedding-8B": { + "id": "Qwen/Qwen3-Embedding-8B", + "name": "Qwen3-Embedding-8B", + "family": "text-embedding", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "temperature": false, + "knowledge": "2025-10", + "release_date": "2026-01-10", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "input": 32768, + "output": 0 + }, + "cost": { + "input": 0.01, + "output": 0 + } + }, + "Qwen/Qwen3-30B-A3B-Instruct-2507": { + "id": "Qwen/Qwen3-30B-A3B-Instruct-2507", + "name": "Qwen3-30B-A3B-Instruct-2507", + "attachment": false, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-12", + "release_date": "2026-01-28", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "input": 120000, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.3, + "cache_read": 0.01, + "cache_write": 0.125 + } + }, + "Qwen/Qwen3-235B-A22B-Instruct-2507": { + "id": "Qwen/Qwen3-235B-A22B-Instruct-2507", + "name": "Qwen3 235B A22B Instruct 2507", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-07", + "release_date": "2025-07-25", + "last_updated": "2025-10-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 8192 + }, + "cost": { + "input": 0.2, + "output": 0.6 + } + }, + "Qwen/Qwen3-32B": { + "id": "Qwen/Qwen3-32B", + "name": "Qwen3-32B", + "attachment": false, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-12", + "release_date": "2026-01-28", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "input": 120000, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.3, + "cache_read": 0.01, + "cache_write": 0.125 + } + }, + "Qwen/Qwen3-235B-A22B-Thinking-2507-fast": { + "id": "Qwen/Qwen3-235B-A22B-Thinking-2507-fast", + "name": "Qwen3-235B-A22B-Thinking-2507-fast", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-07", + "release_date": "2025-07-25", + "last_updated": "2026-05-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8000, + "input": 7000, + "output": 8192 + }, + "cost": { + "input": 0.5, + "output": 2, + "cache_read": 0.05, + "cache_write": 0.625 + } + }, + "Qwen/Qwen3.5-397B-A17B-fast": { + "id": "Qwen/Qwen3.5-397B-A17B-fast", + "name": "Qwen3.5-397B-A17B-fast", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-07", + "release_date": "2025-07-15", + "last_updated": "2026-05-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8000, + "input": 7000, + "output": 8192 + }, + "cost": { + "input": 0.6, + "output": 3.6, + "cache_read": 0.06, + "cache_write": 0.75 + } + }, + "Qwen/Qwen3-Next-80B-A3B-Thinking-fast": { + "id": "Qwen/Qwen3-Next-80B-A3B-Thinking-fast", + "name": "Qwen3-Next-80B-A3B-Thinking-fast", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-07", + "release_date": "2025-07-25", + "last_updated": "2026-05-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8000, + "input": 7000, + "output": 8192 + }, + "cost": { + "input": 0.15, + "output": 1.2, + "cache_read": 0.015, + "cache_write": 0.1875 + } + }, + "Qwen/Qwen3-Next-80B-A3B-Thinking": { + "id": "Qwen/Qwen3-Next-80B-A3B-Thinking", + "name": "Qwen3-Next-80B-A3B-Thinking", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-12", + "release_date": "2026-01-28", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "input": 120000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 1.2, + "reasoning": 1.2, + "cache_read": 0.015, + "cache_write": 0.18 + } + }, + "PrimeIntellect/INTELLECT-3": { + "id": "PrimeIntellect/INTELLECT-3", + "name": "INTELLECT-3", + "attachment": false, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-10", + "release_date": "2026-01-25", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "input": 120000, + "output": 8192 + }, + "cost": { + "input": 0.2, + "output": 1.1, + "cache_read": 0.02, + "cache_write": 0.25 + } + }, + "zai-org/GLM-5": { + "id": "zai-org/GLM-5", + "name": "GLM-5", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "knowledge": "2026-01", + "release_date": "2026-03-01", + "last_updated": "2026-03-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "input": 200000, + "output": 16384 + }, + "cost": { + "input": 1, + "output": 3.2, + "cache_read": 0.1, + "cache_write": 1 + } + }, + "meta-llama/Llama-3.3-70B-Instruct": { + "id": "meta-llama/Llama-3.3-70B-Instruct", + "name": "Llama-3.3-70B-Instruct", + "attachment": false, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-08", + "release_date": "2025-12-05", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "input": 120000, + "output": 8192 + }, + "cost": { + "input": 0.13, + "output": 0.4, + "cache_read": 0.013, + "cache_write": 0.16 + } + }, + "meta-llama/Meta-Llama-3.1-8B-Instruct": { + "id": "meta-llama/Meta-Llama-3.1-8B-Instruct", + "name": "Meta-Llama-3.1-8B-Instruct", + "attachment": false, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2024-07-23", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "input": 120000, + "output": 4096 + }, + "cost": { + "input": 0.02, + "output": 0.06, + "cache_read": 0.002, + "cache_write": 0.025 + } + }, + "nvidia/nemotron-3-super-120b-a12b": { + "id": "nvidia/nemotron-3-super-120b-a12b", + "name": "Nemotron-3-Super-120B-A12B", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2026-02", + "release_date": "2026-03-11", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "input": 256000, + "output": 32768 + }, + "cost": { + "input": 0.3, + "output": 0.9 + } + }, + "nvidia/Llama-3_1-Nemotron-Ultra-253B-v1": { + "id": "nvidia/Llama-3_1-Nemotron-Ultra-253B-v1", + "name": "Llama-3.1-Nemotron-Ultra-253B-v1", + "attachment": false, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2025-01-15", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "input": 120000, + "output": 4096 + }, + "cost": { + "input": 0.6, + "output": 1.8, + "cache_read": 0.06, + "cache_write": 0.75 + } + }, + "nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B": { + "id": "nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B", + "name": "Nemotron-3-Nano-30B-A3B", + "attachment": false, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-05", + "release_date": "2025-08-10", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32000, + "input": 30000, + "output": 4096 + }, + "cost": { + "input": 0.06, + "output": 0.24, + "cache_read": 0.006, + "cache_write": 0.075 + } + }, + "nvidia/Nemotron-3-Nano-Omni": { + "id": "nvidia/Nemotron-3-Nano-Omni", + "name": "Nemotron-3-Nano-Omni", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-01-20", + "last_updated": "2026-05-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 65536, + "input": 60000, + "output": 8192 + }, + "cost": { + "input": 0.06, + "output": 0.24, + "cache_read": 0.006, + "cache_write": 0.075 + } + }, + "deepseek-ai/DeepSeek-V3.2-fast": { + "id": "deepseek-ai/DeepSeek-V3.2-fast", + "name": "DeepSeek-V3.2-fast", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-01-27", + "last_updated": "2026-05-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8000, + "input": 7000, + "output": 8192 + }, + "cost": { + "input": 0.4, + "output": 2, + "cache_read": 0.04, + "cache_write": 0.5 + } + }, + "deepseek-ai/DeepSeek-V3.2": { + "id": "deepseek-ai/DeepSeek-V3.2", + "name": "DeepSeek-V3.2", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-11", + "release_date": "2026-01-20", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163000, + "input": 160000, + "output": 16384 + }, + "cost": { + "input": 0.3, + "output": 0.45, + "reasoning": 0.45, + "cache_read": 0.03, + "cache_write": 0.375 + } + }, + "openai/gpt-oss-120b-fast": { + "id": "openai/gpt-oss-120b-fast", + "name": "gpt-oss-120b-fast", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-06", + "release_date": "2025-06-10", + "last_updated": "2026-05-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8000, + "input": 7000, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.5, + "cache_read": 0.01, + "cache_write": 0.125 + } + }, + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "name": "gpt-oss-120b", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-09", + "release_date": "2026-01-10", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "input": 124000, + "output": 8192 + }, + "cost": { + "input": 0.15, + "output": 0.6, + "reasoning": 0.6, + "cache_read": 0.015, + "cache_write": 0.18 + } + }, + "google/gemma-2-2b-it": { + "id": "google/gemma-2-2b-it", + "name": "Gemma-2-2b-it", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "temperature": true, + "knowledge": "2024-06", + "release_date": "2024-07-31", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "input": 8000, + "output": 4096 + }, + "cost": { + "input": 0.02, + "output": 0.06, + "cache_read": 0.002, + "cache_write": 0.025 + } + }, + "google/gemma-3-27b-it": { + "id": "google/gemma-3-27b-it", + "name": "Gemma-3-27b-it", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-10", + "release_date": "2026-01-20", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 110000, + "input": 100000, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.3, + "cache_read": 0.01, + "cache_write": 0.125 + } + }, + "moonshotai/Kimi-K2.5-fast": { + "id": "moonshotai/Kimi-K2.5-fast", + "name": "Kimi-K2.5-fast", + "family": "kimi", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-06", + "release_date": "2025-12-15", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "input": 256000, + "output": 8192 + }, + "cost": { + "input": 0.5, + "output": 2.5, + "cache_read": 0.05, + "cache_write": 0.625 + } + }, + "moonshotai/Kimi-K2.5": { + "id": "moonshotai/Kimi-K2.5", + "name": "Kimi-K2.5", + "family": "kimi", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-06", + "release_date": "2025-12-15", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "input": 256000, + "output": 8192 + }, + "cost": { + "input": 0.5, + "output": 2.5, + "reasoning": 2.5, + "cache_read": 0.05, + "cache_write": 0.625 + } + }, + "MiniMaxAI/MiniMax-M2.5": { + "id": "MiniMaxAI/MiniMax-M2.5", + "name": "MiniMax-M2.5", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-01-20", + "last_updated": "2026-05-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 196608, + "input": 190000, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03, + "cache_write": 0.375 + } + }, + "MiniMaxAI/MiniMax-M2.5-fast": { + "id": "MiniMaxAI/MiniMax-M2.5-fast", + "name": "MiniMax-M2.5-fast", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-01-20", + "last_updated": "2026-05-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8000, + "input": 7000, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03, + "cache_write": 0.375 + } + }, + "deepseek-ai/DeepSeek-V4-Pro": { + "id": "deepseek-ai/DeepSeek-V4-Pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 1.75, + "output": 3.5, + "cache_read": 0.15 + } + } + } + }, + "poe": { + "id": "poe", + "env": ["POE_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.poe.com/v1", + "name": "Poe", + "doc": "https://creator.poe.com/docs/external-applications/openai-compatible-api", + "models": { + "topazlabs-co/topazlabs": { + "id": "topazlabs-co/topazlabs", + "name": "TopazLabs", + "family": "topazlabs", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2024-12-03", + "last_updated": "2024-12-03", + "modalities": { + "input": ["text"], + "output": ["image"] + }, + "open_weights": false, + "limit": { + "context": 204, + "output": 0 + } + }, + "novita/kimi-k2.5": { + "id": "novita/kimi-k2.5", + "name": "Kimi-K2.5", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 3, + "cache_read": 0.1 + } + }, + "novita/glm-4.7": { + "id": "novita/glm-4.7", + "name": "glm-4.7", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 205000, + "output": 131072 + }, + "status": "deprecated" + }, + "novita/glm-5": { + "id": "novita/glm-5", + "name": "GLM-5", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-15", + "last_updated": "2026-02-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 205000, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3.2, + "cache_read": 0.2 + } + }, + "novita/minimax-m2.1": { + "id": "novita/minimax-m2.1", + "name": "minimax-m2.1", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-12-26", + "last_updated": "2025-12-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 205000, + "output": 131072 + } + }, + "novita/glm-4.6": { + "id": "novita/glm-4.6", + "name": "GLM-4.6", + "family": "glm", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 0, + "output": 0 + } + }, + "novita/kimi-k2.6": { + "id": "novita/kimi-k2.6", + "name": "Kimi-K2.6", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-04-20", + "last_updated": "2026-05-02", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "input": 262144, + "output": 262144 + }, + "cost": { + "input": 0.96, + "output": 4.04, + "cache_read": 0.16 + } + }, + "novita/glm-4.6v": { + "id": "novita/glm-4.6v", + "name": "glm-4.6v", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-12-09", + "last_updated": "2025-12-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 32768 + } + }, + "novita/deepseek-v3.2": { + "id": "novita/deepseek-v3.2", + "name": "DeepSeek-V3.2", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 0 + }, + "cost": { + "input": 0.27, + "output": 0.4, + "cache_read": 0.13 + } + }, + "novita/glm-4.7-flash": { + "id": "novita/glm-4.7-flash", + "name": "glm-4.7-flash", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-01-19", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 65500 + } + }, + "novita/glm-4.7-n": { + "id": "novita/glm-4.7-n", + "name": "glm-4.7-n", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 205000, + "output": 131072 + } + }, + "novita/kimi-k2-thinking": { + "id": "novita/kimi-k2-thinking", + "name": "kimi-k2-thinking", + "family": "kimi", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-11-07", + "last_updated": "2025-11-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 0 + } + }, + "fireworks-ai/kimi-k2.5-fw": { + "id": "fireworks-ai/kimi-k2.5-fw", + "name": "Kimi-K2.5-FW", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "input": 245760, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "empiriolabs/deepseek-v4-pro-el": { + "id": "empiriolabs/deepseek-v4-pro-el", + "name": "DeepSeek-V4-Pro-EL", + "attachment": true, + "reasoning": true, + "tool_call": true, + "release_date": "2026-04-24", + "last_updated": "2026-05-02", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 384000 + }, + "cost": { + "input": 1.67, + "output": 3.33 + } + }, + "empiriolabs/deepseek-v4-flash-el": { + "id": "empiriolabs/deepseek-v4-flash-el", + "name": "DeepSeek-V4-Flash-EL", + "attachment": true, + "reasoning": true, + "tool_call": true, + "release_date": "2026-04-24", + "last_updated": "2026-05-02", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 384000 + }, + "cost": { + "input": 0.14, + "output": 0.28 + } + }, + "elevenlabs/elevenlabs-v2.5-turbo": { + "id": "elevenlabs/elevenlabs-v2.5-turbo", + "name": "ElevenLabs-v2.5-Turbo", + "family": "elevenlabs", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2024-10-28", + "last_updated": "2024-10-28", + "modalities": { + "input": ["text"], + "output": ["audio"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 0 + } + }, + "elevenlabs/elevenlabs-v3": { + "id": "elevenlabs/elevenlabs-v3", + "name": "ElevenLabs-v3", + "family": "elevenlabs", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2025-06-05", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text"], + "output": ["audio"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 0 + } + }, + "elevenlabs/elevenlabs-music": { + "id": "elevenlabs/elevenlabs-music", + "name": "ElevenLabs-Music", + "family": "elevenlabs", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2025-08-29", + "last_updated": "2025-08-29", + "modalities": { + "input": ["text"], + "output": ["audio"] + }, + "open_weights": false, + "limit": { + "context": 2000, + "output": 0 + } + }, + "cerebras/gpt-oss-120b-cs": { + "id": "cerebras/gpt-oss-120b-cs", + "name": "GPT-OSS-120B-CS", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-08-06", + "last_updated": "2025-08-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 0 + }, + "cost": { + "input": 0.35, + "output": 0.75 + } + }, + "cerebras/llama-3.1-8b-cs": { + "id": "cerebras/llama-3.1-8b-cs", + "name": "Llama-3.1-8B-CS", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2025-05-13", + "last_updated": "2025-05-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 0 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } + }, + "cerebras/qwen3-32b-cs": { + "id": "cerebras/qwen3-32b-cs", + "name": "qwen3-32b-cs", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-05-15", + "last_updated": "2025-05-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 0, + "output": 0 + }, + "status": "deprecated" + }, + "cerebras/qwen3-235b-2507-cs": { + "id": "cerebras/qwen3-235b-2507-cs", + "name": "qwen3-235b-2507-cs", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-08-06", + "last_updated": "2025-08-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 0, + "output": 0 + }, + "status": "deprecated" + }, + "cerebras/llama-3.3-70b-cs": { + "id": "cerebras/llama-3.3-70b-cs", + "name": "llama-3.3-70b-cs", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2025-05-13", + "last_updated": "2025-05-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 0, + "output": 0 + }, + "status": "deprecated" + }, + "stabilityai/stablediffusionxl": { + "id": "stabilityai/stablediffusionxl", + "name": "StableDiffusionXL", + "family": "stable-diffusion", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2023-07-09", + "last_updated": "2023-07-09", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, + "open_weights": false, + "limit": { + "context": 200, + "output": 0 + } + }, + "xai/grok-code-fast-1": { + "id": "xai/grok-code-fast-1", + "name": "Grok Code Fast 1", + "family": "grok", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-08-22", + "last_updated": "2025-08-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 128000 + }, + "cost": { + "input": 0.2, + "output": 1.5, + "cache_read": 0.02 + } + }, + "xai/grok-4-fast-reasoning": { + "id": "xai/grok-4-fast-reasoning", + "name": "Grok-4-Fast-Reasoning", + "family": "grok", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-09-16", + "last_updated": "2025-09-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 128000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } + }, + "xai/grok-4.1-fast-non-reasoning": { + "id": "xai/grok-4.1-fast-non-reasoning", + "name": "Grok-4.1-Fast-Non-Reasoning", + "family": "grok", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2025-11-19", + "last_updated": "2025-11-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 30000 + } + }, + "xai/grok-4": { + "id": "xai/grok-4", + "name": "Grok-4", + "family": "grok", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-07-10", + "last_updated": "2025-07-10", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 128000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75 + } + }, + "xai/grok-3-mini": { + "id": "xai/grok-3-mini", + "name": "Grok 3 Mini", + "family": "grok", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-04-11", + "last_updated": "2025-04-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 0.5, + "cache_read": 0.075 + } + }, + "xai/grok-4.20-multi-agent": { + "id": "xai/grok-4.20-multi-agent", + "name": "Grok-4.20-Multi-Agent", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2026-03-13", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 0 + }, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.2 + } + }, + "xai/grok-3": { + "id": "xai/grok-3", + "name": "Grok 3", + "family": "grok", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2025-04-11", + "last_updated": "2025-04-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75 + } + }, + "xai/grok-4-fast-non-reasoning": { + "id": "xai/grok-4-fast-non-reasoning", + "name": "Grok-4-Fast-Non-Reasoning", + "family": "grok", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2025-09-16", + "last_updated": "2025-09-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 128000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } + }, + "xai/grok-4.1-fast-reasoning": { + "id": "xai/grok-4.1-fast-reasoning", + "name": "Grok-4.1-Fast-Reasoning", + "family": "grok", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-11-19", + "last_updated": "2025-11-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 30000 + } + }, + "runwayml/runway": { + "id": "runwayml/runway", + "name": "Runway", + "family": "runway", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2024-10-11", + "last_updated": "2024-10-11", + "modalities": { + "input": ["text", "image"], + "output": ["video"] + }, + "open_weights": false, + "limit": { + "context": 256, + "output": 0 + } + }, + "runwayml/runway-gen-4-turbo": { + "id": "runwayml/runway-gen-4-turbo", + "name": "Runway-Gen-4-Turbo", + "family": "runway", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2025-05-09", + "last_updated": "2025-05-09", + "modalities": { + "input": ["text", "image"], + "output": ["video"] + }, + "open_weights": false, + "limit": { + "context": 256, + "output": 0 + } + }, + "openai/gpt-5.1-codex-max": { + "id": "openai/gpt-5.1-codex-max", + "name": "GPT-5.1-Codex-Max", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-12-08", + "last_updated": "2025-12-08", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.1, + "output": 9, + "cache_read": 0.11 + } + }, + "openai/sora-2-pro": { + "id": "openai/sora-2-pro", + "name": "Sora-2-Pro", + "family": "sora", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2025-10-06", + "last_updated": "2025-10-06", + "modalities": { + "input": ["text", "image"], + "output": ["video"] + }, + "open_weights": false, + "limit": { + "context": 0, + "output": 0 + } + }, + "openai/chatgpt-4o-latest": { + "id": "openai/chatgpt-4o-latest", + "name": "ChatGPT-4o-Latest", + "family": "gpt", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2024-08-14", + "last_updated": "2024-08-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 8192 + }, + "status": "deprecated", + "cost": { + "input": 4.5, + "output": 14 + } + }, + "openai/gpt-5-chat": { + "id": "openai/gpt-5-chat", + "name": "GPT-5-Chat", + "family": "gpt-codex", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.1, + "output": 9, + "cache_read": 0.11 + } + }, + "openai/gpt-5.2-pro": { + "id": "openai/gpt-5.2-pro", + "name": "GPT-5.2-Pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 19, + "output": 150 + } + }, + "openai/gpt-4o-aug": { + "id": "openai/gpt-4o-aug", + "name": "GPT-4o-Aug", + "family": "gpt", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2024-11-21", + "last_updated": "2024-11-21", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 2.2, + "output": 9, + "cache_read": 1.1 + } + }, + "openai/gpt-image-2": { + "id": "openai/gpt-image-2", + "name": "GPT-Image-2", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, + "open_weights": false, + "limit": { + "context": 0, + "output": 0 + }, + "cost": { + "input": 5.0505, + "output": 32.3232, + "cache_read": 1.2626 + } + }, + "openai/gpt-4-classic-0314": { + "id": "openai/gpt-4-classic-0314", + "name": "GPT-4-Classic-0314", + "family": "gpt", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2024-08-26", + "last_updated": "2024-08-26", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8192, + "output": 4096 + }, + "status": "deprecated", + "cost": { + "input": 27, + "output": 54 + } + }, + "openai/gpt-5-mini": { + "id": "openai/gpt-5-mini", + "name": "GPT-5-mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-06-25", + "last_updated": "2025-06-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.22, + "output": 1.8, + "cache_read": 0.022 + } + }, + "openai/gpt-5-nano": { + "id": "openai/gpt-5-nano", + "name": "GPT-5-nano", + "family": "gpt-nano", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.045, + "output": 0.36, + "cache_read": 0.0045 + } + }, + "openai/gpt-5.3-codex": { + "id": "openai/gpt-5.3-codex", + "name": "GPT-5.3-Codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-02-10", + "last_updated": "2026-02-10", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.6, + "output": 13, + "cache_read": 0.16 + } + }, + "openai/gpt-4-turbo": { + "id": "openai/gpt-4-turbo", + "name": "GPT-4-Turbo", + "family": "gpt", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2023-09-13", + "last_updated": "2023-09-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 9, + "output": 27 + } + }, + "openai/gpt-5.2": { + "id": "openai/gpt-5.2", + "name": "GPT-5.2", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-12-08", + "last_updated": "2025-12-08", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.6, + "output": 13, + "cache_read": 0.16 + } + }, + "openai/o3-pro": { + "id": "openai/o3-pro", + "name": "o3-pro", + "family": "o-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-06-10", + "last_updated": "2025-06-10", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 18, + "output": 72 + } + }, + "openai/o3-mini-high": { + "id": "openai/o3-mini-high", + "name": "o3-mini-high", + "family": "o-mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-01-31", + "last_updated": "2025-01-31", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 0.99, + "output": 4 + } + }, + "openai/gpt-4o-mini": { + "id": "openai/gpt-4o-mini", + "name": "GPT-4o-mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 124096, + "output": 4096 + }, + "cost": { + "input": 0.14, + "output": 0.54, + "cache_read": 0.068 + } + }, + "openai/o4-mini-deep-research": { + "id": "openai/o4-mini-deep-research", + "name": "o4-mini-deep-research", + "family": "o-mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-06-27", + "last_updated": "2025-06-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.8, + "output": 7.2, + "cache_read": 0.45 + } + }, + "openai/gpt-5.4-mini": { + "id": "openai/gpt-5.4-mini", + "name": "GPT-5.4-Mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-03-12", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.68, + "output": 4, + "cache_read": 0.068 + } + }, + "openai/dall-e-3": { + "id": "openai/dall-e-3", + "name": "DALL-E-3", + "family": "dall-e", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2023-11-06", + "last_updated": "2023-11-06", + "modalities": { + "input": ["text"], + "output": ["image"] + }, + "open_weights": false, + "limit": { + "context": 800, + "output": 0 + } + }, + "openai/o4-mini": { + "id": "openai/o4-mini", + "name": "o4-mini", + "family": "o-mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 0.99, + "output": 4, + "cache_read": 0.25 + } + }, + "openai/gpt-5.4-nano": { + "id": "openai/gpt-5.4-nano", + "name": "GPT-5.4-Nano", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-03-11", + "last_updated": "2026-03-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.18, + "output": 1.1, + "cache_read": 0.018 + } + }, + "openai/gpt-image-1": { + "id": "openai/gpt-image-1", + "name": "GPT-Image-1", + "family": "gpt", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2025-03-31", + "last_updated": "2025-03-31", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 0 + } + }, + "openai/gpt-5.2-codex": { + "id": "openai/gpt-5.2-codex", + "name": "GPT-5.2-Codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-01-14", + "last_updated": "2026-01-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.6, + "output": 13, + "cache_read": 0.16 + } + }, + "openai/gpt-5.1-codex-mini": { + "id": "openai/gpt-5.1-codex-mini", + "name": "GPT-5.1-Codex-Mini", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-11-12", + "last_updated": "2025-11-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.22, + "output": 1.8, + "cache_read": 0.022 + } + }, + "openai/gpt-5.1": { + "id": "openai/gpt-5.1", + "name": "GPT-5.1", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-11-12", + "last_updated": "2025-11-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.025 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.1, + "output": 9, + "cache_read": 0.11 + } }, - "google/gemma-3-12b-it:free": { - "id": "google/gemma-3-12b-it:free", - "name": "Gemma 3 12B (free)", - "family": "gemma", + "openai/gpt-image-1-mini": { + "id": "openai/gpt-image-1-mini", + "name": "GPT-Image-1-Mini", + "family": "gpt", "attachment": true, "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-03-13", - "last_updated": "2025-03-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 32768, "output": 8192 } - }, - "google/gemini-2.5-flash-lite-preview-09-2025": { - "id": "google/gemini-2.5-flash-lite-preview-09-2025", - "name": "Gemini 2.5 Flash Lite Preview 09-25", - "family": "gemini-flash-lite", - "attachment": true, - "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-08-26", + "last_updated": "2025-08-26", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.025 }, - "limit": { "context": 1048576, "output": 65536 } - }, - "google/gemma-2-9b-it": { - "id": "google/gemma-2-9b-it", - "name": "Gemma 2 9B", - "family": "gemma", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-06", - "release_date": "2024-06-28", - "last_updated": "2024-06-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.03, "output": 0.09 }, - "limit": { "context": 8192, "output": 8192 } + "limit": { + "context": 0, + "output": 0 + } }, - "google/gemini-3.1-pro-preview": { - "id": "google/gemini-3.1-pro-preview", - "name": "Gemini 3.1 Pro Preview", - "family": "gemini-pro", + "openai/o1": { + "id": "openai/o1", + "name": "o1", + "family": "o", "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_details" }, - "structured_output": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-02-19", - "last_updated": "2026-02-19", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "temperature": false, + "release_date": "2024-12-18", + "last_updated": "2024-12-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { - "input": 2, - "output": 12, - "reasoning": 12, - "context_over_200k": { "input": 4, "output": 18, "cache_read": 0.4 } + "limit": { + "context": 200000, + "output": 100000 }, - "limit": { "context": 1048576, "output": 65536 } + "cost": { + "input": 14, + "output": 54 + } }, - "google/gemini-3-pro-preview": { - "id": "google/gemini-3-pro-preview", - "name": "Gemini 3 Pro Preview", - "family": "gemini-pro", + "openai/gpt-5.4-pro": { + "id": "openai/gpt-5.4-pro", + "name": "GPT-5.4-Pro", "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_details" }, - "structured_output": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-11-18", - "last_updated": "2025-11", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "temperature": false, + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 2, "output": 12 }, - "limit": { "context": 1050000, "output": 66000 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 27, + "output": 160 + } }, - "google/gemma-3-27b-it": { - "id": "google/gemma-3-27b-it", - "name": "Gemma 3 27B", - "family": "gemma", + "openai/gpt-3.5-turbo": { + "id": "openai/gpt-3.5-turbo", + "name": "GPT-3.5-Turbo", + "family": "gpt", "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-03-12", - "last_updated": "2025-03-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.04, "output": 0.15 }, - "limit": { "context": 96000, "output": 96000 } - }, - "google/gemma-3-4b-it": { - "id": "google/gemma-3-4b-it", - "name": "Gemma 3 4B", - "family": "gemma", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-03-13", - "last_updated": "2025-03-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.01703, "output": 0.06815 }, - "limit": { "context": 96000, "output": 96000 } - }, - "google/gemma-3n-e4b-it": { - "id": "google/gemma-3n-e4b-it", - "name": "Gemma 3n 4B", - "family": "gemma", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-06", - "release_date": "2025-05-20", - "last_updated": "2025-05-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.02, "output": 0.04 }, - "limit": { "context": 32768, "output": 32768 } + "temperature": false, + "release_date": "2023-09-13", + "last_updated": "2023-09-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16384, + "output": 2048 + }, + "cost": { + "input": 0.45, + "output": 1.4 + } }, - "google/gemini-2.5-pro-preview-06-05": { - "id": "google/gemini-2.5-pro-preview-06-05", - "name": "Gemini 2.5 Pro Preview 06-05", - "family": "gemini-pro", + "openai/o3-deep-research": { + "id": "openai/o3-deep-research", + "name": "o3-deep-research", + "family": "o", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-05", - "last_updated": "2025-06-05", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-06-27", + "last_updated": "2025-06-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.31 }, - "limit": { "context": 1048576, "output": 65536 } - }, - "google/gemma-3-4b-it:free": { - "id": "google/gemma-3-4b-it:free", - "name": "Gemma 3 4B (free)", - "family": "gemma", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-03-13", - "last_updated": "2025-03-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 32768, "output": 8192 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 9, + "output": 36, + "cache_read": 2.2 + } }, - "google/gemini-2.5-pro-preview-05-06": { - "id": "google/gemini-2.5-pro-preview-05-06", - "name": "Gemini 2.5 Pro Preview 05-06", - "family": "gemini-pro", + "openai/o3-mini": { + "id": "openai/o3-mini", + "name": "o3-mini", + "family": "o-mini", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-05-06", - "last_updated": "2025-05-06", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-01-31", + "last_updated": "2025-01-31", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.31 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 0.99, + "output": 4 + } }, - "google/gemini-2.0-flash-001": { - "id": "google/gemini-2.0-flash-001", - "name": "Gemini 2.0 Flash", - "family": "gemini-flash", + "openai/o1-pro": { + "id": "openai/o1-pro", + "name": "o1-pro", + "family": "o-pro", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2024-06", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-03-19", + "last_updated": "2025-03-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.025 }, - "limit": { "context": 1048576, "output": 8192 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 140, + "output": 540 + } }, - "google/gemma-3-27b-it:free": { - "id": "google/gemma-3-27b-it:free", - "name": "Gemma 3 27B (free)", - "family": "gemma", + "openai/gpt-4o-search": { + "id": "openai/gpt-4o-search", + "name": "GPT-4o-Search", + "family": "gpt", "attachment": true, "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-03-12", - "last_updated": "2025-03-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 8192 } + "temperature": false, + "release_date": "2025-03-11", + "last_updated": "2025-03-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 2.2, + "output": 9 + } }, - "google/gemini-3-flash-preview": { - "id": "google/gemini-3-flash-preview", - "name": "Gemini 3 Flash Preview", - "family": "gemini-flash", + "openai/gpt-5-codex": { + "id": "openai/gpt-5-codex", + "name": "GPT-5-Codex", + "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_details" }, - "structured_output": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-09-23", + "last_updated": "2025-09-23", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 3, "cache_read": 0.05 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.1, + "output": 9 + } }, - "google/gemini-2.5-pro": { - "id": "google/gemini-2.5-pro", - "name": "Gemini 2.5 Pro", - "family": "gemini-pro", + "openai/gpt-5.4": { + "id": "openai/gpt-5.4", + "name": "GPT-5.4", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-03-20", - "last_updated": "2025-06-05", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "temperature": false, + "release_date": "2026-02-26", + "last_updated": "2026-02-26", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.31 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 2.2, + "output": 14, + "cache_read": 0.22 + } }, - "google/gemini-2.5-flash": { - "id": "google/gemini-2.5-flash", - "name": "Gemini 2.5 Flash", - "family": "gemini-flash", + "openai/gpt-5.3-codex-spark": { + "id": "openai/gpt-5.3-codex-spark", + "name": "GPT-5.3-Codex-Spark", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-07-17", - "last_updated": "2025-07-17", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "temperature": false, + "release_date": "2026-03-04", + "last_updated": "2026-03-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 2.5, "cache_read": 0.0375 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "google/gemini-3.1-pro-preview-customtools": { - "id": "google/gemini-3.1-pro-preview-customtools", - "name": "Gemini 3.1 Pro Preview Custom Tools", - "family": "gemini-pro", + "openai/gpt-3.5-turbo-raw": { + "id": "openai/gpt-3.5-turbo-raw", + "name": "GPT-3.5-Turbo-Raw", + "family": "gpt", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_details" }, - "structured_output": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-02-19", - "last_updated": "2026-02-19", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "temperature": false, + "release_date": "2023-09-27", + "last_updated": "2023-09-27", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, + "limit": { + "context": 4524, + "output": 2048 + }, "cost": { - "input": 2, - "output": 12, - "reasoning": 12, - "context_over_200k": { "input": 4, "output": 18, "cache_read": 0.4 } + "input": 0.45, + "output": 1.4 + } + }, + "openai/gpt-4.1-nano": { + "id": "openai/gpt-4.1-nano", + "name": "GPT-4.1-nano", + "family": "gpt-nano", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2025-04-15", + "last_updated": "2025-04-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] }, - "limit": { "context": 1048576, "output": 65536 } + "open_weights": false, + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.09, + "output": 0.36, + "cache_read": 0.022 + } }, - "google/gemini-2.5-flash-preview-09-2025": { - "id": "google/gemini-2.5-flash-preview-09-2025", - "name": "Gemini 2.5 Flash Preview 09-25", - "family": "gemini-flash", + "openai/o3": { + "id": "openai/o3", + "name": "o3", + "family": "o", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 2.5, "cache_read": 0.031 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.8, + "output": 7.2, + "cache_read": 0.45 + } }, - "google/gemini-3.1-flash-lite-preview": { - "id": "google/gemini-3.1-flash-lite-preview", - "name": "Gemini 3.1 Flash Lite Preview", - "family": "gemini-flash-lite", + "openai/gpt-5-pro": { + "id": "openai/gpt-5-pro", + "name": "GPT-5-Pro", + "family": "gpt-pro", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-03-03", - "last_updated": "2026-03-03", - "modalities": { "input": ["text", "image", "video", "pdf", "audio"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-10-06", + "last_updated": "2025-10-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { - "input": 0.25, - "output": 1.5, - "reasoning": 1.5, - "cache_read": 0.025, - "cache_write": 0.083, - "input_audio": 0.5, - "output_audio": 0.5 + "limit": { + "context": 400000, + "output": 128000 }, - "limit": { "context": 1048576, "output": 65536 } + "cost": { + "input": 14, + "output": 110 + } }, - "google/gemma-3-12b-it": { - "id": "google/gemma-3-12b-it", - "name": "Gemma 3 12B", - "family": "gemma", + "openai/sora-2": { + "id": "openai/sora-2", + "name": "Sora-2", + "family": "sora", "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-03-13", - "last_updated": "2025-03-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.03, "output": 0.1 }, - "limit": { "context": 131072, "output": 131072 } + "tool_call": true, + "temperature": false, + "release_date": "2025-10-06", + "last_updated": "2025-10-06", + "modalities": { + "input": ["text", "image"], + "output": ["video"] + }, + "open_weights": false, + "limit": { + "context": 0, + "output": 0 + } }, - "google/gemma-3n-e2b-it:free": { - "id": "google/gemma-3n-e2b-it:free", - "name": "Gemma 3n 2B (free)", - "family": "gemma", + "openai/gpt-4o": { + "id": "openai/gpt-4o", + "name": "GPT-4o", + "family": "gpt", "attachment": true, "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-06", - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 8192, "output": 2000 } + "tool_call": true, + "temperature": false, + "release_date": "2024-05-13", + "last_updated": "2024-05-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 8192 + } }, - "google/gemma-3n-e4b-it:free": { - "id": "google/gemma-3n-e4b-it:free", - "name": "Gemma 3n 4B (free)", - "family": "gemma", + "openai/gpt-5": { + "id": "openai/gpt-5", + "name": "GPT-5", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.1, + "output": 9, + "cache_read": 0.11 + } + }, + "openai/gpt-5.2-instant": { + "id": "openai/gpt-5.2-instant", + "name": "GPT-5.2-Instant", "attachment": true, "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-06", - "release_date": "2025-05-20", - "last_updated": "2025-05-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 8192, "output": 2000 } + "tool_call": true, + "temperature": false, + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.6, + "output": 13, + "cache_read": 0.16 + } }, - "meta-llama/llama-3.2-11b-vision-instruct": { - "id": "meta-llama/llama-3.2-11b-vision-instruct", - "name": "Llama 3.2 11B Vision Instruct", - "family": "llama", + "openai/gpt-4o-mini-search": { + "id": "openai/gpt-4o-mini-search", + "name": "GPT-4o-mini-Search", + "family": "gpt-mini", "attachment": true, "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 8192 } + "tool_call": true, + "temperature": false, + "release_date": "2025-03-11", + "last_updated": "2025-03-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.14, + "output": 0.54 + } }, - "meta-llama/llama-3.2-3b-instruct:free": { - "id": "meta-llama/llama-3.2-3b-instruct:free", - "name": "Llama 3.2 3B Instruct (free)", - "family": "llama", + "openai/gpt-image-1.5": { + "id": "openai/gpt-image-1.5", + "name": "gpt-image-1.5", "attachment": true, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 131072 } + "temperature": false, + "release_date": "2025-12-16", + "last_updated": "2025-12-16", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 0 + } }, - "meta-llama/llama-3.3-70b-instruct:free": { - "id": "meta-llama/llama-3.3-70b-instruct:free", - "name": "Llama 3.3 70B Instruct (free)", - "family": "llama", - "attachment": false, + "openai/gpt-3.5-turbo-instruct": { + "id": "openai/gpt-3.5-turbo-instruct", + "name": "GPT-3.5-Turbo-Instruct", + "family": "gpt", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2024-12", - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 131072 } + "temperature": false, + "release_date": "2023-09-20", + "last_updated": "2023-09-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 3500, + "output": 1024 + }, + "cost": { + "input": 1.4, + "output": 1.8 + } }, - "openrouter/free": { - "id": "openrouter/free", - "name": "Free Models Router", + "openai/gpt-4.1": { + "id": "openai/gpt-4.1", + "name": "GPT-4.1", + "family": "gpt", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 1.8, + "output": 7.2, + "cache_read": 0.45 + } + }, + "openai/gpt-5.1-instant": { + "id": "openai/gpt-5.1-instant", + "name": "GPT-5.1-Instant", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-02-01", - "last_updated": "2026-02-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-11-12", + "last_updated": "2025-11-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 200000, "input": 200000, "output": 8000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.1, + "output": 9, + "cache_read": 0.11 + } }, - "arcee-ai/trinity-mini:free": { - "id": "arcee-ai/trinity-mini:free", - "name": "Trinity Mini", - "family": "trinity-mini", - "attachment": false, + "openai/gpt-4.1-mini": { + "id": "openai/gpt-4.1-mini", + "name": "GPT-4.1-mini", + "family": "gpt-mini", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-06", - "release_date": "2026-01-28", - "last_updated": "2026-01-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 131072 } + "temperature": false, + "release_date": "2025-04-15", + "last_updated": "2025-04-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.36, + "output": 1.4, + "cache_read": 0.09 + } }, - "arcee-ai/trinity-large-preview:free": { - "id": "arcee-ai/trinity-large-preview:free", - "name": "Trinity Large Preview", - "family": "trinity", - "attachment": false, + "openai/gpt-4-classic": { + "id": "openai/gpt-4-classic", + "name": "GPT-4-Classic", + "family": "gpt", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-06", - "release_date": "2026-01-28", - "last_updated": "2026-01-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 131072 } + "temperature": false, + "release_date": "2024-03-25", + "last_updated": "2024-03-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8192, + "output": 4096 + }, + "status": "deprecated", + "cost": { + "input": 27, + "output": 54 + } }, - "nousresearch/hermes-4-70b": { - "id": "nousresearch/hermes-4-70b", - "name": "Hermes 4 70B", - "family": "hermes", - "attachment": false, + "openai/gpt-5.1-codex": { + "id": "openai/gpt-5.1-codex", + "name": "GPT-5.1-Codex", + "family": "gpt-codex", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2025-08-25", - "last_updated": "2025-08-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.13, "output": 0.4 }, - "limit": { "context": 131072, "output": 131072 } + "temperature": false, + "release_date": "2025-11-12", + "last_updated": "2025-11-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.1, + "output": 9, + "cache_read": 0.11 + } }, - "nousresearch/hermes-4-405b": { - "id": "nousresearch/hermes-4-405b", - "name": "Hermes 4 405B", - "family": "hermes", - "attachment": false, - "reasoning": true, + "openai/gpt-5.3-instant": { + "id": "openai/gpt-5.3-instant", + "name": "GPT-5.3-Instant", + "attachment": true, + "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2025-08-25", - "last_updated": "2025-08-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1, "output": 3 }, - "limit": { "context": 131072, "output": 131072 } + "temperature": false, + "release_date": "2026-03-03", + "last_updated": "2026-03-03", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 111616, + "output": 16384 + }, + "cost": { + "input": 1.6, + "output": 13, + "cache_read": 0.16 + } }, - "nousresearch/hermes-3-llama-3.1-405b:free": { - "id": "nousresearch/hermes-3-llama-3.1-405b:free", - "name": "Hermes 3 405B Instruct (free)", - "family": "hermes", - "attachment": false, - "reasoning": true, - "tool_call": false, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-08-16", - "last_updated": "2024-08-16", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 131072 } + "google/veo-3-fast": { + "id": "google/veo-3-fast", + "name": "Veo-3-Fast", + "family": "veo", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": false, + "release_date": "2025-10-13", + "last_updated": "2025-10-13", + "modalities": { + "input": ["text"], + "output": ["video"] + }, + "open_weights": false, + "limit": { + "context": 480, + "output": 0 + } }, - "minimax/minimax-01": { - "id": "minimax/minimax-01", - "name": "MiniMax-01", - "family": "minimax", + "google/veo-3.1-fast": { + "id": "google/veo-3.1-fast", + "name": "Veo-3.1-Fast", + "family": "veo", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "temperature": true, - "release_date": "2025-01-15", - "last_updated": "2025-01-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 1.1 }, - "limit": { "context": 1000000, "output": 1000000 } + "temperature": false, + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image"], + "output": ["video"] + }, + "open_weights": false, + "limit": { + "context": 480, + "output": 0 + } }, - "minimax/minimax-m2": { - "id": "minimax/minimax-m2", - "name": "MiniMax M2", - "family": "minimax", - "attachment": false, + "google/gemini-3.1-pro": { + "id": "google/gemini-3.1-pro", + "name": "Gemini-3.1-Pro", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_details" }, - "structured_output": true, - "temperature": true, - "release_date": "2025-10-23", - "last_updated": "2025-10-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.28, "output": 1.15, "cache_read": 0.28, "cache_write": 1.15 }, - "limit": { "context": 196600, "output": 118000 } + "temperature": false, + "release_date": "2026-02-19", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2 + } }, - "minimax/minimax-m2.1": { - "id": "minimax/minimax-m2.1", - "name": "MiniMax M2.1", - "family": "minimax", - "attachment": false, - "reasoning": true, + "google/imagen-3-fast": { + "id": "google/imagen-3-fast", + "name": "Imagen-3-Fast", + "family": "imagen", + "attachment": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_details" }, - "structured_output": true, - "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 204800, "output": 131072 } + "temperature": false, + "release_date": "2024-10-17", + "last_updated": "2024-10-17", + "modalities": { + "input": ["text"], + "output": ["image"] + }, + "open_weights": false, + "limit": { + "context": 480, + "output": 0 + } }, - "minimax/minimax-m2.7": { - "id": "minimax/minimax-m2.7", - "name": "MiniMax M2.7", - "family": "minimax", - "attachment": false, - "reasoning": true, + "google/gemini-2.0-flash": { + "id": "google/gemini-2.0-flash", + "name": "Gemini-2.0-Flash", + "family": "gemini-flash", + "attachment": true, + "reasoning": false, "tool_call": true, - "temperature": true, - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.06, "cache_write": 0.375 }, - "limit": { "context": 204800, "output": 131072 } + "temperature": false, + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 990000, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.42 + } }, - "minimax/minimax-m2.5": { - "id": "minimax/minimax-m2.5", - "name": "MiniMax M2.5", - "family": "minimax", - "attachment": false, + "google/gemini-deep-research": { + "id": "google/gemini-deep-research", + "name": "gemini-deep-research", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_details" }, - "structured_output": true, - "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.03 }, - "limit": { "context": 204800, "output": 131072 } + "temperature": false, + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 0 + }, + "status": "deprecated", + "cost": { + "input": 1.6, + "output": 9.6 + } }, - "minimax/minimax-m1": { - "id": "minimax/minimax-m1", - "name": "MiniMax M1", - "family": "minimax", - "attachment": false, + "google/gemini-2.5-pro": { + "id": "google/gemini-2.5-pro", + "name": "Gemini-2.5-Pro", + "family": "gemini-pro", + "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.4, "output": 2.2 }, - "limit": { "context": 1000000, "output": 40000 } + "temperature": false, + "release_date": "2025-02-05", + "last_updated": "2025-02-05", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1065535, + "output": 65535 + }, + "cost": { + "input": 0.87, + "output": 7, + "cache_read": 0.087 + } }, - "qwen/qwen3-coder-30b-a3b-instruct": { - "id": "qwen/qwen3-coder-30b-a3b-instruct", - "name": "Qwen3 Coder 30B A3B Instruct", - "family": "qwen", - "attachment": false, + "google/imagen-3": { + "id": "google/imagen-3", + "name": "Imagen-3", + "family": "imagen", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-31", - "last_updated": "2025-07-31", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.07, "output": 0.27 }, - "limit": { "context": 160000, "output": 65536 } + "temperature": false, + "release_date": "2024-10-15", + "last_updated": "2024-10-15", + "modalities": { + "input": ["text"], + "output": ["image"] + }, + "open_weights": false, + "limit": { + "context": 480, + "output": 0 + } }, - "qwen/qwen3-235b-a22b-07-25": { - "id": "qwen/qwen3-235b-a22b-07-25", - "name": "Qwen3 235B A22B Instruct 2507", - "family": "qwen", - "attachment": false, + "google/nano-banana": { + "id": "google/nano-banana", + "name": "Nano-Banana", + "family": "nano-banana", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04-28", - "last_updated": "2025-07-21", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.85 }, - "limit": { "context": 262144, "output": 131072 } + "temperature": false, + "release_date": "2025-08-21", + "last_updated": "2025-08-21", + "modalities": { + "input": ["text", "image"], + "output": ["text", "image"] + }, + "open_weights": false, + "limit": { + "context": 65536, + "output": 0 + }, + "cost": { + "input": 0.21, + "output": 1.8, + "cache_read": 0.021 + } }, - "qwen/qwen3-30b-a3b-thinking-2507": { - "id": "qwen/qwen3-30b-a3b-thinking-2507", - "name": "Qwen3 30B A3B Thinking 2507", - "family": "qwen", - "attachment": false, + "google/gemini-2.5-flash": { + "id": "google/gemini-2.5-flash", + "name": "Gemini-2.5-Flash", + "family": "gemini-flash", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-29", - "last_updated": "2025-07-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 262000, "output": 262000 } + "temperature": false, + "release_date": "2025-04-26", + "last_updated": "2025-04-26", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1065535, + "output": 65535 + }, + "cost": { + "input": 0.21, + "output": 1.8, + "cache_read": 0.021 + } }, - "qwen/qwen3.5-plus-02-15": { - "id": "qwen/qwen3.5-plus-02-15", - "name": "Qwen3.5 Plus 2026-02-15", - "family": "qwen", + "google/gemini-3.1-flash-lite": { + "id": "google/gemini-3.1-flash-lite", + "name": "Gemini-3.1-Flash-Lite", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-02-16", - "last_updated": "2026-02-16", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "temperature": false, + "release_date": "2026-02-18", + "last_updated": "2026-02-18", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 2.4 }, - "limit": { "context": 1000000, "output": 65536 } - }, - "qwen/qwen3-coder": { - "id": "qwen/qwen3-coder", - "name": "Qwen3 Coder", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 262144, "output": 66536 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.25, + "output": 1.5 + } }, - "qwen/qwen3-4b:free": { - "id": "qwen/qwen3-4b:free", - "name": "Qwen3 4B (free)", - "family": "qwen", - "attachment": false, + "google/gemini-3-flash": { + "id": "google/gemini-3-flash", + "name": "Gemini-3-Flash", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04-30", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 40960, "output": 40960 } - }, - "qwen/qwen3-coder:free": { - "id": "qwen/qwen3-coder:free", - "name": "Qwen3 Coder 480B A35B Instruct (free)", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 66536 } - }, - "qwen/qwen2.5-vl-72b-instruct": { - "id": "qwen/qwen2.5-vl-72b-instruct", - "name": "Qwen2.5 VL 72B Instruct", - "family": "qwen", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-02-01", - "last_updated": "2025-02-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 32768, "output": 8192 } + "temperature": false, + "release_date": "2025-10-07", + "last_updated": "2025-10-07", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.4, + "output": 2.4, + "cache_read": 0.04 + } }, - "qwen/qwen3-next-80b-a3b-thinking": { - "id": "qwen/qwen3-next-80b-a3b-thinking", - "name": "Qwen3 Next 80B A3B Thinking", - "family": "qwen", - "attachment": false, - "reasoning": true, + "google/veo-3.1": { + "id": "google/veo-3.1", + "name": "Veo-3.1", + "family": "veo", + "attachment": true, + "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-11", - "last_updated": "2025-09-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.14, "output": 1.4 }, - "limit": { "context": 262144, "output": 262144 } + "temperature": false, + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text"], + "output": ["video"] + }, + "open_weights": false, + "limit": { + "context": 480, + "output": 0 + } }, - "qwen/qwen3-235b-a22b-thinking-2507": { - "id": "qwen/qwen3-235b-a22b-thinking-2507", - "name": "Qwen3 235B A22B Thinking 2507", - "family": "qwen", - "attachment": false, - "reasoning": true, + "google/lyria": { + "id": "google/lyria", + "name": "Lyria", + "family": "lyria", + "attachment": true, + "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-25", - "last_updated": "2025-07-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.078, "output": 0.312 }, - "limit": { "context": 262144, "output": 81920 } + "temperature": false, + "release_date": "2025-06-04", + "last_updated": "2025-06-04", + "modalities": { + "input": ["text"], + "output": ["audio"] + }, + "open_weights": false, + "limit": { + "context": 0, + "output": 0 + } }, - "qwen/qwen3-next-80b-a3b-instruct:free": { - "id": "qwen/qwen3-next-80b-a3b-instruct:free", - "name": "Qwen3 Next 80B A3B Instruct (free)", - "family": "qwen", - "attachment": false, + "google/imagen-4-ultra": { + "id": "google/imagen-4-ultra", + "name": "Imagen-4-Ultra", + "family": "imagen", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-11", - "last_updated": "2025-09-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262144, "output": 262144 } + "temperature": false, + "release_date": "2025-05-24", + "last_updated": "2025-05-24", + "modalities": { + "input": ["text"], + "output": ["image"] + }, + "open_weights": false, + "limit": { + "context": 480, + "output": 0 + } }, - "qwen/qwen3-next-80b-a3b-instruct": { - "id": "qwen/qwen3-next-80b-a3b-instruct", - "name": "Qwen3 Next 80B A3B Instruct", - "family": "qwen", - "attachment": false, + "google/nano-banana-pro": { + "id": "google/nano-banana-pro", + "name": "Nano-Banana-Pro", + "family": "nano-banana", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-11", - "last_updated": "2025-09-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.14, "output": 1.4 }, - "limit": { "context": 262144, "output": 262144 } + "temperature": false, + "release_date": "2025-11-19", + "last_updated": "2025-11-19", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, + "open_weights": false, + "limit": { + "context": 65536, + "output": 0 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2 + } }, - "qwen/qwen3.6-plus-preview:free": { - "id": "qwen/qwen3.6-plus-preview:free", - "name": "Qwen3.6 Plus Preview (free)", - "family": "qwen", - "attachment": false, + "google/gemini-3-pro": { + "id": "google/gemini-3-pro", + "name": "Gemini-3-Pro", + "family": "gemini-pro", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-03-30", - "last_updated": "2026-03-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-10-22", + "last_updated": "2025-10-22", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 1000000, "output": 65536 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "status": "deprecated", + "cost": { + "input": 1.6, + "output": 9.6, + "cache_read": 0.16 + } }, - "qwen/qwen3-coder:exacto": { - "id": "qwen/qwen3-coder:exacto", - "name": "Qwen3 Coder (exacto)", - "family": "qwen", - "attachment": false, + "google/imagen-4-fast": { + "id": "google/imagen-4-fast", + "name": "Imagen-4-Fast", + "family": "imagen", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.38, "output": 1.53 }, - "limit": { "context": 131072, "output": 32768 } + "temperature": false, + "release_date": "2025-06-25", + "last_updated": "2025-06-25", + "modalities": { + "input": ["text"], + "output": ["image"] + }, + "open_weights": false, + "limit": { + "context": 480, + "output": 0 + } }, - "qwen/qwen3.5-397b-a17b": { - "id": "qwen/qwen3.5-397b-a17b", - "name": "Qwen3.5 397B A17B", - "family": "qwen", + "google/veo-3": { + "id": "google/veo-3", + "name": "Veo-3", + "family": "veo", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-02-16", - "last_updated": "2026-02-16", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 3.6 }, - "limit": { "context": 262144, "output": 65536 } + "temperature": false, + "release_date": "2025-05-21", + "last_updated": "2025-05-21", + "modalities": { + "input": ["text"], + "output": ["video"] + }, + "open_weights": false, + "limit": { + "context": 480, + "output": 0 + } }, - "qwen/qwen3-max": { - "id": "qwen/qwen3-max", - "name": "Qwen3 Max", - "family": "qwen", - "attachment": false, + "google/gemini-2.5-flash-lite": { + "id": "google/gemini-2.5-flash-lite", + "name": "Gemini-2.5-Flash-Lite", + "family": "gemini-flash-lite", + "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-06-19", + "last_updated": "2025-06-19", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.2, "output": 6 }, - "limit": { "context": 262144, "output": 32768 } + "limit": { + "context": 1024000, + "output": 64000 + }, + "cost": { + "input": 0.07, + "output": 0.28 + } }, - "qwen/qwen3-coder-flash": { - "id": "qwen/qwen3-coder-flash", - "name": "Qwen3 Coder Flash", - "family": "qwen", - "attachment": false, + "google/imagen-4": { + "id": "google/imagen-4", + "name": "Imagen-4", + "family": "imagen", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": false, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.5 }, - "limit": { "context": 128000, "output": 66536 } + "limit": { + "context": 480, + "output": 0 + } }, - "qwen/qwen-2.5-coder-32b-instruct": { - "id": "qwen/qwen-2.5-coder-32b-instruct", - "name": "Qwen2.5 Coder 32B Instruct", - "family": "qwen", - "attachment": false, + "google/gemma-4-31b": { + "id": "google/gemma-4-31b", + "name": "Gemma-4-31B", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-11-11", - "last_updated": "2024-11-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 32768, "output": 8192 } + "tool_call": true, + "temperature": false, + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "qwen/qwen3-30b-a3b-instruct-2507": { - "id": "qwen/qwen3-30b-a3b-instruct-2507", - "name": "Qwen3 30B A3B Instruct 2507", - "family": "qwen", - "attachment": false, + "google/gemini-2.0-flash-lite": { + "id": "google/gemini-2.0-flash-lite", + "name": "Gemini-2.0-Flash-Lite", + "family": "gemini-flash-lite", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-29", - "last_updated": "2025-07-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 262000, "output": 262000 } + "temperature": false, + "release_date": "2025-02-05", + "last_updated": "2025-02-05", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 990000, + "output": 8192 + }, + "cost": { + "input": 0.052, + "output": 0.21 + } }, - "xiaomi/mimo-v2-pro": { - "id": "xiaomi/mimo-v2-pro", - "name": "MiMo-V2-Pro", - "family": "mimo", - "attachment": false, - "reasoning": true, + "google/veo-2": { + "id": "google/veo-2", + "name": "Veo-2", + "family": "veo", + "attachment": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_details" }, - "structured_output": true, - "temperature": true, - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1, "output": 3 }, - "limit": { "context": 1048576, "output": 65536 } + "temperature": false, + "release_date": "2024-12-02", + "last_updated": "2024-12-02", + "modalities": { + "input": ["text"], + "output": ["video"] + }, + "open_weights": false, + "limit": { + "context": 480, + "output": 0 + } }, - "xiaomi/mimo-v2-omni": { - "id": "xiaomi/mimo-v2-omni", - "name": "MiMo-V2-Omni", - "family": "mimo", + "lumalabs/ray2": { + "id": "lumalabs/ray2", + "name": "Ray2", + "family": "ray", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_details" }, - "structured_output": true, - "temperature": true, - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text", "image", "video", "audio"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 262144, "output": 65536 } + "temperature": false, + "release_date": "2025-02-20", + "last_updated": "2025-02-20", + "modalities": { + "input": ["text", "image"], + "output": ["video"] + }, + "open_weights": false, + "limit": { + "context": 5000, + "output": 0 + } }, - "xiaomi/mimo-v2-flash": { - "id": "xiaomi/mimo-v2-flash", - "name": "MiMo-V2-Flash", - "family": "mimo", - "attachment": false, + "anthropic/claude-opus-4.1": { + "id": "anthropic/claude-opus-4.1", + "name": "Claude-Opus-4.1", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-12-14", - "last_updated": "2025-12-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.3, "cache_read": 0.01 }, - "limit": { "context": 262144, "output": 65536 } - }, - "black-forest-labs/flux.2-pro": { - "id": "black-forest-labs/flux.2-pro", - "name": "FLUX.2 Pro", - "family": "flux", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-11-25", - "last_updated": "2026-01-31", - "modalities": { "input": ["image", "text"], "output": ["image"] }, + "temperature": false, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 46864, "output": 46864 } + "limit": { + "context": 196608, + "output": 32000 + }, + "cost": { + "input": 13, + "output": 64, + "cache_read": 1.3, + "cache_write": 16 + } }, - "black-forest-labs/flux.2-max": { - "id": "black-forest-labs/flux.2-max", - "name": "FLUX.2 Max", - "family": "flux", - "attachment": false, + "anthropic/claude-sonnet-3.5": { + "id": "anthropic/claude-sonnet-3.5", + "name": "Claude-Sonnet-3.5", + "family": "claude-sonnet", + "attachment": true, "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-12-16", - "last_updated": "2026-01-31", - "modalities": { "input": ["image", "text"], "output": ["image"] }, + "tool_call": true, + "temperature": false, + "release_date": "2024-06-05", + "last_updated": "2024-06-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 46864, "output": 46864 } + "limit": { + "context": 189096, + "output": 8192 + }, + "status": "deprecated", + "cost": { + "input": 2.6, + "output": 13, + "cache_read": 0.26, + "cache_write": 3.2 + } }, - "black-forest-labs/flux.2-klein-4b": { - "id": "black-forest-labs/flux.2-klein-4b", - "name": "FLUX.2 Klein 4B", - "family": "flux", - "attachment": false, + "anthropic/claude-haiku-3": { + "id": "anthropic/claude-haiku-3", + "name": "Claude-Haiku-3", + "family": "claude-haiku", + "attachment": true, "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2025-06", - "release_date": "2026-01-14", - "last_updated": "2026-01-31", - "modalities": { "input": ["image", "text"], "output": ["image"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 40960, "output": 40960 } + "tool_call": true, + "temperature": false, + "release_date": "2024-03-09", + "last_updated": "2024-03-09", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 189096, + "output": 8192 + }, + "cost": { + "input": 0.21, + "output": 1.1, + "cache_read": 0.021, + "cache_write": 0.26 + } }, - "black-forest-labs/flux.2-flex": { - "id": "black-forest-labs/flux.2-flex", - "name": "FLUX.2 Flex", - "family": "flux", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-11-25", - "last_updated": "2026-01-31", - "modalities": { "input": ["image", "text"], "output": ["image"] }, + "anthropic/claude-opus-4.6": { + "id": "anthropic/claude-opus-4.6", + "name": "Claude-Opus-4.6", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-02-04", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 67344, "output": 67344 } + "limit": { + "context": 983040, + "output": 128000 + }, + "cost": { + "input": 4.3, + "output": 21, + "cache_read": 0.43, + "cache_write": 5.3 + } }, - "stepfun/step-3.5-flash": { - "id": "stepfun/step-3.5-flash", - "name": "Step 3.5 Flash", - "family": "step", - "attachment": false, + "anthropic/claude-opus-4.7": { + "id": "anthropic/claude-opus-4.7", + "name": "Claude-Opus-4.7", + "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-01-29", - "last_updated": "2026-01-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.3, "cache_read": 0.02 }, - "limit": { "context": 256000, "output": 256000 } + "temperature": false, + "release_date": "2026-04-15", + "last_updated": "2026-04-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 128000 + }, + "cost": { + "input": 4.3, + "output": 21, + "cache_read": 0.43, + "cache_write": 5.4 + } }, - "stepfun/step-3.5-flash:free": { - "id": "stepfun/step-3.5-flash:free", - "name": "Step 3.5 Flash (free)", - "family": "step", - "attachment": false, + "anthropic/claude-sonnet-4": { + "id": "anthropic/claude-sonnet-4", + "name": "Claude-Sonnet-4", + "family": "claude-sonnet", + "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-01-29", - "last_updated": "2026-01-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 256000 } + "temperature": false, + "release_date": "2025-05-21", + "last_updated": "2025-05-21", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 983040, + "output": 64000 + }, + "cost": { + "input": 2.6, + "output": 13, + "cache_read": 0.26, + "cache_write": 3.2 + } }, - "liquid/lfm-2.5-1.2b-instruct:free": { - "id": "liquid/lfm-2.5-1.2b-instruct:free", - "name": "LFM2.5-1.2B-Instruct (free)", - "family": "liquid", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2025-06", - "release_date": "2026-01-20", - "last_updated": "2026-01-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 32768 } + "anthropic/claude-sonnet-4.5": { + "id": "anthropic/claude-sonnet-4.5", + "name": "Claude-Sonnet-4.5", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2025-09-26", + "last_updated": "2025-09-26", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 983040, + "output": 32768 + }, + "cost": { + "input": 2.6, + "output": 13, + "cache_read": 0.26, + "cache_write": 3.2 + } }, - "liquid/lfm-2.5-1.2b-thinking:free": { - "id": "liquid/lfm-2.5-1.2b-thinking:free", - "name": "LFM2.5-1.2B-Thinking (free)", - "family": "liquid", - "attachment": false, + "anthropic/claude-opus-4.5": { + "id": "anthropic/claude-opus-4.5", + "name": "Claude-Opus-4.5", + "family": "claude-opus", + "attachment": true, "reasoning": true, - "tool_call": false, - "temperature": true, - "knowledge": "2025-06", - "release_date": "2026-01-20", - "last_updated": "2026-01-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 32768 } + "tool_call": true, + "temperature": false, + "release_date": "2025-11-21", + "last_updated": "2025-11-21", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 196608, + "output": 64000 + }, + "cost": { + "input": 4.3, + "output": 21, + "cache_read": 0.43, + "cache_write": 5.3 + } }, - "moonshotai/kimi-k2-thinking": { - "id": "moonshotai/kimi-k2-thinking", - "name": "Kimi K2 Thinking", - "family": "kimi-thinking", - "attachment": false, + "anthropic/claude-sonnet-3.7": { + "id": "anthropic/claude-sonnet-3.7", + "name": "Claude-Sonnet-3.7", + "family": "claude-sonnet", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_details" }, - "structured_output": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-11-06", - "last_updated": "2025-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.5, "cache_read": 0.15 }, - "limit": { "context": 262144, "output": 262144 } + "temperature": false, + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 196608, + "output": 128000 + }, + "cost": { + "input": 2.6, + "output": 13, + "cache_read": 0.26, + "cache_write": 3.2 + } }, - "moonshotai/kimi-k2.5": { - "id": "moonshotai/kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi", + "anthropic/claude-opus-4": { + "id": "anthropic/claude-opus-4", + "name": "Claude-Opus-4", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_details" }, - "structured_output": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 3, "cache_read": 0.1 }, - "limit": { "context": 262144, "output": 262144 } + "temperature": false, + "release_date": "2025-05-21", + "last_updated": "2025-05-21", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 192512, + "output": 28672 + }, + "cost": { + "input": 13, + "output": 64, + "cache_read": 1.3, + "cache_write": 16 + } }, - "moonshotai/kimi-k2-0905": { - "id": "moonshotai/kimi-k2-0905", - "name": "Kimi K2 Instruct 0905", - "family": "kimi", - "attachment": false, + "anthropic/claude-haiku-3.5": { + "id": "anthropic/claude-haiku-3.5", + "name": "Claude-Haiku-3.5", + "family": "claude-haiku", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.5 }, - "limit": { "context": 262144, "output": 16384 } + "temperature": false, + "release_date": "2024-10-01", + "last_updated": "2024-10-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 189096, + "output": 8192 + }, + "cost": { + "input": 0.68, + "output": 3.4, + "cache_read": 0.068, + "cache_write": 0.85 + } }, - "moonshotai/kimi-k2": { - "id": "moonshotai/kimi-k2", - "name": "Kimi K2", - "family": "kimi", - "attachment": false, - "reasoning": false, + "anthropic/claude-haiku-4.5": { + "id": "anthropic/claude-haiku-4.5", + "name": "Claude-Haiku-4.5", + "family": "claude-haiku", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-07-11", - "last_updated": "2025-07-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.55, "output": 2.2 }, - "limit": { "context": 131072, "output": 32768 } + "temperature": false, + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 192000, + "output": 64000 + }, + "cost": { + "input": 0.85, + "output": 4.3, + "cache_read": 0.085, + "cache_write": 1.1 + } }, - "moonshotai/kimi-k2-0905:exacto": { - "id": "moonshotai/kimi-k2-0905:exacto", - "name": "Kimi K2 Instruct 0905 (exacto)", - "family": "kimi", - "attachment": false, + "anthropic/claude-sonnet-3.5-june": { + "id": "anthropic/claude-sonnet-3.5-june", + "name": "Claude-Sonnet-3.5-June", + "family": "claude-sonnet", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.5 }, - "limit": { "context": 262144, "output": 16384 } + "temperature": false, + "release_date": "2024-11-18", + "last_updated": "2024-11-18", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 189096, + "output": 8192 + }, + "status": "deprecated", + "cost": { + "input": 2.6, + "output": 13, + "cache_read": 0.26, + "cache_write": 3.2 + } }, - "moonshotai/kimi-k2:free": { - "id": "moonshotai/kimi-k2:free", - "name": "Kimi K2 (free)", - "family": "kimi", - "attachment": false, - "reasoning": false, + "anthropic/claude-sonnet-4.6": { + "id": "anthropic/claude-sonnet-4.6", + "name": "Claude-Sonnet-4.6", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-11", - "last_updated": "2025-07-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 32800, "output": 32800 } + "temperature": false, + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 983040, + "output": 128000 + }, + "cost": { + "input": 2.6, + "output": 13, + "cache_read": 0.26, + "cache_write": 3.2 + } }, - "bytedance-seed/seedream-4.5": { - "id": "bytedance-seed/seedream-4.5", - "name": "Seedream 4.5", - "family": "seed", - "attachment": false, + "ideogramai/ideogram": { + "id": "ideogramai/ideogram", + "name": "Ideogram", + "family": "ideogram", + "attachment": true, "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-12-23", - "last_updated": "2026-01-31", - "modalities": { "input": ["image", "text"], "output": ["image"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 4096, "output": 4096 } - }, - "inception/mercury-2": { - "id": "inception/mercury-2", - "name": "Mercury 2", - "family": "mercury", - "attachment": false, - "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-03-04", - "last_updated": "2026-03-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2024-04-03", + "last_updated": "2024-04-03", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 0.75, "cache_read": 0.025 }, - "limit": { "context": 128000, "output": 50000 } + "limit": { + "context": 150, + "output": 0 + } }, - "inception/mercury-coder": { - "id": "inception/mercury-coder", - "name": "Mercury Coder", - "family": "mercury", - "attachment": false, + "ideogramai/ideogram-v2": { + "id": "ideogramai/ideogram-v2", + "name": "Ideogram-v2", + "family": "ideogram", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-04-30", - "last_updated": "2025-04-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2024-08-21", + "last_updated": "2024-08-21", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 0.75, "cache_read": 0.025 }, - "limit": { "context": 128000, "output": 32000 } + "limit": { + "context": 150, + "output": 0 + } }, - "inception/mercury": { - "id": "inception/mercury", - "name": "Mercury", - "family": "mercury", - "attachment": false, + "ideogramai/ideogram-v2a-turbo": { + "id": "ideogramai/ideogram-v2a-turbo", + "name": "Ideogram-v2a-Turbo", + "family": "ideogram", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-06-26", - "last_updated": "2025-06-26", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-02-27", + "last_updated": "2025-02-27", + "modalities": { + "input": ["text"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 0.75, "cache_read": 0.025 }, - "limit": { "context": 128000, "output": 32000 } + "limit": { + "context": 150, + "output": 0 + } }, - "mistralai/mistral-medium-3.1": { - "id": "mistralai/mistral-medium-3.1", - "name": "Mistral Medium 3.1", - "family": "mistral-medium", + "ideogramai/ideogram-v2a": { + "id": "ideogramai/ideogram-v2a", + "name": "Ideogram-v2a", + "family": "ideogram", "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-05", - "release_date": "2025-08-12", - "last_updated": "2025-08-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-02-27", + "last_updated": "2025-02-27", + "modalities": { + "input": ["text"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 150, + "output": 0 + } }, - "mistralai/devstral-small-2505": { - "id": "mistralai/devstral-small-2505", - "name": "Devstral Small", - "family": "devstral", - "attachment": false, + "trytako/tako": { + "id": "trytako/tako", + "name": "Tako", + "family": "tako", + "attachment": true, "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2025-05", - "release_date": "2025-05-07", - "last_updated": "2025-05-07", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.06, "output": 0.12 }, - "limit": { "context": 128000, "output": 128000 } + "temperature": false, + "release_date": "2024-08-15", + "last_updated": "2024-08-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2048, + "output": 0 + } }, - "mistralai/mistral-small-2603": { - "id": "mistralai/mistral-small-2603", - "name": "Mistral Small 4", - "family": "mistral-small", + "poetools/claude-code": { + "id": "poetools/claude-code", + "name": "claude-code", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-06", - "release_date": "2026-03-16", - "last_updated": "2026-03-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 262144, "output": 262144 } + "temperature": false, + "release_date": "2025-11-27", + "last_updated": "2025-11-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 0, + "output": 0 + } }, - "mistralai/mistral-medium-3": { - "id": "mistralai/mistral-medium-3", - "name": "Mistral Medium 3", - "family": "mistral-medium", + "openai/gpt-5.5": { + "id": "openai/gpt-5.5", + "name": "GPT-5.5", + "family": "gpt", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "knowledge": "2025-05", - "release_date": "2025-05-07", - "last_updated": "2025-05-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-12-01", + "release_date": "2026-04-08", + "last_updated": "2026-04-08", + "modalities": { + "input": ["text", "image"], + "output": ["text", "image"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 4.5455, + "output": 27.2727, + "cache_read": 0.4545 + } }, - "mistralai/devstral-2512": { - "id": "mistralai/devstral-2512", - "name": "Devstral 2 2512", - "family": "devstral", - "attachment": false, - "reasoning": false, + "openai/gpt-5.5-pro": { + "id": "openai/gpt-5.5-pro", + "name": "GPT-5.5-Pro", + "family": "gpt-pro", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-12", - "release_date": "2025-09-12", - "last_updated": "2025-09-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 262144, "output": 262144 } - }, - "mistralai/codestral-2508": { - "id": "mistralai/codestral-2508", - "name": "Codestral 2508", - "family": "codestral", + "structured_output": false, + "temperature": false, + "knowledge": "2025-12-01", + "release_date": "2026-04-08", + "last_updated": "2026-04-08", + "modalities": { + "input": ["text", "image"], + "output": ["text", "image"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 27.2727, + "output": 163.6364 + } + } + } + }, + "helicone": { + "id": "helicone", + "env": ["HELICONE_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://ai-gateway.helicone.ai/v1", + "name": "Helicone", + "doc": "https://helicone.ai/models", + "models": { + "mistral-nemo": { + "id": "mistral-nemo", + "name": "Mistral Nemo", + "family": "mistral-nemo", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, + "tool_call": false, "temperature": true, - "knowledge": "2025-05", - "release_date": "2025-08-01", - "last_updated": "2025-08-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 0.9 }, - "limit": { "context": 256000, "output": 256000 } + "knowledge": "2024-07", + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16400 + }, + "cost": { + "input": 20, + "output": 40 + } }, - "mistralai/devstral-medium-2507": { - "id": "mistralai/devstral-medium-2507", - "name": "Devstral Medium", - "family": "devstral", + "grok-4-1-fast-reasoning": { + "id": "grok-4-1-fast-reasoning", + "name": "xAI Grok 4.1 Fast Reasoning", + "family": "grok", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-05", - "release_date": "2025-07-10", - "last_updated": "2025-07-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 131072, "output": 131072 } + "knowledge": "2025-11", + "release_date": "2025-11-17", + "last_updated": "2025-11-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 2000000 + }, + "cost": { + "input": 0.19999999999999998, + "output": 0.5, + "cache_read": 0.049999999999999996 + } }, - "mistralai/devstral-small-2507": { - "id": "mistralai/devstral-small-2507", - "name": "Devstral Small 1.1", - "family": "devstral", + "gemma2-9b-it": { + "id": "gemma2-9b-it", + "name": "Google Gemma 2", + "family": "gemma", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-05", - "release_date": "2025-07-10", - "last_updated": "2025-07-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 131072, "output": 131072 } - }, - "mistralai/mistral-small-3.2-24b-instruct": { - "id": "mistralai/mistral-small-3.2-24b-instruct", - "name": "Mistral Small 3.2 24B Instruct", - "family": "mistral-small", - "attachment": true, - "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-06-20", - "last_updated": "2025-06-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 96000, "output": 8192 } - }, - "mistralai/mistral-small-3.1-24b-instruct": { - "id": "mistralai/mistral-small-3.1-24b-instruct", - "name": "Mistral Small 3.1 24B Instruct", - "family": "mistral-small", - "attachment": true, - "reasoning": false, - "tool_call": true, - "structured_output": true, + "tool_call": false, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-03-17", - "last_updated": "2025-03-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 8192 } - } - } - }, - "zenmux": { - "id": "zenmux", - "env": ["ZENMUX_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://zenmux.ai/api/v1", - "name": "ZenMux", - "doc": "https://docs.zenmux.ai", - "models": { - "openai/gpt-5.2-codex": { - "id": "openai/gpt-5.2-codex", - "name": "GPT-5.2-Codex", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2025-01-01", - "release_date": "2026-01-15", - "last_updated": "2026-01-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2024-06", + "release_date": "2024-06-25", + "last_updated": "2024-06-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.17 }, - "limit": { "context": 400000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/openai", "api": "https://zenmux.ai/api/v1" } + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 0.01, + "output": 0.03 + } }, - "openai/gpt-5.1-codex-mini": { - "id": "openai/gpt-5.1-codex-mini", - "name": "GPT-5.1-Codex-Mini", - "attachment": true, - "reasoning": true, + "llama-3.3-70b-instruct": { + "id": "llama-3.3-70b-instruct", + "name": "Meta Llama 3.3 70B Instruct", + "family": "llama", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.03 }, - "limit": { "context": 400000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/openai", "api": "https://zenmux.ai/api/v1" } + "limit": { + "context": 128000, + "output": 16400 + }, + "cost": { + "input": 0.13, + "output": 0.39 + } }, - "openai/gpt-5.4-pro": { - "id": "openai/gpt-5.4-pro", - "name": "GPT-5.4 Pro", - "attachment": true, - "reasoning": true, + "llama-4-scout": { + "id": "llama-4-scout", + "name": "Meta Llama 4 Scout 17B 16E", + "family": "llama", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-08-31", - "release_date": "2026-03-20", - "last_updated": "2026-03-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 45, "output": 225 }, - "limit": { "context": 1050000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai", "api": "https://zenmux.ai/api/v1" } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.08, + "output": 0.3 + } }, - "openai/gpt-5.4-mini": { - "id": "openai/gpt-5.4-mini", - "name": "GPT-5.4 Mini", - "attachment": true, + "chatgpt-4o-latest": { + "id": "chatgpt-4o-latest", + "name": "OpenAI ChatGPT-4o", + "family": "gpt", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-08-31", - "release_date": "2026-03-20", - "last_updated": "2026-03-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-08", + "release_date": "2024-08-14", + "last_updated": "2024-08-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.75, "output": 4.5 }, - "limit": { "context": 400000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai", "api": "https://zenmux.ai/api/v1" } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 5, + "output": 20, + "cache_read": 2.5 + } }, - "openai/gpt-5": { - "id": "openai/gpt-5", - "name": "GPT-5", - "attachment": true, - "reasoning": true, + "claude-3.5-sonnet-v2": { + "id": "claude-3.5-sonnet-v2", + "name": "Anthropic: Claude 3.5 Sonnet v2", + "family": "claude-sonnet", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.12 }, - "limit": { "context": 400000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/openai", "api": "https://zenmux.ai/api/v1" } + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.30000000000000004, + "cache_write": 3.75 + } }, - "openai/gpt-5.3-codex": { - "id": "openai/gpt-5.3-codex", - "name": "GPT-5.3 Codex", - "attachment": true, - "reasoning": true, + "hermes-2-pro-llama-3-8b": { + "id": "hermes-2-pro-llama-3-8b", + "name": "Hermes 2 Pro Llama 3 8B", + "family": "llama", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-08-31", - "release_date": "2026-03-20", - "last_updated": "2026-03-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-05", + "release_date": "2024-05-27", + "last_updated": "2024-05-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14 }, - "limit": { "context": 400000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai", "api": "https://zenmux.ai/api/v1" } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.14, + "output": 0.14 + } }, - "openai/gpt-5.1-chat": { - "id": "openai/gpt-5.1-chat", - "name": "GPT-5.1 Chat", - "attachment": true, + "claude-3.7-sonnet": { + "id": "claude-3.7-sonnet", + "name": "Anthropic: Claude 3.7 Sonnet", + "family": "claude-sonnet", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["pdf", "image", "text"], "output": ["text"] }, + "knowledge": "2025-02", + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.12 }, - "limit": { "context": 128000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/openai", "api": "https://zenmux.ai/api/v1" } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.30000000000000004, + "cache_write": 3.75 + } }, - "openai/gpt-5.4": { - "id": "openai/gpt-5.4", - "name": "GPT-5.4", - "attachment": true, - "reasoning": true, - "tool_call": true, + "llama-prompt-guard-2-22m": { + "id": "llama-prompt-guard-2-22m", + "name": "Meta Llama Prompt Guard 2 22M", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2025-08-31", - "release_date": "2026-03-20", - "last_updated": "2026-03-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2024-10-01", + "last_updated": "2024-10-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3.75, "output": 18.75 }, - "limit": { "context": 1050000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai", "api": "https://zenmux.ai/api/v1" } + "limit": { + "context": 512, + "output": 2 + }, + "cost": { + "input": 0.01, + "output": 0.01 + } }, - "openai/gpt-5.4-nano": { - "id": "openai/gpt-5.4-nano", - "name": "GPT-5.4 Nano", + "o1-mini": { + "id": "o1-mini", + "name": "OpenAI: o1-mini", + "family": "o-mini", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-08-31", - "release_date": "2026-03-20", - "last_updated": "2026-03-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 1.25 }, - "limit": { "context": 400000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai", "api": "https://zenmux.ai/api/v1" } + "limit": { + "context": 128000, + "output": 65536 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.55 + } }, - "openai/gpt-5.3-chat": { - "id": "openai/gpt-5.3-chat", - "name": "GPT-5.3 Chat", - "attachment": true, + "gpt-4.1-mini-2025-04-14": { + "id": "gpt-4.1-mini-2025-04-14", + "name": "OpenAI GPT-4.1 Mini", + "family": "gpt-mini", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-08-31", - "release_date": "2026-03-20", - "last_updated": "2026-03-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14 }, - "limit": { "context": 128000, "output": 16380 }, - "provider": { "npm": "@ai-sdk/openai", "api": "https://zenmux.ai/api/v1" } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.39999999999999997, + "output": 1.5999999999999999, + "cache_read": 0.09999999999999999 + } }, - "openai/gpt-5.2": { - "id": "openai/gpt-5.2", - "name": "GPT-5.2", - "attachment": true, + "deepseek-r1-distill-llama-70b": { + "id": "deepseek-r1-distill-llama-70b", + "name": "DeepSeek R1 Distill Llama 70B", + "family": "deepseek-thinking", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2025-01-01", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["image", "text", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.17 }, - "limit": { "context": 400000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/openai", "api": "https://zenmux.ai/api/v1" } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.03, + "output": 0.13 + } }, - "openai/gpt-5.1": { - "id": "openai/gpt-5.1", - "name": "GPT-5.1", - "attachment": true, + "qwen3-32b": { + "id": "qwen3-32b", + "name": "Qwen3 32B", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["image", "text", "pdf"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04-28", + "last_updated": "2025-04-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.12 }, - "limit": { "context": 400000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/openai", "api": "https://zenmux.ai/api/v1" } + "limit": { + "context": 131072, + "output": 40960 + }, + "cost": { + "input": 0.29, + "output": 0.59 + } }, - "openai/gpt-5-codex": { - "id": "openai/gpt-5-codex", - "name": "GPT-5 Codex", - "attachment": true, - "reasoning": true, + "llama-3.3-70b-versatile": { + "id": "llama-3.3-70b-versatile", + "name": "Meta Llama 3.3 70B Versatile", + "family": "llama", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-09-23", - "last_updated": "2025-09-23", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.12 }, - "limit": { "context": 400000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/openai", "api": "https://zenmux.ai/api/v1" } + "limit": { + "context": 131072, + "output": 32678 + }, + "cost": { + "input": 0.59, + "output": 0.7899999999999999 + } }, - "openai/gpt-5.1-codex": { - "id": "openai/gpt-5.1-codex", - "name": "GPT-5.1-Codex", - "attachment": true, - "reasoning": true, + "gpt-5-mini": { + "id": "gpt-5-mini", + "name": "OpenAI GPT-5 Mini", + "family": "gpt-mini", + "attachment": false, + "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.12 }, - "limit": { "context": 400000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/openai", "api": "https://zenmux.ai/api/v1" } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.024999999999999998 + } }, - "openai/gpt-5.2-pro": { - "id": "openai/gpt-5.2-pro", - "name": "GPT-5.2-Pro", - "attachment": true, - "reasoning": true, + "gpt-5-nano": { + "id": "gpt-5-nano", + "name": "OpenAI GPT-5 Nano", + "family": "gpt-nano", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 21, "output": 168 }, - "limit": { "context": 400000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai", "api": "https://zenmux.ai/api/v1" } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.049999999999999996, + "output": 0.39999999999999997, + "cache_read": 0.005 + } }, - "x-ai/grok-4-fast": { - "id": "x-ai/grok-4-fast", - "name": "Grok 4 Fast", - "attachment": true, + "gemini-3-pro-preview": { + "id": "gemini-3-pro-preview", + "name": "Google Gemini 3 Pro Preview", + "family": "gemini-pro", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-09-19", - "last_updated": "2025-09-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-11", + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 64000 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.19999999999999998 + } }, - "x-ai/grok-4.1-fast": { - "id": "x-ai/grok-4.1-fast", - "name": "Grok 4.1 Fast", - "attachment": true, - "reasoning": true, + "claude-3-haiku-20240307": { + "id": "claude-3-haiku-20240307", + "name": "Anthropic: Claude 3 Haiku", + "family": "claude-haiku", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-11-20", - "last_updated": "2025-11-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-03", + "release_date": "2024-03-07", + "last_updated": "2024-03-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 64000 } + "limit": { + "context": 200000, + "output": 4096 + }, + "cost": { + "input": 0.25, + "output": 1.25, + "cache_read": 0.03, + "cache_write": 0.3 + } }, - "x-ai/grok-4": { - "id": "x-ai/grok-4", - "name": "Grok 4", - "attachment": true, - "reasoning": true, + "llama-4-maverick": { + "id": "llama-4-maverick", + "name": "Meta Llama 4 Maverick 17B 128E", + "family": "llama", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.75 }, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "x-ai/grok-code-fast-1": { - "id": "x-ai/grok-code-fast-1", - "name": "Grok Code Fast 1", + "claude-sonnet-4-5-20250929": { + "id": "claude-sonnet-4-5-20250929", + "name": "Anthropic: Claude Sonnet 4.5 (20250929)", + "family": "claude-sonnet", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-08-26", - "last_updated": "2025-08-26", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-09", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 1.5, "cache_read": 0.02 }, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.30000000000000004, + "cache_write": 3.75 + } }, - "x-ai/grok-4.1-fast-non-reasoning": { - "id": "x-ai/grok-4.1-fast-non-reasoning", - "name": "Grok 4.1 Fast Non Reasoning", - "attachment": true, - "reasoning": false, + "gemini-2.5-pro": { + "id": "gemini-2.5-pro", + "name": "Google Gemini 2.5 Pro", + "family": "gemini-pro", + "attachment": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-11-20", - "last_updated": "2025-11-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-06", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 64000 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.3125, + "cache_write": 1.25 + } }, - "x-ai/grok-4.2-fast": { - "id": "x-ai/grok-4.2-fast", - "name": "Grok 4.2 Fast", - "attachment": true, + "claude-4.5-opus": { + "id": "claude-4.5-opus", + "name": "Anthropic: Claude Opus 4.5", + "family": "claude-opus", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-08-31", - "release_date": "2026-03-20", - "last_updated": "2026-03-20", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2025-11", + "release_date": "2025-11-24", + "last_updated": "2025-11-24", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 9 }, - "limit": { "context": 2000000, "output": 30000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "x-ai/grok-4.2-fast-non-reasoning": { - "id": "x-ai/grok-4.2-fast-non-reasoning", - "name": "Grok 4.2 Fast Non Reasoning", - "attachment": true, + "grok-4-1-fast-non-reasoning": { + "id": "grok-4-1-fast-non-reasoning", + "name": "xAI Grok 4.1 Fast Non-Reasoning", + "family": "grok", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-08-31", - "release_date": "2026-03-20", - "last_updated": "2026-03-20", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 9 }, - "limit": { "context": 2000000, "output": 30000 } - }, - "anthropic/claude-opus-4.6": { - "id": "anthropic/claude-opus-4.6", - "name": "Claude Opus 4.6", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2026-02-06", - "last_updated": "2026-02-06", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "knowledge": "2025-11", + "release_date": "2025-11-17", + "last_updated": "2025-11-17", + "modalities": { + "input": ["text", "image"], + "output": ["text", "image"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 1000000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/anthropic", "api": "https://zenmux.ai/api/anthropic/v1" } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.19999999999999998, + "output": 0.5, + "cache_read": 0.049999999999999996 + } }, - "anthropic/claude-haiku-4.5": { - "id": "anthropic/claude-haiku-4.5", - "name": "Claude Haiku 4.5", - "attachment": true, + "sonar-pro": { + "id": "sonar-pro", + "name": "Perplexity Sonar Pro", + "family": "sonar-pro", + "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-01-27", + "last_updated": "2025-01-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/anthropic", "api": "https://zenmux.ai/api/anthropic/v1" } + "limit": { + "context": 200000, + "output": 4096 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "anthropic/claude-opus-4.1": { - "id": "anthropic/claude-opus-4.1", - "name": "Claude Opus 4.1", - "attachment": true, - "reasoning": true, + "mistral-large-2411": { + "id": "mistral-large-2411", + "name": "Mistral-Large", + "family": "mistral-large", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["image", "text", "pdf"], "output": ["text"] }, + "knowledge": "2024-07", + "release_date": "2024-07-24", + "last_updated": "2024-07-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/anthropic", "api": "https://zenmux.ai/api/anthropic/v1" } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 6 + } }, - "anthropic/claude-3.7-sonnet": { - "id": "anthropic/claude-3.7-sonnet", - "name": "Claude 3.7 Sonnet", - "attachment": true, - "reasoning": true, + "o3-pro": { + "id": "o3-pro", + "name": "OpenAI o3 Pro", + "family": "o-pro", + "attachment": false, + "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-02-24", - "last_updated": "2025-02-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-06", + "release_date": "2024-06-01", + "last_updated": "2024-06-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/anthropic", "api": "https://zenmux.ai/api/anthropic/v1" } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 20, + "output": 80 + } }, - "anthropic/claude-sonnet-4.6": { - "id": "anthropic/claude-sonnet-4.6", - "name": "Claude Sonnet 4.6", - "attachment": true, + "claude-opus-4-1": { + "id": "claude-opus-4-1", + "name": "Anthropic: Claude Opus 4.1", + "family": "claude-opus", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2026-02-18", - "last_updated": "2026-02-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-08", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 1000000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/anthropic", "api": "https://zenmux.ai/api/anthropic/v1" } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "anthropic/claude-sonnet-4": { - "id": "anthropic/claude-sonnet-4", - "name": "Claude Sonnet 4", - "attachment": true, - "reasoning": true, + "gpt-4o-mini": { + "id": "gpt-4o-mini", + "name": "OpenAI GPT-4o-mini", + "family": "gpt-mini", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["image", "text", "pdf"], "output": ["text"] }, + "knowledge": "2024-07", + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 1000000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/anthropic", "api": "https://zenmux.ai/api/anthropic/v1" } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.075 + } }, - "anthropic/claude-3.5-haiku": { - "id": "anthropic/claude-3.5-haiku", - "name": "Claude 3.5 Haiku", - "attachment": true, + "claude-4.5-haiku": { + "id": "claude-4.5-haiku", + "name": "Anthropic: Claude 4.5 Haiku", + "family": "claude-haiku", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2024-11-04", - "last_updated": "2024-11-04", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-10", + "release_date": "2025-10-01", + "last_updated": "2025-10-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.8, "output": 4, "cache_read": 0.08, "cache_write": 1 }, - "limit": { "context": 200000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/anthropic", "api": "https://zenmux.ai/api/anthropic/v1" } + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.09999999999999999, + "cache_write": 1.25 + } }, - "anthropic/claude-opus-4.5": { - "id": "anthropic/claude-opus-4.5", - "name": "Claude Opus 4.5", - "attachment": true, - "reasoning": true, + "kimi-k2-0711": { + "id": "kimi-k2-0711", + "name": "Kimi K2 (07/11)", + "family": "kimi", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-11-24", - "last_updated": "2025-11-24", - "modalities": { "input": ["pdf", "image", "text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 200000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/anthropic", "api": "https://zenmux.ai/api/anthropic/v1" } + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.5700000000000001, + "output": 2.3 + } }, - "anthropic/claude-sonnet-4.5": { - "id": "anthropic/claude-sonnet-4.5", - "name": "Claude Sonnet 4.5", - "attachment": true, - "reasoning": true, + "o4-mini": { + "id": "o4-mini", + "name": "OpenAI o4 Mini", + "family": "o-mini", + "attachment": false, + "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-06", + "release_date": "2024-06-01", + "last_updated": "2024-06-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 1000000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/anthropic", "api": "https://zenmux.ai/api/anthropic/v1" } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.275 + } }, - "anthropic/claude-opus-4": { - "id": "anthropic/claude-opus-4", - "name": "Claude Opus 4", - "attachment": true, + "sonar-deep-research": { + "id": "sonar-deep-research", + "name": "Perplexity Sonar Deep Research", + "family": "sonar-deep-research", + "attachment": false, "reasoning": true, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["image", "text", "pdf"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-01-27", + "last_updated": "2025-01-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 }, - "provider": { "npm": "@ai-sdk/anthropic", "api": "https://zenmux.ai/api/anthropic/v1" } + "limit": { + "context": 127000, + "output": 4096 + }, + "cost": { + "input": 2, + "output": 8 + } }, - "volcengine/doubao-seed-2.0-lite": { - "id": "volcengine/doubao-seed-2.0-lite", - "name": "Doubao-Seed-2.0-lite", - "attachment": true, - "reasoning": true, - "tool_call": true, + "gemma-3-12b-it": { + "id": "gemma-3-12b-it", + "name": "Google Gemma 3 12B", + "family": "gemma", + "attachment": false, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2026-02-14", - "release_date": "2026-02-14", - "last_updated": "2026-02-14", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.09, "output": 0.51, "cache_read": 0.02, "cache_write": 0.0024 }, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.049999999999999996, + "output": 0.09999999999999999 + } }, - "volcengine/doubao-seed-2.0-mini": { - "id": "volcengine/doubao-seed-2.0-mini", - "name": "Doubao-Seed-2.0-mini", - "attachment": true, + "gemini-2.5-flash": { + "id": "gemini-2.5-flash", + "name": "Google Gemini 2.5 Flash", + "family": "gemini-flash", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2026-02-14", - "release_date": "2026-02-14", - "last_updated": "2026-02-14", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2025-06", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.03, "output": 0.28, "cache_read": 0.01, "cache_write": 0.0024 }, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 1048576, + "output": 65535 + }, + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.075, + "cache_write": 0.3 + } }, - "volcengine/doubao-seed-2.0-pro": { - "id": "volcengine/doubao-seed-2.0-pro", - "name": "Doubao-Seed-2.0-pro", - "attachment": true, - "reasoning": true, + "deepseek-tng-r1t2-chimera": { + "id": "deepseek-tng-r1t2-chimera", + "name": "DeepSeek TNG R1T2 Chimera", + "family": "deepseek-thinking", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2026-02-14", - "release_date": "2026-02-14", - "last_updated": "2026-02-14", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-07-02", + "last_updated": "2025-07-02", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.45, "output": 2.24, "cache_read": 0.09, "cache_write": 0.0024 }, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 130000, + "output": 163840 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "volcengine/doubao-seed-2.0-code": { - "id": "volcengine/doubao-seed-2.0-code", - "name": "Doubao Seed 2.0 Code", - "attachment": true, + "gpt-5.1-codex-mini": { + "id": "gpt-5.1-codex-mini", + "name": "OpenAI: GPT-5.1 Codex Mini", + "family": "gpt-codex", + "attachment": false, "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2026-03-20", - "last_updated": "2026-03-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text", "image"] + }, "open_weights": false, - "cost": { "input": 0.9, "output": 4.48 }, - "limit": { "context": 256000, "output": 32000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.024999999999999998 + } }, - "volcengine/doubao-seed-1.8": { - "id": "volcengine/doubao-seed-1.8", - "name": "Doubao-Seed-1.8", - "attachment": true, + "claude-sonnet-4": { + "id": "claude-sonnet-4", + "name": "Anthropic: Claude Sonnet 4", + "family": "claude-sonnet", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-12-18", - "last_updated": "2025-12-18", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2025-05-14", + "last_updated": "2025-05-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.11, "output": 0.28, "cache_read": 0.02, "cache_write": 0.0024 }, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.30000000000000004, + "cache_write": 3.75 + } }, - "volcengine/doubao-seed-code": { - "id": "volcengine/doubao-seed-code", - "name": "Doubao-Seed-Code", - "attachment": true, - "reasoning": true, + "grok-code-fast-1": { + "id": "grok-code-fast-1", + "name": "xAI Grok Code Fast 1", + "family": "grok", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-11-11", - "last_updated": "2025-11-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-08", + "release_date": "2024-08-25", + "last_updated": "2024-08-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.17, "output": 1.12, "cache_read": 0.03 }, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 256000, + "output": 10000 + }, + "cost": { + "input": 0.19999999999999998, + "output": 1.5, + "cache_read": 0.02 + } }, - "deepseek/deepseek-chat": { - "id": "deepseek/deepseek-chat", - "name": "DeepSeek-V3.2 (Non-thinking Mode)", + "gpt-5.1": { + "id": "gpt-5.1", + "name": "OpenAI GPT-5.1", + "family": "gpt", "attachment": false, "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text", "image"] + }, "open_weights": false, - "cost": { "input": 0.28, "output": 0.42, "cache_read": 0.03 }, - "limit": { "context": 128000, "output": 64000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.12500000000000003 + } }, - "deepseek/deepseek-v3.2-exp": { - "id": "deepseek/deepseek-v3.2-exp", - "name": "DeepSeek-V3.2-Exp", + "deepseek-reasoner": { + "id": "deepseek-reasoner", + "name": "DeepSeek Reasoner", + "family": "deepseek-thinking", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.22, "output": 0.33 }, - "limit": { "context": 163000, "output": 64000 } + "limit": { + "context": 128000, + "output": 64000 + }, + "cost": { + "input": 0.56, + "output": 1.68, + "cache_read": 0.07 + } }, - "deepseek/deepseek-v3.2": { - "id": "deepseek/deepseek-v3.2", - "name": "DeepSeek V3.2", + "grok-4-fast-reasoning": { + "id": "grok-4-fast-reasoning", + "name": "xAI: Grok 4 Fast Reasoning", + "family": "grok", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-12-05", - "last_updated": "2025-12-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-09", + "release_date": "2025-09-01", + "last_updated": "2025-09-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.28, "output": 0.43 }, - "limit": { "context": 128000, "output": 64000 } + "limit": { + "context": 2000000, + "output": 2000000 + }, + "cost": { + "input": 0.19999999999999998, + "output": 0.5, + "cache_read": 0.049999999999999996 + } }, - "z-ai/glm-4.7-flash-free": { - "id": "z-ai/glm-4.7-flash-free", - "name": "GLM 4.7 Flash (Free)", + "o1": { + "id": "o1", + "name": "OpenAI: o1", + "family": "o", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2026-01-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 15, + "output": 60, + "cache_read": 7.5 + } }, - "z-ai/glm-5-turbo": { - "id": "z-ai/glm-5-turbo", - "name": "GLM 5 Turbo", - "attachment": true, - "reasoning": true, + "llama-3.1-8b-instant": { + "id": "llama-3.1-8b-instant", + "name": "Meta Llama 3.1 8B Instant", + "family": "llama", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2026-03-20", - "last_updated": "2026-03-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-07", + "release_date": "2024-07-01", + "last_updated": "2024-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.88, "output": 3.48 }, - "limit": { "context": 200000, "output": 128000 } + "limit": { + "context": 131072, + "output": 32678 + }, + "cost": { + "input": 0.049999999999999996, + "output": 0.08 + } }, - "z-ai/glm-4.5": { - "id": "z-ai/glm-4.5", - "name": "GLM 4.5", + "o3-mini": { + "id": "o3-mini", + "name": "OpenAI o3 Mini", + "family": "o-mini", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-07-25", - "last_updated": "2025-07-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2023-10", + "release_date": "2023-10-01", + "last_updated": "2023-10-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.35, "output": 1.54, "cache_read": 0.07 }, - "limit": { "context": 128000, "output": 64000 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.55 + } }, - "z-ai/glm-4.7-flashx": { - "id": "z-ai/glm-4.7-flashx", - "name": "GLM 4.7 FlashX", + "sonar": { + "id": "sonar", + "name": "Perplexity Sonar", + "family": "sonar", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2026-01-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-01-27", + "last_updated": "2025-01-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.07, "output": 0.42, "cache_read": 0.01 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 127000, + "output": 4096 + }, + "cost": { + "input": 1, + "output": 1 + } }, - "z-ai/glm-4.6": { - "id": "z-ai/glm-4.6", - "name": "GLM 4.6", + "kimi-k2-0905": { + "id": "kimi-k2-0905", + "name": "Kimi K2 (09/05)", + "family": "kimi", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-09", + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.35, "output": 1.54, "cache_read": 0.07 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 262144, + "output": 16384 + }, + "cost": { + "input": 0.5, + "output": 2, + "cache_read": 0.39999999999999997 + } }, - "z-ai/glm-4.6v": { - "id": "z-ai/glm-4.6v", - "name": "GLM 4.6V", - "attachment": true, - "reasoning": true, - "tool_call": true, + "mistral-small": { + "id": "mistral-small", + "name": "Mistral Small", + "family": "mistral-small", + "attachment": false, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-12-08", - "last_updated": "2025-12-08", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2024-02", + "release_date": "2024-02-26", + "last_updated": "2024-02-26", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.14, "output": 0.42, "cache_read": 0.03 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 75, + "output": 200 + } }, - "z-ai/glm-5": { - "id": "z-ai/glm-5", - "name": "GLM 5", + "qwen3-30b-a3b": { + "id": "qwen3-30b-a3b", + "name": "Qwen3 30B A3B", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.58, "output": 2.6, "cache_read": 0.14 }, - "limit": { "context": 200000, "output": 128000 } + "knowledge": "2025-06", + "release_date": "2025-06-01", + "last_updated": "2025-06-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 41000, + "output": 41000 + }, + "cost": { + "input": 0.08, + "output": 0.29 + } }, - "z-ai/glm-4.5-air": { - "id": "z-ai/glm-4.5-air", - "name": "GLM 4.5 Air", + "grok-4": { + "id": "grok-4", + "name": "xAI Grok 4", + "family": "grok", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-07-25", - "last_updated": "2025-07-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-07", + "release_date": "2024-07-09", + "last_updated": "2024-07-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.11, "output": 0.56, "cache_read": 0.02 }, - "limit": { "context": 128000, "output": 64000 } + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75 + } }, - "z-ai/glm-4.7": { - "id": "z-ai/glm-4.7", - "name": "GLM 4.7", + "qwen3-235b-a22b-thinking": { + "id": "qwen3-235b-a22b-thinking", + "name": "Qwen3 235B A22B Thinking", + "family": "qwen", "attachment": false, "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "tool_call": false, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-07-25", + "last_updated": "2025-07-25", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.28, "output": 1.14, "cache_read": 0.06 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 262144, + "output": 81920 + }, + "cost": { + "input": 0.3, + "output": 2.9000000000000004 + } }, - "z-ai/glm-4.6v-flash-free": { - "id": "z-ai/glm-4.6v-flash-free", - "name": "GLM 4.6V Flash (Free)", - "attachment": true, - "reasoning": true, - "tool_call": true, + "qwen2.5-coder-7b-fast": { + "id": "qwen2.5-coder-7b-fast", + "name": "Qwen2.5 Coder 7B fast", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-12-08", - "last_updated": "2025-12-08", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2024-09", + "release_date": "2024-09-15", + "last_updated": "2024-09-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 32000, + "output": 8192 + }, + "cost": { + "input": 0.03, + "output": 0.09 + } }, - "z-ai/glm-4.6v-flash": { - "id": "z-ai/glm-4.6v-flash", - "name": "GLM 4.6V FlashX", - "attachment": true, - "reasoning": true, + "llama-3.1-8b-instruct-turbo": { + "id": "llama-3.1-8b-instruct-turbo", + "name": "Meta Llama 3.1 8B Instruct Turbo", + "family": "llama", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-12-08", - "last_updated": "2025-12-08", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2024-07", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.02, "output": 0.21, "cache_read": 0.0043 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.02, + "output": 0.03 + } }, - "inclusionai/ring-1t": { - "id": "inclusionai/ring-1t", - "name": "Ring-1T", + "qwen3-next-80b-a3b-instruct": { + "id": "qwen3-next-80b-a3b-instruct", + "name": "Qwen3 Next 80B A3B Instruct", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-10-12", - "last_updated": "2025-10-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.56, "output": 2.24, "cache_read": 0.11 }, - "limit": { "context": 128000, "output": 64000 } + "limit": { + "context": 262000, + "output": 16384 + }, + "cost": { + "input": 0.14, + "output": 1.4 + } }, - "inclusionai/ling-1t": { - "id": "inclusionai/ling-1t", - "name": "Ling-1T", + "glm-4.6": { + "id": "glm-4.6", + "name": "Zai GLM-4.6", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-10-09", - "last_updated": "2025-10-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-07", + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.56, "output": 2.24, "cache_read": 0.11 }, - "limit": { "context": 128000, "output": 64000 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.44999999999999996, + "output": 1.5 + } }, - "google/gemini-2.5-flash-lite": { - "id": "google/gemini-2.5-flash-lite", - "name": "Gemini 2.5 Flash Lite", - "attachment": true, + "gpt-5-codex": { + "id": "gpt-5-codex", + "name": "OpenAI: GPT-5 Codex", + "family": "gpt-codex", + "attachment": false, "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-07-22", - "last_updated": "2025-07-22", - "modalities": { "input": ["pdf", "image", "text", "audio"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.03, "cache_write": 1 }, - "limit": { "context": 1048000, "output": 64000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.12500000000000003 + } }, - "google/gemini-3.1-pro-preview": { - "id": "google/gemini-3.1-pro-preview", - "name": "Gemini 3.1 Pro Preview", - "attachment": true, + "claude-opus-4-1-20250805": { + "id": "claude-opus-4-1-20250805", + "name": "Anthropic: Claude Opus 4.1 (20250805)", + "family": "claude-opus", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2026-02-19", - "release_date": "2026-02-19", - "last_updated": "2026-02-19", - "modalities": { "input": ["text", "image", "pdf", "audio", "video"], "output": ["text"] }, + "knowledge": "2025-08", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 12, "cache_read": 0.2, "cache_write": 4.5 }, - "limit": { "context": 1048000, "output": 64000 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "google/gemini-3-pro-preview": { - "id": "google/gemini-3-pro-preview", - "name": "Gemini 3 Pro Preview", - "attachment": true, - "reasoning": true, + "gpt-5.1-chat-latest": { + "id": "gpt-5.1-chat-latest", + "name": "OpenAI GPT-5.1 Chat", + "family": "gpt-codex", + "attachment": false, + "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image", "pdf", "audio", "video"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text", "image"] + }, "open_weights": false, - "cost": { "input": 2, "output": 12, "cache_read": 0.2, "cache_write": 4.5 }, - "limit": { "context": 1048000, "output": 64000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.12500000000000003 + } }, - "google/gemini-3-flash-preview": { - "id": "google/gemini-3-flash-preview", - "name": "Gemini 3 Flash Preview", - "attachment": true, - "reasoning": true, + "claude-haiku-4-5-20251001": { + "id": "claude-haiku-4-5-20251001", + "name": "Anthropic: Claude 4.5 Haiku (20251001)", + "family": "claude-haiku", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text", "image", "pdf", "audio"], "output": ["text"] }, + "knowledge": "2025-10", + "release_date": "2025-10-01", + "last_updated": "2025-10-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 3, "cache_read": 0.05, "cache_write": 1 }, - "limit": { "context": 1048000, "output": 64000 } + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.09999999999999999, + "cache_write": 1.25 + } }, - "google/gemini-2.5-pro": { - "id": "google/gemini-2.5-pro", - "name": "Gemini 2.5 Pro", - "attachment": true, + "sonar-reasoning": { + "id": "sonar-reasoning", + "name": "Perplexity Sonar Reasoning", + "family": "sonar-reasoning", + "attachment": false, "reasoning": true, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["pdf", "image", "text", "audio", "video"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-01-27", + "last_updated": "2025-01-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.31, "cache_write": 4.5 }, - "limit": { "context": 1048000, "output": 64000 } + "limit": { + "context": 127000, + "output": 4096 + }, + "cost": { + "input": 1, + "output": 5 + } }, - "google/gemini-2.5-flash": { - "id": "google/gemini-2.5-flash", - "name": "Gemini 2.5 Flash", - "attachment": true, + "claude-opus-4": { + "id": "claude-opus-4", + "name": "Anthropic: Claude Opus 4", + "family": "claude-opus", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["pdf", "image", "text", "audio"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2025-05-14", + "last_updated": "2025-05-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 2.5, "cache_read": 0.07, "cache_write": 1 }, - "limit": { "context": 1048000, "output": 64000 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "google/gemini-3.1-flash-lite-preview": { - "id": "google/gemini-3.1-flash-lite-preview", - "name": "Gemini 3.1 Flash Lite Preview", - "attachment": true, + "llama-prompt-guard-2-86m": { + "id": "llama-prompt-guard-2-86m", + "name": "Meta Llama Prompt Guard 2 86M", + "family": "llama", + "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "temperature": true, - "release_date": "2025-03-20", - "last_updated": "2025-03-20", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2024-10-01", + "last_updated": "2024-10-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 1.5 }, - "limit": { "context": 1050000, "output": 65530 } + "limit": { + "context": 512, + "output": 2 + }, + "cost": { + "input": 0.01, + "output": 0.01 + } }, - "baidu/ernie-5.0-thinking-preview": { - "id": "baidu/ernie-5.0-thinking-preview", - "name": "ERNIE 5.0", - "attachment": true, - "reasoning": true, + "gpt-4.1-nano": { + "id": "gpt-4.1-nano", + "name": "OpenAI GPT-4.1 Nano", + "family": "gpt-nano", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2026-01-22", - "last_updated": "2026-01-22", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.84, "output": 3.37 }, - "limit": { "context": 128000, "output": 64000 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.09999999999999999, + "output": 0.39999999999999997, + "cache_read": 0.024999999999999998 + } }, - "minimax/minimax-m2.5-lightning": { - "id": "minimax/minimax-m2.5-lightning", - "name": "MiniMax M2.5 highspeed", + "qwen3-coder-30b-a3b-instruct": { + "id": "qwen3-coder-30b-a3b-instruct", + "name": "Qwen3 Coder 30B A3B Instruct", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2026-02-13", - "last_updated": "2026-02-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-07-31", + "last_updated": "2025-07-31", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 4.8, "cache_read": 0.06, "cache_write": 0.75 }, - "limit": { "context": 204800, "output": 131072 }, - "provider": { "npm": "@ai-sdk/anthropic", "api": "https://zenmux.ai/api/anthropic/v1" } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.09999999999999999, + "output": 0.3 + } }, - "minimax/minimax-m2": { - "id": "minimax/minimax-m2", - "name": "MiniMax M2", + "claude-3.5-haiku": { + "id": "claude-3.5-haiku", + "name": "Anthropic: Claude 3.5 Haiku", + "family": "claude-haiku", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-10-27", - "last_updated": "2025-10-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.03, "cache_write": 0.38 }, - "limit": { "context": 204000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/anthropic", "api": "https://zenmux.ai/api/anthropic/v1" } + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 0.7999999999999999, + "output": 4, + "cache_read": 0.08, + "cache_write": 1 + } }, - "minimax/minimax-m2.1": { - "id": "minimax/minimax-m2.1", - "name": "MiniMax M2.1", + "grok-3-mini": { + "id": "grok-3-mini", + "name": "xAI Grok 3 Mini", + "family": "grok", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-06", + "release_date": "2024-06-01", + "last_updated": "2024-06-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.03, "cache_write": 0.38 }, - "limit": { "context": 204000, "output": 64000 }, - "provider": { "npm": "@ai-sdk/anthropic", "api": "https://zenmux.ai/api/anthropic/v1" } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 0.5, + "cache_read": 0.075 + } }, - "minimax/minimax-m2.7": { - "id": "minimax/minimax-m2.7", - "name": "MiniMax M2.7", - "attachment": true, - "reasoning": true, + "o3": { + "id": "o3", + "name": "OpenAI o3", + "family": "o", + "attachment": false, + "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2026-03-20", - "last_updated": "2026-03-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-06", + "release_date": "2024-06-01", + "last_updated": "2024-06-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3055, "output": 1.2219 }, - "limit": { "context": 204800, "output": 131070 }, - "provider": { "npm": "@ai-sdk/anthropic", "api": "https://zenmux.ai/api/anthropic/v1" } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } }, - "minimax/minimax-m2.7-highspeed": { - "id": "minimax/minimax-m2.7-highspeed", - "name": "MiniMax M2.7 highspeed", - "attachment": true, - "reasoning": true, + "deepseek-v3.2": { + "id": "deepseek-v3.2", + "name": "DeepSeek V3.2", + "family": "deepseek", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2026-03-20", - "last_updated": "2026-03-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-09", + "release_date": "2025-09-22", + "last_updated": "2025-09-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.611, "output": 2.4439 }, - "limit": { "context": 204800, "output": 131070 }, - "provider": { "npm": "@ai-sdk/anthropic", "api": "https://zenmux.ai/api/anthropic/v1" } + "limit": { + "context": 163840, + "output": 65536 + }, + "cost": { + "input": 0.27, + "output": 0.41 + } }, - "minimax/minimax-m2.5": { - "id": "minimax/minimax-m2.5", - "name": "MiniMax M2.5", + "gpt-oss-20b": { + "id": "gpt-oss-20b", + "name": "OpenAI GPT-OSS 20b", + "family": "gpt-oss", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2026-02-13", - "last_updated": "2026-02-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-06", + "release_date": "2024-06-01", + "last_updated": "2024-06-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.03, "cache_write": 0.375 }, - "limit": { "context": 204800, "output": 131072 }, - "provider": { "npm": "@ai-sdk/anthropic", "api": "https://zenmux.ai/api/anthropic/v1" } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.049999999999999996, + "output": 0.19999999999999998 + } }, - "qwen/qwen3.5-plus": { - "id": "qwen/qwen3.5-plus", - "name": "Qwen3.5 Plus", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2026-03-20", - "last_updated": "2026-03-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "gpt-5-pro": { + "id": "gpt-5-pro", + "name": "OpenAI: GPT-5 Pro", + "family": "gpt-pro", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.8, "output": 4.8 }, - "limit": { "context": 1000000, "output": 64000 } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 15, + "output": 120 + } }, - "qwen/qwen3-coder-plus": { - "id": "qwen/qwen3-coder-plus", - "name": "Qwen3-Coder-Plus", + "llama-guard-4": { + "id": "llama-guard-4", + "name": "Meta Llama Guard 4 12B", + "family": "llama", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1, "cache_write": 1.25 }, - "limit": { "context": 1000000, "output": 64000 } + "limit": { + "context": 131072, + "output": 1024 + }, + "cost": { + "input": 0.21, + "output": 0.21 + } }, - "qwen/qwen3-max": { - "id": "qwen/qwen3-max", - "name": "Qwen3-Max-Thinking", + "gpt-4o": { + "id": "gpt-4o", + "name": "OpenAI GPT-4o", + "family": "gpt", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2026-01-23", - "last_updated": "2026-01-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-05", + "release_date": "2024-05-13", + "last_updated": "2024-05-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.2, "output": 6 }, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10, + "cache_read": 1.25 + } }, - "qwen/qwen3.5-flash": { - "id": "qwen/qwen3.5-flash", - "name": "Qwen3.5 Flash", - "attachment": true, + "qwen3-vl-235b-a22b-instruct": { + "id": "qwen3-vl-235b-a22b-instruct", + "name": "Qwen3 VL 235B A22B Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2026-03-20", - "last_updated": "2026-03-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-09", + "release_date": "2025-09-23", + "last_updated": "2025-09-23", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 1020000, "output": 1020000 } + "limit": { + "context": 256000, + "output": 16384 + }, + "cost": { + "input": 0.3, + "output": 1.5 + } }, - "xiaomi/mimo-v2-flash-free": { - "id": "xiaomi/mimo-v2-flash-free", - "name": "MiMo-V2-Flash Free", + "gemini-2.5-flash-lite": { + "id": "gemini-2.5-flash-lite", + "name": "Google Gemini 2.5 Flash Lite", + "family": "gemini-flash-lite", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-07-22", + "last_updated": "2025-07-22", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 262000, "output": 64000 } + "limit": { + "context": 1048576, + "output": 65535 + }, + "cost": { + "input": 0.09999999999999999, + "output": 0.39999999999999997, + "cache_read": 0.024999999999999998, + "cache_write": 0.09999999999999999 + } }, - "xiaomi/mimo-v2-pro": { - "id": "xiaomi/mimo-v2-pro", - "name": "MiMo V2 Pro", - "attachment": true, - "reasoning": true, + "qwen3-coder": { + "id": "qwen3-coder", + "name": "Qwen3 Coder 480B A35B Instruct Turbo", + "family": "qwen", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2026-03-20", - "last_updated": "2026-03-20", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.5, "output": 4.5 }, - "limit": { "context": 1000000, "output": 256000 } + "limit": { + "context": 262144, + "output": 16384 + }, + "cost": { + "input": 0.22, + "output": 0.95 + } }, - "xiaomi/mimo-v2-omni": { - "id": "xiaomi/mimo-v2-omni", - "name": "MiMo V2 Omni", - "attachment": true, + "gpt-5": { + "id": "gpt-5", + "name": "OpenAI GPT-5", + "family": "gpt", + "attachment": false, "reasoning": false, "tool_call": true, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.12500000000000003 + } + }, + "ernie-4.5-21b-a3b-thinking": { + "id": "ernie-4.5-21b-a3b-thinking", + "name": "Baidu Ernie 4.5 21B A3B Thinking", + "family": "ernie", + "attachment": false, + "reasoning": true, + "tool_call": false, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2026-03-20", - "last_updated": "2026-03-20", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2025-03", + "release_date": "2025-03-16", + "last_updated": "2025-03-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 265000, "output": 265000 } + "limit": { + "context": 128000, + "output": 8000 + }, + "cost": { + "input": 0.07, + "output": 0.28 + } }, - "xiaomi/mimo-v2-flash": { - "id": "xiaomi/mimo-v2-flash", - "name": "MiMo-V2-Flash", + "gpt-oss-120b": { + "id": "gpt-oss-120b", + "name": "OpenAI GPT-OSS 120b", + "family": "gpt-oss", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-06", + "release_date": "2024-06-01", + "last_updated": "2024-06-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.3, "cache_read": 0.01 }, - "limit": { "context": 262000, "output": 64000 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.04, + "output": 0.16 + } }, - "stepfun/step-3.5-flash-free": { - "id": "stepfun/step-3.5-flash-free", - "name": "Step 3.5 Flash (Free)", + "gpt-5-chat-latest": { + "id": "gpt-5-chat-latest", + "name": "OpenAI GPT-5 Chat Latest", + "family": "gpt-codex", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2026-02-02", - "last_updated": "2026-02-02", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-09", + "release_date": "2024-09-30", + "last_updated": "2024-09-30", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.12500000000000003 + } }, - "stepfun/step-3": { - "id": "stepfun/step-3", - "name": "Step-3", - "attachment": true, + "claude-4.5-sonnet": { + "id": "claude-4.5-sonnet", + "name": "Anthropic: Claude Sonnet 4.5", + "family": "claude-sonnet", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-07-31", - "last_updated": "2025-07-31", - "modalities": { "input": ["image", "text"], "output": ["text"] }, + "knowledge": "2025-09", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.21, "output": 0.57 }, - "limit": { "context": 65536, "output": 64000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.30000000000000004, + "cache_write": 3.75 + } }, - "stepfun/step-3.5-flash": { - "id": "stepfun/step-3.5-flash", - "name": "Step 3.5 Flash", + "deepseek-v3": { + "id": "deepseek-v3", + "name": "DeepSeek V3", + "family": "deepseek", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2026-02-02", - "last_updated": "2026-02-02", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2024-12-26", + "last_updated": "2024-12-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.56, + "output": 1.68, + "cache_read": 0.07 + } }, - "kuaishou/kat-coder-pro-v1-free": { - "id": "kuaishou/kat-coder-pro-v1-free", - "name": "KAT-Coder-Pro-V1 Free", + "llama-3.1-8b-instruct": { + "id": "llama-3.1-8b-instruct", + "name": "Meta Llama 3.1 8B Instruct", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-10-23", - "last_updated": "2025-10-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-07", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 16384, + "output": 16384 + }, + "cost": { + "input": 0.02, + "output": 0.049999999999999996 + } }, - "kuaishou/kat-coder-pro-v1": { - "id": "kuaishou/kat-coder-pro-v1", - "name": "KAT-Coder-Pro-V1", + "gpt-4.1": { + "id": "gpt-4.1", + "name": "OpenAI GPT-4.1", + "family": "gpt", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-10-23", - "last_updated": "2025-10-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.06 }, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } }, - "moonshotai/kimi-k2-thinking": { - "id": "moonshotai/kimi-k2-thinking", + "kimi-k2-thinking": { + "id": "kimi-k2-thinking", "name": "Kimi K2 Thinking", + "family": "kimi-thinking", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", + "knowledge": "2025-11", "release_date": "2025-11-06", "last_updated": "2025-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.6, "output": 2.5, "cache_read": 0.15 }, - "limit": { "context": 262000, "output": 64000 } - }, - "moonshotai/kimi-k2.5": { - "id": "moonshotai/kimi-k2.5", - "name": "Kimi K2.5", - "attachment": true, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": false, - "knowledge": "2025-01-01", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.58, "output": 3.02, "cache_read": 0.1 }, - "limit": { "context": 262000, "output": 64000 } + "limit": { + "context": 256000, + "output": 262144 + }, + "cost": { + "input": 0.48, + "output": 2 + } }, - "moonshotai/kimi-k2-0905": { - "id": "moonshotai/kimi-k2-0905", - "name": "Kimi K2 0905", + "gpt-4.1-mini": { + "id": "gpt-4.1-mini", + "name": "OpenAI GPT-4.1 Mini", + "family": "gpt-mini", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-09-04", - "last_updated": "2025-09-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 2.5, "cache_read": 0.15 }, - "limit": { "context": 262000, "output": 64000 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.39999999999999997, + "output": 1.5999999999999999, + "cache_read": 0.09999999999999999 + } }, - "moonshotai/kimi-k2-thinking-turbo": { - "id": "moonshotai/kimi-k2-thinking-turbo", - "name": "Kimi K2 Thinking Turbo", + "deepseek-v3.1-terminus": { + "id": "deepseek-v3.1-terminus", + "name": "DeepSeek V3.1 Terminus", + "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2025-11-06", - "last_updated": "2025-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-09", + "release_date": "2025-09-22", + "last_updated": "2025-09-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.15, "output": 8, "cache_read": 0.15 }, - "limit": { "context": 262000, "output": 64000 } - } - } - }, - "perplexity": { - "id": "perplexity", - "env": ["PERPLEXITY_API_KEY"], - "npm": "@ai-sdk/perplexity", - "name": "Perplexity", - "doc": "https://docs.perplexity.ai", - "models": { - "sonar-deep-research": { - "id": "sonar-deep-research", - "name": "Perplexity Sonar Deep Research", + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.27, + "output": 1, + "cache_read": 0.21600000000000003 + } + }, + "gpt-5.1-codex": { + "id": "gpt-5.1-codex", + "name": "OpenAI: GPT-5.1 Codex", + "family": "gpt-codex", "attachment": false, - "reasoning": true, - "tool_call": false, + "reasoning": false, + "tool_call": true, "temperature": false, "knowledge": "2025-01", - "release_date": "2025-02-01", - "last_updated": "2025-09-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text", "image"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8, "reasoning": 3 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.12500000000000003 + } }, - "sonar": { - "id": "sonar", - "name": "Sonar", - "family": "sonar", + "grok-3": { + "id": "grok-3", + "name": "xAI Grok 3", + "family": "grok", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "knowledge": "2025-09-01", - "release_date": "2024-01-01", - "last_updated": "2025-09-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-06", + "release_date": "2024-06-01", + "last_updated": "2024-06-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75 + } + }, + "grok-4-fast-non-reasoning": { + "id": "grok-4-fast-non-reasoning", + "name": "xAI Grok 4 Fast Non-Reasoning", + "family": "grok", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2025-09", + "release_date": "2025-09-19", + "last_updated": "2025-09-19", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 1 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 2000000, + "output": 2000000 + }, + "cost": { + "input": 0.19999999999999998, + "output": 0.5, + "cache_read": 0.049999999999999996 + } }, "sonar-reasoning-pro": { "id": "sonar-reasoning-pro", - "name": "Sonar Reasoning Pro", + "name": "Perplexity Sonar Reasoning Pro", "family": "sonar-reasoning", - "attachment": true, + "attachment": false, "reasoning": true, "tool_call": false, "temperature": true, - "knowledge": "2025-09-01", - "release_date": "2024-01-01", - "last_updated": "2025-09-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 8 }, - "limit": { "context": 128000, "output": 4096 } - }, - "sonar-pro": { - "id": "sonar-pro", - "name": "Sonar Pro", - "family": "sonar-pro", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2025-09-01", - "release_date": "2024-01-01", - "last_updated": "2025-09-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-01-27", + "last_updated": "2025-01-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 200000, "output": 8192 } + "limit": { + "context": 127000, + "output": 4096 + }, + "cost": { + "input": 2, + "output": 8 + } } } }, - "privatemode-ai": { - "id": "privatemode-ai", - "env": ["PRIVATEMODE_API_KEY", "PRIVATEMODE_ENDPOINT"], + "ollama-cloud": { + "id": "ollama-cloud", + "env": ["OLLAMA_API_KEY"], "npm": "@ai-sdk/openai-compatible", - "api": "http://localhost:8080/v1", - "name": "Privatemode AI", - "doc": "https://docs.privatemode.ai/api/overview", + "api": "https://ollama.com/v1", + "name": "Ollama Cloud", + "doc": "https://docs.ollama.com/cloud", "models": { - "gpt-oss-120b": { - "id": "gpt-oss-120b", - "name": "gpt-oss-120b", - "family": "gpt-oss", + "minimax-m2.7": { + "id": "minimax-m2.7", + "name": "minimax-m2.7", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-08", - "release_date": "2025-08-04", - "last_updated": "2025-08-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 128000 } - }, - "qwen3-embedding-4b": { - "id": "qwen3-embedding-4b", - "name": "Qwen3-Embedding 4B", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-06-06", - "last_updated": "2025-06-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 32000, "output": 2560 } - }, - "whisper-large-v3": { - "id": "whisper-large-v3", - "name": "Whisper large-v3", - "family": "whisper", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "temperature": true, - "knowledge": "2023-09", - "release_date": "2023-09-01", - "last_updated": "2023-09-01", - "modalities": { "input": ["audio"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 0, "output": 4096 } - }, - "gemma-3-27b": { - "id": "gemma-3-27b", - "name": "Gemma 3 27B", - "family": "gemma", - "attachment": true, - "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-03-12", - "last_updated": "2025-03-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 196608, + "output": 196608 + } }, - "qwen3-coder-30b-a3b": { - "id": "qwen3-coder-30b-a3b", - "name": "Qwen3-Coder 30B-A3B", - "family": "qwen", + "gpt-oss:20b": { + "id": "gpt-oss:20b", + "name": "gpt-oss:20b", + "family": "gpt-oss", "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 32768 } - } - } - }, - "perplexity-agent": { - "id": "perplexity-agent", - "env": ["PERPLEXITY_API_KEY"], - "npm": "@ai-sdk/openai", - "api": "https://api.perplexity.ai/v1", - "name": "Perplexity Agent", - "doc": "https://docs.perplexity.ai/docs/agent-api/models", - "models": { - "openai/gpt-5-mini": { - "id": "openai/gpt-5-mini", - "name": "GPT-5 Mini", - "family": "gpt-mini", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.025 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } - }, - "openai/gpt-5.4": { - "id": "openai/gpt-5.4", - "name": "GPT-5.4", - "family": "gpt", - "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 15, "cache_read": 0.25 }, - "limit": { "context": 1050000, "input": 922000, "output": 128000 } + "release_date": "2025-08-05", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + } }, - "openai/gpt-5.2": { - "id": "openai/gpt-5.2", - "name": "GPT-5.2", - "family": "gpt", + "kimi-k2.5": { + "id": "kimi-k2.5", + "name": "kimi-k2.5", + "family": "kimi", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + } }, - "openai/gpt-5.1": { - "id": "openai/gpt-5.1", - "name": "GPT-5.1", - "family": "gpt", - "attachment": true, + "glm-4.7": { + "id": "glm-4.7", + "name": "glm-4.7", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "release_date": "2025-12-22", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 131072 + } }, - "anthropic/claude-opus-4-5": { - "id": "anthropic/claude-opus-4-5", - "name": "Claude Opus 4.5", - "family": "claude-opus", + "gemma4:31b": { + "id": "gemma4:31b", + "name": "gemma4:31b", + "family": "gemma", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-11-24", - "last_updated": "2025-11-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5 }, - "limit": { "context": 200000, "output": 64000 } + "knowledge": "2025-01", + "release_date": "2026-04-02", + "last_updated": "2026-04-08", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + } }, - "anthropic/claude-opus-4-6": { - "id": "anthropic/claude-opus-4-6", - "name": "Claude Opus 4.6", - "family": "claude-opus", - "attachment": true, + "gpt-oss:120b": { + "id": "gpt-oss:120b", + "name": "gpt-oss:120b", + "family": "gpt-oss", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-05", - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5 }, - "limit": { "context": 200000, "output": 128000 } + "release_date": "2025-08-05", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + } }, - "anthropic/claude-sonnet-4-6": { - "id": "anthropic/claude-sonnet-4-6", - "name": "Claude Sonnet 4.6", - "family": "claude-sonnet", + "qwen3.5:397b": { + "id": "qwen3.5:397b", + "name": "qwen3.5:397b", + "family": "qwen", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-08", - "release_date": "2026-02-17", + "interleaved": { + "field": "reasoning_details" + }, + "release_date": "2026-02-15", "last_updated": "2026-02-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3 }, - "limit": { "context": 200000, "output": 64000 } + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + } }, - "anthropic/claude-sonnet-4-5": { - "id": "anthropic/claude-sonnet-4-5", - "name": "Claude Sonnet 4.5", - "family": "claude-sonnet", - "attachment": true, + "deepseek-v3.1:671b": { + "id": "deepseek-v3.1:671b", + "name": "deepseek-v3.1:671b", + "family": "deepseek", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3 }, - "limit": { "context": 200000, "output": 64000 } + "release_date": "2025-08-21", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 163840 + } }, - "anthropic/claude-haiku-4-5": { - "id": "anthropic/claude-haiku-4-5", - "name": "Claude Haiku 4.5", - "family": "claude-haiku", - "attachment": true, + "glm-5": { + "id": "glm-5", + "name": "glm-5", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-02-28", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1 }, - "limit": { "context": 200000, "output": 64000 } + "interleaved": { + "field": "reasoning_content" + }, + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 131072 + } }, - "xai/grok-4-1-fast-non-reasoning": { - "id": "xai/grok-4-1-fast-non-reasoning", - "name": "Grok 4.1 Fast (Non-Reasoning)", - "family": "grok", + "qwen3-vl:235b-instruct": { + "id": "qwen3-vl:235b-instruct", + "name": "qwen3-vl:235b-instruct", + "family": "qwen", "attachment": true, "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-11-19", - "last_updated": "2025-11-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } - }, - "nvidia/nemotron-3-super-120b-a12b": { - "id": "nvidia/nemotron-3-super-120b-a12b", - "name": "Nemotron 3 Super 120B", - "family": "nemotron", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2026-02", - "release_date": "2026-03-11", - "last_updated": "2026-03-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-09-22", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.25, "output": 2.5 }, - "limit": { "context": 1000000, "output": 32000 } + "limit": { + "context": 262144, + "output": 131072 + } }, - "google/gemini-3.1-pro-preview": { - "id": "google/gemini-3.1-pro-preview", - "name": "Gemini 3.1 Pro Preview", - "family": "gemini-pro", + "gemma3:4b": { + "id": "gemma3:4b", + "name": "gemma3:4b", + "family": "gemma", "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-02-19", - "last_updated": "2026-02-19", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { - "input": 2, - "output": 12, - "cache_read": 0.2, - "context_over_200k": { "input": 4, "output": 18, "cache_read": 0.4 } + "reasoning": false, + "tool_call": false, + "release_date": "2024-12-01", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] }, - "limit": { "context": 1048576, "output": 65536 } + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + } }, - "google/gemini-3-flash-preview": { - "id": "google/gemini-3-flash-preview", - "name": "Gemini 3 Flash Preview", + "gemini-3-flash-preview": { + "id": "gemini-3-flash-preview", + "name": "gemini-3-flash-preview", "family": "gemini-flash", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, "knowledge": "2025-01", "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { - "input": 0.5, - "output": 3, - "cache_read": 0.05, - "context_over_200k": { "input": 0.5, "output": 3, "cache_read": 0.05 } + "last_updated": "2026-04-08", + "modalities": { + "input": ["text", "image"], + "output": ["text"] }, - "limit": { "context": 1048576, "output": 65536 } + "open_weights": true, + "limit": { + "context": 1048576, + "output": 65536 + } }, - "google/gemini-2.5-pro": { - "id": "google/gemini-2.5-pro", - "name": "Gemini 2.5 Pro", - "family": "gemini-pro", + "ministral-3:14b": { + "id": "ministral-3:14b", + "name": "ministral-3:14b", + "family": "ministral", "attachment": true, + "reasoning": false, + "tool_call": true, + "release_date": "2024-12-01", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 128000 + } + }, + "minimax-m2": { + "id": "minimax-m2", + "name": "minimax-m2", + "family": "minimax", + "attachment": false, + "reasoning": false, + "tool_call": true, + "release_date": "2025-10-23", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 128000 + } + }, + "qwen3-next:80b": { + "id": "qwen3-next:80b", + "name": "qwen3-next:80b", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-03-20", - "last_updated": "2025-06-05", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { - "input": 1.25, - "output": 10, - "cache_read": 0.125, - "context_over_200k": { "input": 2.5, "output": 15, "cache_read": 0.25 } + "release_date": "2025-09-15", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] }, - "limit": { "context": 1048576, "output": 65536 } + "open_weights": true, + "limit": { + "context": 262144, + "output": 32768 + } }, - "google/gemini-2.5-flash": { - "id": "google/gemini-2.5-flash", - "name": "Gemini 2.5 Flash", - "family": "gemini-flash", + "qwen3-vl:235b": { + "id": "qwen3-vl:235b", + "name": "qwen3-vl:235b", + "family": "qwen", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-03-20", - "last_updated": "2025-06-05", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.5, "cache_read": 0.03 }, - "limit": { "context": 1048576, "output": 65536 } + "release_date": "2025-09-22", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 32768 + } }, - "perplexity/sonar": { - "id": "perplexity/sonar", - "name": "Sonar", - "family": "sonar", + "rnj-1:8b": { + "id": "rnj-1:8b", + "name": "rnj-1:8b", + "family": "rnj", "attachment": false, "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2025-09-01", - "release_date": "2024-01-01", - "last_updated": "2025-09-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 2.5, "cache_read": 0.0625 }, - "limit": { "context": 128000, "output": 8192 } - } - } - }, - "gitlab": { - "id": "gitlab", - "env": ["GITLAB_TOKEN"], - "npm": "gitlab-ai-provider", - "name": "GitLab Duo", - "doc": "https://docs.gitlab.com/user/duo_agent_platform/", - "models": { - "duo-chat-gpt-5-1": { - "id": "duo-chat-gpt-5-1", - "name": "Agentic Chat (GPT-5.1)", - "family": "gpt", - "attachment": true, + "release_date": "2025-12-06", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 4096 + } + }, + "minimax-m2.1": { + "id": "minimax-m2.1", + "name": "minimax-m2.1", + "family": "minimax", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2026-01-22", - "last_updated": "2026-01-22", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "release_date": "2025-12-23", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + } }, - "duo-chat-gpt-5-2": { - "id": "duo-chat-gpt-5-2", - "name": "Agentic Chat (GPT-5.2)", - "family": "gpt", - "attachment": true, + "glm-5.1": { + "id": "glm-5.1", + "name": "glm-5.1", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-01-23", - "last_updated": "2026-01-23", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "interleaved": { + "field": "reasoning_content" + }, + "release_date": "2026-03-27", + "last_updated": "2026-04-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 131072 + } }, - "duo-chat-sonnet-4-6": { - "id": "duo-chat-sonnet-4-6", - "name": "Agentic Chat (Claude Sonnet 4.6)", - "family": "claude-sonnet", + "mistral-large-3:675b": { + "id": "mistral-large-3:675b", + "name": "mistral-large-3:675b", + "family": "mistral-large", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2025-08-31", - "release_date": "2026-02-17", - "last_updated": "2026-02-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 1000000, "output": 64000 } + "release_date": "2025-12-02", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + } }, - "duo-chat-opus-4-6": { - "id": "duo-chat-opus-4-6", - "name": "Agentic Chat (Claude Opus 4.6)", - "family": "claude-opus", + "ministral-3:8b": { + "id": "ministral-3:8b", + "name": "ministral-3:8b", + "family": "ministral", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 1000000, "output": 64000 } + "release_date": "2024-12-01", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 128000 + } }, - "duo-chat-haiku-4-5": { - "id": "duo-chat-haiku-4-5", - "name": "Agentic Chat (Claude Haiku 4.5)", - "family": "claude-haiku", + "gemma3:12b": { + "id": "gemma3:12b", + "name": "gemma3:12b", + "family": "gemma", "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-02-28", - "release_date": "2026-01-08", - "last_updated": "2026-01-08", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 200000, "output": 64000 } + "reasoning": false, + "tool_call": false, + "release_date": "2024-12-01", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + } }, - "duo-chat-gpt-5-codex": { - "id": "duo-chat-gpt-5-codex", - "name": "Agentic Chat (GPT-5 Codex)", - "family": "gpt-codex", + "qwen3-coder:480b": { + "id": "qwen3-coder:480b", + "name": "qwen3-coder:480b", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2026-01-22", - "last_updated": "2026-01-22", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "release_date": "2025-07-22", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + } }, - "duo-chat-gpt-5-mini": { - "id": "duo-chat-gpt-5-mini", - "name": "Agentic Chat (GPT-5 Mini)", - "family": "gpt-mini", - "attachment": true, + "nemotron-3-nano:30b": { + "id": "nemotron-3-nano:30b", + "name": "nemotron-3-nano:30b", + "family": "nemotron", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2026-01-22", - "last_updated": "2026-01-22", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "release_date": "2025-12-15", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 131072 + } }, - "duo-chat-gpt-5-2-codex": { - "id": "duo-chat-gpt-5-2-codex", - "name": "Agentic Chat (GPT-5.2 Codex)", - "family": "gpt-codex", - "attachment": true, + "deepseek-v4-flash": { + "id": "deepseek-v4-flash", + "name": "deepseek-v4-flash", + "family": "deepseek-flash", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-01-22", - "last_updated": "2026-01-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 1048576 + } }, - "duo-chat-gpt-5-4-mini": { - "id": "duo-chat-gpt-5-4-mini", - "name": "Agentic Chat (GPT-5.4 Mini)", - "family": "gpt-mini", - "attachment": true, + "glm-4.6": { + "id": "glm-4.6", + "name": "glm-4.6", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-17", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "release_date": "2025-09-29", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 131072 + } }, - "duo-chat-gpt-5-3-codex": { - "id": "duo-chat-gpt-5-3-codex", - "name": "Agentic Chat (GPT-5.3 Codex)", - "family": "gpt-codex", + "kimi-k2.6": { + "id": "kimi-k2.6", + "name": "kimi-k2.6", + "family": "kimi", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "release_date": "2026-04-20", + "last_updated": "2026-04-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + } }, - "duo-chat-gpt-5-4-nano": { - "id": "duo-chat-gpt-5-4-nano", - "name": "Agentic Chat (GPT-5.4 Nano)", - "family": "gpt-nano", + "ministral-3:3b": { + "id": "ministral-3:3b", + "name": "ministral-3:3b", + "family": "ministral", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-17", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "release_date": "2024-10-22", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 128000 + } }, - "duo-chat-gpt-5-4": { - "id": "duo-chat-gpt-5-4", - "name": "Agentic Chat (GPT-5.4)", - "family": "gpt", + "gemma3:27b": { + "id": "gemma3:27b", + "name": "gemma3:27b", + "family": "gemma", "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 1050000, "input": 922000, "output": 128000 } + "reasoning": false, + "tool_call": false, + "release_date": "2025-07-27", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + } }, - "duo-chat-sonnet-4-5": { - "id": "duo-chat-sonnet-4-5", - "name": "Agentic Chat (Claude Sonnet 4.5)", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, + "devstral-2:123b": { + "id": "devstral-2:123b", + "name": "devstral-2:123b", + "family": "devstral", + "attachment": false, + "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2026-01-08", - "last_updated": "2026-01-08", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 200000, "output": 64000 } + "release_date": "2025-12-09", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + } }, - "duo-chat-opus-4-5": { - "id": "duo-chat-opus-4-5", - "name": "Agentic Chat (Claude Opus 4.5)", - "family": "claude-opus", - "attachment": true, + "cogito-2.1:671b": { + "id": "cogito-2.1:671b", + "name": "cogito-2.1:671b", + "family": "cogito", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2026-01-08", - "last_updated": "2026-01-08", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 200000, "output": 64000 } - } - } - }, - "vivgrid": { - "id": "vivgrid", - "env": ["VIVGRID_API_KEY"], - "npm": "@ai-sdk/openai", - "api": "https://api.vivgrid.com/v1", - "name": "Vivgrid", - "doc": "https://docs.vivgrid.com/models", - "models": { - "gpt-5.2-codex": { - "id": "gpt-5.2-codex", - "name": "GPT-5.2 Codex", - "family": "gpt-codex", + "release_date": "2025-11-19", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 32000 + } + }, + "qwen3-coder-next": { + "id": "qwen3-coder-next", + "name": "qwen3-coder-next", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": true, + "release_date": "2026-02-02", + "last_updated": "2026-02-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + } + }, + "nemotron-3-super": { + "id": "nemotron-3-super", + "name": "nemotron-3-super", + "family": "nemotron", "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-01-14", - "last_updated": "2026-01-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "output": 128000 } + "release_date": "2026-03-11", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + } }, - "gemini-3.1-pro-preview": { - "id": "gemini-3.1-pro-preview", - "name": "Gemini 3.1 Pro Preview", - "family": "gemini-pro", - "attachment": true, + "deepseek-v4-pro": { + "id": "deepseek-v4-pro", + "name": "deepseek-v4-pro", + "family": "deepseek-thinking", + "attachment": false, + "reasoning": true, + "tool_call": true, + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 1048576 + } + }, + "minimax-m2.5": { + "id": "minimax-m2.5", + "name": "minimax-m2.5", + "family": "minimax", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, "knowledge": "2025-01", - "release_date": "2026-02-19", - "last_updated": "2026-02-19", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { - "input": 2, - "output": 12, - "cache_read": 0.2, - "context_over_200k": { "input": 4, "output": 18, "cache_read": 0.4 } + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] }, - "limit": { "context": 1048576, "output": 65536 }, - "provider": { "npm": "@ai-sdk/openai-compatible" } + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + } }, - "gpt-5-mini": { - "id": "gpt-5-mini", - "name": "GPT-5 Mini", - "family": "gpt-mini", - "attachment": true, + "deepseek-v3.2": { + "id": "deepseek-v3.2", + "name": "deepseek-v3.2", + "family": "deepseek", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.03 }, - "limit": { "context": 272000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai-compatible" } + "release_date": "2025-06-15", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 65536 + } }, - "gpt-5.1-codex-max": { - "id": "gpt-5.1-codex-max", - "name": "GPT-5.1 Codex Max", - "family": "gpt-codex", + "kimi-k2-thinking": { + "id": "kimi-k2-thinking", + "name": "kimi-k2-thinking", + "family": "kimi-thinking", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } + "knowledge": "2024-08", + "release_date": "2025-11-06", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + } }, - "gpt-5.4": { - "id": "gpt-5.4", - "name": "GPT-5.4", - "family": "gpt", + "devstral-small-2:24b": { + "id": "devstral-small-2:24b", + "name": "devstral-small-2:24b", + "family": "devstral", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 15, "cache_read": 0.25 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai-compatible" } + "release_date": "2025-12-09", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + } }, - "glm-5": { - "id": "glm-5", - "name": "GLM-5", + "kimi-k2:1t": { + "id": "kimi-k2:1t", + "name": "kimi-k2:1t", + "family": "kimi", + "attachment": false, + "reasoning": false, + "tool_call": true, + "knowledge": "2024-10", + "release_date": "2025-07-11", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + } + } + } + }, + "zai-coding-plan": { + "id": "zai-coding-plan", + "env": ["ZHIPU_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.z.ai/api/coding/paas/v4", + "name": "Z.AI Coding Plan", + "doc": "https://docs.z.ai/devpack/overview", + "models": { + "glm-4.7": { + "id": "glm-4.7", + "name": "GLM-4.7", "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1, "output": 3.2, "cache_read": 0.2 }, - "limit": { "context": 202752, "output": 131000 }, - "provider": { "npm": "@ai-sdk/openai-compatible" } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "gemini-3.1-flash-lite-preview": { - "id": "gemini-3.1-flash-lite-preview", - "name": "Gemini 3.1 Flash Lite Preview", - "family": "gemini-flash-lite", - "attachment": true, + "glm-5.1": { + "id": "glm-5.1", + "name": "GLM-5.1", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-03-03", - "last_updated": "2026-03-03", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, + "release_date": "2026-03-27", + "last_updated": "2026-03-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 1.5, "cache_read": 0.025, "cache_write": 1 }, - "limit": { "context": 1048576, "output": 65536 }, - "provider": { "npm": "@ai-sdk/openai-compatible" } + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "deepseek-v3.2": { - "id": "deepseek-v3.2", - "name": "DeepSeek-V3.2", - "family": "deepseek", + "glm-4.5-air": { + "id": "glm-4.5-air", + "name": "GLM-4.5-Air", + "family": "glm-air", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.28, "output": 0.42 }, - "limit": { "context": 128000, "output": 128000 }, - "provider": { "npm": "@ai-sdk/openai-compatible" } + "limit": { + "context": 131072, + "output": 98304 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "gpt-5.1-codex": { - "id": "gpt-5.1-codex", - "name": "GPT-5.1 Codex", - "family": "gpt-codex", + "glm-5-turbo": { + "id": "glm-5-turbo", + "name": "GLM-5-Turbo", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "release_date": "2026-03-16", + "last_updated": "2026-03-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } + }, + "glm-5v-turbo": { + "id": "glm-5v-turbo", + "name": "GLM-5V-Turbo", + "family": "glm", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-04-01", + "last_updated": "2026-04-01", + "modalities": { + "input": ["text", "image", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } } } }, - "helicone": { - "id": "helicone", - "env": ["HELICONE_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://ai-gateway.helicone.ai/v1", - "name": "Helicone", - "doc": "https://helicone.ai/models", + "amazon-bedrock": { + "id": "amazon-bedrock", + "env": ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION", "AWS_BEARER_TOKEN_BEDROCK"], + "npm": "@ai-sdk/amazon-bedrock", + "name": "Amazon Bedrock", + "doc": "https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html", "models": { - "qwen3-coder-30b-a3b-instruct": { - "id": "qwen3-coder-30b-a3b-instruct", - "name": "Qwen3 Coder 30B A3B Instruct", - "family": "qwen", + "openai.gpt-oss-safeguard-120b": { + "id": "openai.gpt-oss-safeguard-120b", + "name": "GPT OSS Safeguard 120B", + "family": "gpt-oss", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-07-31", - "last_updated": "2025-07-31", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.09999999999999999, "output": 0.3 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "gemini-2.5-flash-lite": { - "id": "gemini-2.5-flash-lite", - "name": "Google Gemini 2.5 Flash Lite", - "family": "gemini-flash-lite", + "nvidia.nemotron-nano-3-30b": { + "id": "nvidia.nemotron-nano-3-30b", + "name": "NVIDIA Nemotron Nano 3 30B", + "family": "nemotron", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-07-22", - "last_updated": "2025-07-22", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, "cost": { - "input": 0.09999999999999999, - "output": 0.39999999999999997, - "cache_read": 0.024999999999999998, - "cache_write": 0.09999999999999999 + "input": 0.06, + "output": 0.24 + } + }, + "nvidia.nemotron-super-3-120b": { + "id": "nvidia.nemotron-super-3-120b", + "name": "NVIDIA Nemotron 3 Super 120B A12B", + "family": "nemotron", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-11", + "last_updated": "2026-03-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 131072 }, - "limit": { "context": 1048576, "output": 65535 } + "cost": { + "input": 0.15, + "output": 0.65 + } }, - "gpt-5.1-codex-mini": { - "id": "gpt-5.1-codex-mini", - "name": "OpenAI: GPT-5.1 Codex Mini", - "family": "gpt-codex", + "writer.palmyra-x5-v1:0": { + "id": "writer.palmyra-x5-v1:0", + "name": "Palmyra X5", + "family": "palmyra", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2025-01", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text", "image"], "output": ["text", "image"] }, + "temperature": true, + "release_date": "2025-04-28", + "last_updated": "2025-04-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.024999999999999998 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 1040000, + "output": 8192 + }, + "cost": { + "input": 0.6, + "output": 6 + } }, - "llama-3.3-70b-versatile": { - "id": "llama-3.3-70b-versatile", - "name": "Meta Llama 3.3 70B Versatile", - "family": "llama", + "mistral.ministral-3-8b-instruct": { + "id": "mistral.ministral-3-8b-instruct", + "name": "Ministral 3 8B", + "family": "ministral", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.59, "output": 0.7899999999999999 }, - "limit": { "context": 131072, "output": 32678 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.15, + "output": 0.15 + } }, - "claude-4.5-opus": { - "id": "claude-4.5-opus", - "name": "Anthropic: Claude Opus 4.5", + "au.anthropic.claude-opus-4-6-v1": { + "id": "au.anthropic.claude-opus-4-6-v1", + "name": "AU Anthropic Claude Opus 4.6", "family": "claude-opus", - "attachment": false, + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-11", - "release_date": "2025-11-24", - "last_updated": "2025-11-24", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 16.5, + "output": 82.5, + "cache_read": 1.65, + "cache_write": 20.625 + } }, - "hermes-2-pro-llama-3-8b": { - "id": "hermes-2-pro-llama-3-8b", - "name": "Hermes 2 Pro Llama 3 8B", - "family": "llama", + "mistral.ministral-3-3b-instruct": { + "id": "mistral.ministral-3-3b-instruct", + "name": "Ministral 3 3B", + "family": "ministral", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-05", - "release_date": "2024-05-27", - "last_updated": "2024-05-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.14, "output": 0.14 }, - "limit": { "context": 131072, "output": 131072 } + "release_date": "2025-12-02", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "o3-mini": { - "id": "o3-mini", - "name": "OpenAI o3 Mini", - "family": "o-mini", - "attachment": false, - "reasoning": false, + "anthropic.claude-sonnet-4-5-20250929-v1:0": { + "id": "anthropic.claude-sonnet-4-5-20250929-v1:0", + "name": "Claude Sonnet 4.5", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2023-10", - "release_date": "2023-10-01", - "last_updated": "2023-10-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.55 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "deepseek-v3.1-terminus": { - "id": "deepseek-v3.1-terminus", - "name": "DeepSeek V3.1 Terminus", - "family": "deepseek", + "mistral.devstral-2-123b": { + "id": "mistral.devstral-2-123b", + "name": "Devstral 2 123B", + "family": "devstral", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-09-22", - "last_updated": "2025-09-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.27, "output": 1, "cache_read": 0.21600000000000003 }, - "limit": { "context": 128000, "output": 16384 } + "release_date": "2026-02-17", + "last_updated": "2026-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 8192 + }, + "cost": { + "input": 0.4, + "output": 2 + } }, - "deepseek-r1-distill-llama-70b": { - "id": "deepseek-r1-distill-llama-70b", - "name": "DeepSeek R1 Distill Llama 70B", - "family": "deepseek-thinking", - "attachment": false, + "global.anthropic.claude-opus-4-5-20251101-v1:0": { + "id": "global.anthropic.claude-opus-4-5-20251101-v1:0", + "name": "Claude Opus 4.5 (Global)", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-03-31", + "release_date": "2025-11-24", + "last_updated": "2025-08-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.03, "output": 0.13 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "sonar-reasoning": { - "id": "sonar-reasoning", - "name": "Perplexity Sonar Reasoning", - "family": "sonar-reasoning", - "attachment": false, - "reasoning": true, - "tool_call": false, + "mistral.voxtral-small-24b-2507": { + "id": "mistral.voxtral-small-24b-2507", + "name": "Voxtral Small 24B 2507", + "family": "mistral", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-01-27", - "last_updated": "2025-01-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1, "output": 5 }, - "limit": { "context": 127000, "output": 4096 } + "release_date": "2025-07-01", + "last_updated": "2025-07-01", + "modalities": { + "input": ["text", "audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32000, + "output": 8192 + }, + "cost": { + "input": 0.15, + "output": 0.35 + } }, - "gpt-5-pro": { - "id": "gpt-5-pro", - "name": "OpenAI: GPT-5 Pro", - "family": "gpt-pro", + "google.gemma-3-12b-it": { + "id": "google.gemma-3-12b-it", + "name": "Google Gemma 3 12B", + "family": "gemma", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": false, - "knowledge": "2025-01", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 120 }, - "limit": { "context": 128000, "output": 32768 } - }, - "qwen3-vl-235b-a22b-instruct": { - "id": "qwen3-vl-235b-a22b-instruct", - "name": "Qwen3 VL 235B A22B Instruct", - "family": "qwen", - "attachment": false, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.049999999999999996, + "output": 0.09999999999999999 + } + }, + "amazon.nova-pro-v1:0": { + "id": "amazon.nova-pro-v1:0", + "name": "Nova Pro", + "family": "nova-pro", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-09-23", - "last_updated": "2025-09-23", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2024-12-03", + "last_updated": "2024-12-03", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.5 }, - "limit": { "context": 256000, "output": 16384 } + "limit": { + "context": 300000, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 3.2, + "cache_read": 0.2 + } }, - "kimi-k2-thinking": { - "id": "kimi-k2-thinking", - "name": "Kimi K2 Thinking", - "family": "kimi-thinking", - "attachment": false, - "reasoning": false, + "anthropic.claude-haiku-4-5-20251001-v1:0": { + "id": "anthropic.claude-haiku-4-5-20251001-v1:0", + "name": "Claude Haiku 4.5", + "family": "claude-haiku", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-11", - "release_date": "2025-11-06", - "last_updated": "2025-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.48, "output": 2 }, - "limit": { "context": 256000, "output": 262144 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } }, - "kimi-k2-0711": { - "id": "kimi-k2-0711", - "name": "Kimi K2 (07/11)", - "family": "kimi", + "minimax.minimax-m2": { + "id": "minimax.minimax-m2", + "name": "MiniMax M2", + "family": "minimax", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5700000000000001, "output": 2.3 }, - "limit": { "context": 131072, "output": 16384 } + "release_date": "2025-10-27", + "last_updated": "2025-10-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204608, + "output": 128000 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "gpt-5": { - "id": "gpt-5", - "name": "OpenAI GPT-5", - "family": "gpt", - "attachment": false, - "reasoning": false, + "global.anthropic.claude-opus-4-7": { + "id": "global.anthropic.claude-opus-4-7", + "name": "Claude Opus 4.7 (Global)", + "family": "claude-opus", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": false, - "knowledge": "2025-01", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.12500000000000003 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "llama-3.3-70b-instruct": { - "id": "llama-3.3-70b-instruct", - "name": "Meta Llama 3.3 70B Instruct", - "family": "llama", + "mistral.pixtral-large-2502-v1:0": { + "id": "mistral.pixtral-large-2502-v1:0", + "name": "Pixtral Large (25.02)", + "family": "mistral", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-04-08", + "last_updated": "2025-04-08", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.13, "output": 0.39 }, - "limit": { "context": 128000, "output": 16400 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 2, + "output": 6 + } }, - "chatgpt-4o-latest": { - "id": "chatgpt-4o-latest", - "name": "OpenAI ChatGPT-4o", - "family": "gpt", - "attachment": false, + "meta.llama4-maverick-17b-instruct-v1:0": { + "id": "meta.llama4-maverick-17b-instruct-v1:0", + "name": "Llama 4 Maverick 17B Instruct", + "family": "llama", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, "knowledge": "2024-08", - "release_date": "2024-08-14", - "last_updated": "2024-08-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5, "output": 20, "cache_read": 2.5 }, - "limit": { "context": 128000, "output": 16384 } + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 16384 + }, + "cost": { + "input": 0.24, + "output": 0.97 + } }, - "deepseek-v3": { - "id": "deepseek-v3", - "name": "DeepSeek V3", - "family": "deepseek", - "attachment": false, - "reasoning": false, + "us.anthropic.claude-sonnet-4-5-20250929-v1:0": { + "id": "us.anthropic.claude-sonnet-4-5-20250929-v1:0", + "name": "Claude Sonnet 4.5 (US)", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2024-12-26", - "last_updated": "2024-12-26", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.56, "output": 1.68, "cache_read": 0.07 }, - "limit": { "context": 128000, "output": 8192 } - }, - "gemma2-9b-it": { - "id": "gemma2-9b-it", - "name": "Google Gemma 2", - "family": "gemma", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-06", - "release_date": "2024-06-25", - "last_updated": "2024-06-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.01, "output": 0.03 }, - "limit": { "context": 8192, "output": 8192 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "gemini-3-pro-preview": { - "id": "gemini-3-pro-preview", - "name": "Google Gemini 3 Pro Preview", - "family": "gemini-pro", - "attachment": false, + "us.anthropic.claude-haiku-4-5-20251001-v1:0": { + "id": "us.anthropic.claude-haiku-4-5-20251001-v1:0", + "name": "Claude Haiku 4.5 (US)", + "family": "claude-haiku", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-11", - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 12, "cache_read": 0.19999999999999998 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } }, - "gpt-4o": { - "id": "gpt-4o", - "name": "OpenAI GPT-4o", - "family": "gpt", + "amazon.nova-micro-v1:0": { + "id": "amazon.nova-micro-v1:0", + "name": "Nova Micro", + "family": "nova-micro", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-05", - "release_date": "2024-05-13", - "last_updated": "2024-05-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2024-12-03", + "last_updated": "2024-12-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 10, "cache_read": 1.25 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.035, + "output": 0.14, + "cache_read": 0.00875 + } }, - "grok-4-fast-non-reasoning": { - "id": "grok-4-fast-non-reasoning", - "name": "xAI Grok 4 Fast Non-Reasoning", - "family": "grok", - "attachment": false, - "reasoning": false, + "global.anthropic.claude-sonnet-4-5-20250929-v1:0": { + "id": "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + "name": "Claude Sonnet 4.5 (Global)", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-09-19", - "last_updated": "2025-09-19", - "modalities": { "input": ["text", "image", "audio"], "output": ["text"] }, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.19999999999999998, "output": 0.5, "cache_read": 0.049999999999999996 }, - "limit": { "context": 2000000, "output": 2000000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "grok-4": { - "id": "grok-4", - "name": "xAI Grok 4", - "family": "grok", + "openai.gpt-oss-20b-1:0": { + "id": "openai.gpt-oss-20b-1:0", + "name": "gpt-oss-20b", + "family": "gpt-oss", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2024-07-09", - "last_updated": "2024-07-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.75 }, - "limit": { "context": 256000, "output": 256000 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.07, + "output": 0.3 + } }, - "grok-code-fast-1": { - "id": "grok-code-fast-1", - "name": "xAI Grok Code Fast 1", - "family": "grok", + "zai.glm-5": { + "id": "zai.glm-5", + "name": "GLM-5", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-08", - "release_date": "2024-08-25", - "last_updated": "2024-08-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.19999999999999998, "output": 1.5, "cache_read": 0.02 }, - "limit": { "context": 256000, "output": 10000 } + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 101376 + }, + "cost": { + "input": 1, + "output": 3.2 + } }, - "kimi-k2-0905": { - "id": "kimi-k2-0905", - "name": "Kimi K2 (09/05)", - "family": "kimi", + "qwen.qwen3-32b-v1:0": { + "id": "qwen.qwen3-32b-v1:0", + "name": "Qwen3 32B (dense)", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 2, "cache_read": 0.39999999999999997 }, - "limit": { "context": 262144, "output": 16384 } - }, - "gpt-5-mini": { - "id": "gpt-5-mini", - "name": "OpenAI GPT-5 Mini", - "family": "gpt-mini", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": false, - "knowledge": "2025-01", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.024999999999999998 }, - "limit": { "context": 400000, "output": 128000 } + "knowledge": "2024-04", + "release_date": "2025-09-18", + "last_updated": "2025-09-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 16384, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "mistral-large-2411": { - "id": "mistral-large-2411", - "name": "Mistral-Large", - "family": "mistral-large", + "deepseek.v3.2": { + "id": "deepseek.v3.2", + "name": "DeepSeek-V3.2", + "family": "deepseek", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, "knowledge": "2024-07", - "release_date": "2024-07-24", - "last_updated": "2024-07-24", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 6 }, - "limit": { "context": 128000, "output": 32768 } + "release_date": "2026-02-06", + "last_updated": "2026-02-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 81920 + }, + "cost": { + "input": 0.62, + "output": 1.85 + } }, - "ernie-4.5-21b-a3b-thinking": { - "id": "ernie-4.5-21b-a3b-thinking", - "name": "Baidu Ernie 4.5 21B A3B Thinking", - "family": "ernie", - "attachment": false, + "eu.anthropic.claude-haiku-4-5-20251001-v1:0": { + "id": "eu.anthropic.claude-haiku-4-5-20251001-v1:0", + "name": "Claude Haiku 4.5 (EU)", + "family": "claude-haiku", + "attachment": true, "reasoning": true, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-03-16", - "last_updated": "2025-03-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.07, "output": 0.28 }, - "limit": { "context": 128000, "output": 8000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } }, - "llama-guard-4": { - "id": "llama-guard-4", - "name": "Meta Llama Guard 4 12B", - "family": "llama", + "zai.glm-4.7-flash": { + "id": "zai.glm-4.7-flash", + "name": "GLM-4.7-Flash", + "family": "glm-flash", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.21, "output": 0.21 }, - "limit": { "context": 131072, "output": 1024 } + "knowledge": "2025-04", + "release_date": "2026-01-19", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 0.07, + "output": 0.4 + } }, - "claude-sonnet-4-5-20250929": { - "id": "claude-sonnet-4-5-20250929", - "name": "Anthropic: Claude Sonnet 4.5 (20250929)", - "family": "claude-sonnet", - "attachment": false, + "us.anthropic.claude-opus-4-7": { + "id": "us.anthropic.claude-opus-4-7", + "name": "Claude Opus 4.7 (US)", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.30000000000000004, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "gpt-4o-mini": { - "id": "gpt-4o-mini", - "name": "OpenAI GPT-4o-mini", - "family": "gpt-mini", + "amazon.nova-2-lite-v1:0": { + "id": "amazon.nova-2-lite-v1:0", + "name": "Nova 2 Lite", + "family": "nova", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.6, "cache_read": 0.075 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.33, + "output": 2.75 + } }, - "qwen2.5-coder-7b-fast": { - "id": "qwen2.5-coder-7b-fast", - "name": "Qwen2.5 Coder 7B fast", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": false, + "anthropic.claude-opus-4-5-20251101-v1:0": { + "id": "anthropic.claude-opus-4-5-20251101-v1:0", + "name": "Claude Opus 4.5", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-09", - "release_date": "2024-09-15", - "last_updated": "2024-09-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-03-31", + "release_date": "2025-11-24", + "last_updated": "2025-08-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.03, "output": 0.09 }, - "limit": { "context": 32000, "output": 8192 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "qwen3-30b-a3b": { - "id": "qwen3-30b-a3b", - "name": "Qwen3 30B A3B", + "qwen.qwen3-coder-480b-a35b-v1:0": { + "id": "qwen.qwen3-coder-480b-a35b-v1:0", + "name": "Qwen3 Coder 480B A35B Instruct", "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-06-01", - "last_updated": "2025-06-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.08, "output": 0.29 }, - "limit": { "context": 41000, "output": 41000 } + "knowledge": "2024-04", + "release_date": "2025-09-18", + "last_updated": "2025-09-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.22, + "output": 1.8 + } }, - "gpt-4.1": { - "id": "gpt-4.1", - "name": "OpenAI GPT-4.1", - "family": "gpt", - "attachment": false, + "amazon.nova-lite-v1:0": { + "id": "amazon.nova-lite-v1:0", + "name": "Nova Lite", + "family": "nova-lite", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2024-12-03", + "last_updated": "2024-12-03", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 300000, + "output": 8192 + }, + "cost": { + "input": 0.06, + "output": 0.24, + "cache_read": 0.015 + } }, - "llama-3.1-8b-instruct": { - "id": "llama-3.1-8b-instruct", - "name": "Meta Llama 3.1 8B Instruct", + "meta.llama3-1-8b-instruct-v1:0": { + "id": "meta.llama3-1-8b-instruct-v1:0", + "name": "Llama 3.1 8B Instruct", "family": "llama", "attachment": false, "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2024-07", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.02, "output": 0.049999999999999996 }, - "limit": { "context": 16384, "output": 16384 } - }, - "gpt-5.1-chat-latest": { - "id": "gpt-5.1-chat-latest", - "name": "OpenAI GPT-5.1 Chat", - "family": "gpt-codex", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": false, - "knowledge": "2025-01", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text", "image"], "output": ["text", "image"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.12500000000000003 }, - "limit": { "context": 128000, "output": 16384 } + "temperature": true, + "knowledge": "2023-12", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.22, + "output": 0.22 + } }, - "sonar-deep-research": { - "id": "sonar-deep-research", - "name": "Perplexity Sonar Deep Research", - "family": "sonar-deep-research", - "attachment": false, + "anthropic.claude-opus-4-7": { + "id": "anthropic.claude-opus-4-7", + "name": "Claude Opus 4.7", + "family": "claude-opus", + "attachment": true, "reasoning": true, - "tool_call": false, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-01-27", - "last_updated": "2025-01-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8 }, - "limit": { "context": 127000, "output": 4096 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "sonar": { - "id": "sonar", - "name": "Perplexity Sonar", - "family": "sonar", - "attachment": false, + "google.gemma-3-27b-it": { + "id": "google.gemma-3-27b-it", + "name": "Google Gemma 3 27B Instruct", + "family": "gemma", + "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-01-27", - "last_updated": "2025-01-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1, "output": 1 }, - "limit": { "context": 127000, "output": 4096 } + "knowledge": "2025-07", + "release_date": "2025-07-27", + "last_updated": "2025-07-27", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 8192 + }, + "cost": { + "input": 0.12, + "output": 0.2 + } }, - "gpt-oss-120b": { - "id": "gpt-oss-120b", - "name": "OpenAI GPT-OSS 120b", - "family": "gpt-oss", - "attachment": false, + "global.anthropic.claude-haiku-4-5-20251001-v1:0": { + "id": "global.anthropic.claude-haiku-4-5-20251001-v1:0", + "name": "Claude Haiku 4.5 (Global)", + "family": "claude-haiku", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-06", - "release_date": "2024-06-01", - "last_updated": "2024-06-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.04, "output": 0.16 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } }, - "llama-4-scout": { - "id": "llama-4-scout", - "name": "Meta Llama 4 Scout 17B 16E", - "family": "llama", + "google.gemma-3-4b-it": { + "id": "google.gemma-3-4b-it", + "name": "Gemma 3 4B IT", + "family": "gemma", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.08, "output": 0.3 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.04, + "output": 0.08 + } }, - "llama-prompt-guard-2-22m": { - "id": "llama-prompt-guard-2-22m", - "name": "Meta Llama Prompt Guard 2 22M", + "meta.llama4-scout-17b-instruct-v1:0": { + "id": "meta.llama4-scout-17b-instruct-v1:0", + "name": "Llama 4 Scout 17B Instruct", "family": "llama", - "attachment": false, + "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-10-01", - "last_updated": "2024-10-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.01, "output": 0.01 }, - "limit": { "context": 512, "output": 2 } + "knowledge": "2024-08", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 3500000, + "output": 16384 + }, + "cost": { + "input": 0.17, + "output": 0.66 + } }, - "qwen3-coder": { - "id": "qwen3-coder", - "name": "Qwen3 Coder 480B A35B Instruct Turbo", - "family": "qwen", + "deepseek.v3-v1:0": { + "id": "deepseek.v3-v1:0", + "name": "DeepSeek-V3.1", + "family": "deepseek", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.22, "output": 0.95 }, - "limit": { "context": 262144, "output": 16384 } - }, - "o1": { - "id": "o1", - "name": "OpenAI: o1", - "family": "o", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "knowledge": "2025-01", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 60, "cache_read": 7.5 }, - "limit": { "context": 200000, "output": 100000 } + "knowledge": "2024-07", + "release_date": "2025-09-18", + "last_updated": "2025-09-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 81920 + }, + "cost": { + "input": 0.58, + "output": 1.68 + } }, - "codex-mini-latest": { - "id": "codex-mini-latest", - "name": "OpenAI Codex Mini Latest", - "family": "gpt-codex-mini", + "mistral.magistral-small-2509": { + "id": "mistral.magistral-small-2509", + "name": "Magistral Small 1.2", + "family": "magistral", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2025-01", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.5, "output": 6, "cache_read": 0.375 }, - "limit": { "context": 200000, "output": 100000 } + "structured_output": true, + "temperature": true, + "release_date": "2025-12-02", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 40000 + }, + "cost": { + "input": 0.5, + "output": 1.5 + } }, - "o3": { - "id": "o3", - "name": "OpenAI o3", - "family": "o", + "qwen.qwen3-next-80b-a3b": { + "id": "qwen.qwen3-next-80b-a3b", + "name": "Qwen/Qwen3-Next-80B-A3B-Instruct", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, - "temperature": false, - "knowledge": "2024-06", - "release_date": "2024-06-01", - "last_updated": "2024-06-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "temperature": true, + "release_date": "2025-09-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.14, + "output": 1.4 + } }, - "grok-4-1-fast-non-reasoning": { - "id": "grok-4-1-fast-non-reasoning", - "name": "xAI Grok 4.1 Fast Non-Reasoning", - "family": "grok", + "zai.glm-4.7": { + "id": "zai.glm-4.7", + "name": "GLM-4.7", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-11", - "release_date": "2025-11-17", - "last_updated": "2025-11-17", - "modalities": { "input": ["text", "image"], "output": ["text", "image"] }, - "open_weights": false, - "cost": { "input": 0.19999999999999998, "output": 0.5, "cache_read": 0.049999999999999996 }, - "limit": { "context": 2000000, "output": 30000 } + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 2.2 + } }, - "sonar-reasoning-pro": { - "id": "sonar-reasoning-pro", - "name": "Perplexity Sonar Reasoning Pro", - "family": "sonar-reasoning", + "moonshot.kimi-k2-thinking": { + "id": "moonshot.kimi-k2-thinking", + "name": "Kimi K2 Thinking", + "family": "kimi-thinking", "attachment": false, "reasoning": true, - "tool_call": false, + "tool_call": true, + "interleaved": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-01-27", - "last_updated": "2025-01-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 8 }, - "limit": { "context": 127000, "output": 4096 } + "release_date": "2025-12-02", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.6, + "output": 2.5 + } }, - "claude-3-haiku-20240307": { - "id": "claude-3-haiku-20240307", - "name": "Anthropic: Claude 3 Haiku", - "family": "claude-haiku", - "attachment": false, - "reasoning": false, + "us.anthropic.claude-opus-4-5-20251101-v1:0": { + "id": "us.anthropic.claude-opus-4-5-20251101-v1:0", + "name": "Claude Opus 4.5 (US)", + "family": "claude-opus", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-03", - "release_date": "2024-03-07", - "last_updated": "2024-03-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-03-31", + "release_date": "2025-11-24", + "last_updated": "2025-08-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 1.25, "cache_read": 0.03, "cache_write": 0.3 }, - "limit": { "context": 200000, "output": 4096 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "llama-3.1-8b-instant": { - "id": "llama-3.1-8b-instant", - "name": "Meta Llama 3.1 8B Instant", - "family": "llama", + "mistral.ministral-3-14b-instruct": { + "id": "mistral.ministral-3-14b-instruct", + "name": "Ministral 14B 3.0", + "family": "ministral", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2024-07-01", - "last_updated": "2024-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.049999999999999996, "output": 0.08 }, - "limit": { "context": 131072, "output": 32678 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.2, + "output": 0.2 + } }, - "deepseek-reasoner": { - "id": "deepseek-reasoner", - "name": "DeepSeek Reasoner", + "deepseek.r1-v1:0": { + "id": "deepseek.r1-v1:0", + "name": "DeepSeek-R1", "family": "deepseek-thinking", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "knowledge": "2025-01", + "knowledge": "2024-07", "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "last_updated": "2025-05-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.56, "output": 1.68, "cache_read": 0.07 }, - "limit": { "context": 128000, "output": 64000 } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 1.35, + "output": 5.4 + } }, - "claude-3.5-sonnet-v2": { - "id": "claude-3.5-sonnet-v2", - "name": "Anthropic: Claude 3.5 Sonnet v2", - "family": "claude-sonnet", + "mistral.voxtral-mini-3b-2507": { + "id": "mistral.voxtral-mini-3b-2507", + "name": "Voxtral Mini 3B 2507", + "family": "mistral", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["audio", "text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.30000000000000004, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 8192 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.04, + "output": 0.04 + } }, - "llama-4-maverick": { - "id": "llama-4-maverick", - "name": "Meta Llama 4 Maverick 17B 128E", - "family": "llama", + "openai.gpt-oss-120b-1:0": { + "id": "openai.gpt-oss-120b-1:0", + "name": "gpt-oss-120b", + "family": "gpt-oss", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "grok-3-mini": { - "id": "grok-3-mini", - "name": "xAI Grok 3 Mini", - "family": "grok", + "nvidia.nemotron-nano-12b-v2": { + "id": "nvidia.nemotron-nano-12b-v2", + "name": "NVIDIA Nemotron Nano 12B v2 VL BF16", + "family": "nemotron", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-06", - "release_date": "2024-06-01", - "last_updated": "2024-06-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 0.5, "cache_read": 0.075 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.2, + "output": 0.6 + } }, - "gpt-4.1-mini-2025-04-14": { - "id": "gpt-4.1-mini-2025-04-14", - "name": "OpenAI GPT-4.1 Mini", - "family": "gpt-mini", - "attachment": false, - "reasoning": false, + "eu.anthropic.claude-opus-4-7": { + "id": "eu.anthropic.claude-opus-4-7", + "name": "Claude Opus 4.7 (EU)", + "family": "claude-opus", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.39999999999999997, "output": 1.5999999999999999, "cache_read": 0.09999999999999999 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "claude-opus-4-1": { - "id": "claude-opus-4-1", - "name": "Anthropic: Claude Opus 4.1", - "family": "claude-opus", + "minimax.minimax-m2.5": { + "id": "minimax.minimax-m2.5", + "name": "MiniMax M2.5", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-08", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 196608, + "output": 98304 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "glm-4.6": { - "id": "glm-4.6", - "name": "Zai GLM-4.6", - "family": "glm", + "meta.llama3-3-70b-instruct-v1:0": { + "id": "meta.llama3-3-70b-instruct-v1:0", + "name": "Llama 3.3 70B Instruct", + "family": "llama", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.44999999999999996, "output": 1.5 }, - "limit": { "context": 204800, "output": 131072 } + "knowledge": "2023-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.72, + "output": 0.72 + } }, - "llama-3.1-8b-instruct-turbo": { - "id": "llama-3.1-8b-instruct-turbo", - "name": "Meta Llama 3.1 8B Instruct Turbo", + "meta.llama3-1-70b-instruct-v1:0": { + "id": "meta.llama3-1-70b-instruct-v1:0", + "name": "Llama 3.1 70B Instruct", "family": "llama", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-07", + "knowledge": "2023-12", "release_date": "2024-07-23", "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.72, + "output": 0.72 + } + }, + "eu.anthropic.claude-sonnet-4-5-20250929-v1:0": { + "id": "eu.anthropic.claude-sonnet-4-5-20250929-v1:0", + "name": "Claude Sonnet 4.5 (EU)", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + "eu.anthropic.claude-opus-4-5-20251101-v1:0": { + "id": "eu.anthropic.claude-opus-4-5-20251101-v1:0", + "name": "Claude Opus 4.5 (EU)", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-11-24", + "last_updated": "2025-08-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.02, "output": 0.03 }, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } + }, + "moonshotai.kimi-k2.5": { + "id": "moonshotai.kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": true, + "temperature": true, + "release_date": "2026-02-06", + "last_updated": "2026-02-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.6, + "output": 3 + } }, - "claude-3.7-sonnet": { - "id": "claude-3.7-sonnet", - "name": "Anthropic: Claude 3.7 Sonnet", + "au.anthropic.claude-sonnet-4-6": { + "id": "au.anthropic.claude-sonnet-4-6", + "name": "AU Anthropic Claude Sonnet 4.6", "family": "claude-sonnet", - "attachment": false, - "reasoning": false, + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-02", - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-08", + "release_date": "2026-02-17", + "last_updated": "2026-02-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.30000000000000004, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 3.3, + "output": 16.5, + "cache_read": 0.33, + "cache_write": 4.125 + } }, - "gemini-2.5-pro": { - "id": "gemini-2.5-pro", - "name": "Google Gemini 2.5 Pro", - "family": "gemini-pro", + "openai.gpt-oss-safeguard-20b": { + "id": "openai.gpt-oss-safeguard-20b", + "name": "GPT OSS Safeguard 20B", + "family": "gpt-oss", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.3125, "cache_write": 1.25 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.07, + "output": 0.2 + } }, - "qwen3-235b-a22b-thinking": { - "id": "qwen3-235b-a22b-thinking", - "name": "Qwen3 235B A22B Thinking", + "qwen.qwen3-coder-30b-a3b-v1:0": { + "id": "qwen.qwen3-coder-30b-a3b-v1:0", + "name": "Qwen3 Coder 30B A3B Instruct", "family": "qwen", "attachment": false, - "reasoning": true, - "tool_call": false, + "reasoning": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-07-25", - "last_updated": "2025-07-25", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-09-18", + "last_updated": "2025-09-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 2.9000000000000004 }, - "limit": { "context": 262144, "output": 81920 } + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "claude-opus-4-1-20250805": { - "id": "claude-opus-4-1-20250805", - "name": "Anthropic: Claude Opus 4.1 (20250805)", - "family": "claude-opus", + "minimax.minimax-m2.1": { + "id": "minimax.minimax-m2.1", + "name": "MiniMax M2.1", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-08", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "claude-sonnet-4": { - "id": "claude-sonnet-4", - "name": "Anthropic: Claude Sonnet 4", - "family": "claude-sonnet", - "attachment": false, - "reasoning": true, + "qwen.qwen3-vl-235b-a22b": { + "id": "qwen.qwen3-vl-235b-a22b", + "name": "Qwen/Qwen3-VL-235B-A22B-Instruct", + "family": "qwen", + "attachment": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-05", - "release_date": "2025-05-14", - "last_updated": "2025-05-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-10-04", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.30000000000000004, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.3, + "output": 1.5 + } }, - "gemini-2.5-flash": { - "id": "gemini-2.5-flash", - "name": "Google Gemini 2.5 Flash", - "family": "gemini-flash", + "qwen.qwen3-coder-next": { + "id": "qwen.qwen3-coder-next", + "name": "Qwen3 Coder Next", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.5, "cache_read": 0.075, "cache_write": 0.3 }, - "limit": { "context": 1048576, "output": 65535 } + "release_date": "2026-02-06", + "last_updated": "2026-02-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.22, + "output": 1.8 + } }, - "sonar-pro": { - "id": "sonar-pro", - "name": "Perplexity Sonar Pro", - "family": "sonar-pro", + "nvidia.nemotron-nano-9b-v2": { + "id": "nvidia.nemotron-nano-9b-v2", + "name": "NVIDIA Nemotron Nano 9B v2", + "family": "nemotron", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-01-27", - "last_updated": "2025-01-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 200000, "output": 4096 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.06, + "output": 0.23 + } }, - "mistral-nemo": { - "id": "mistral-nemo", - "name": "Mistral Nemo", - "family": "mistral-nemo", + "mistral.mistral-large-3-675b-instruct": { + "id": "mistral.mistral-large-3-675b-instruct", + "name": "Mistral Large 3", + "family": "mistral", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 20, "output": 40 }, - "limit": { "context": 128000, "output": 16400 } + "release_date": "2025-12-02", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 8192 + }, + "cost": { + "input": 0.5, + "output": 1.5 + } }, - "qwen3-next-80b-a3b-instruct": { - "id": "qwen3-next-80b-a3b-instruct", - "name": "Qwen3 Next 80B A3B Instruct", + "qwen.qwen3-235b-a22b-2507-v1:0": { + "id": "qwen.qwen3-235b-a22b-2507-v1:0", + "name": "Qwen3 235B A22B 2507", "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.14, "output": 1.4 }, - "limit": { "context": 262000, "output": 16384 } + "knowledge": "2024-04", + "release_date": "2025-09-18", + "last_updated": "2025-09-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0.22, + "output": 0.88 + } }, - "grok-4-1-fast-reasoning": { - "id": "grok-4-1-fast-reasoning", - "name": "xAI Grok 4.1 Fast Reasoning", - "family": "grok", + "writer.palmyra-x4-v1:0": { + "id": "writer.palmyra-x4-v1:0", + "name": "Palmyra X4", + "family": "palmyra", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-11", - "release_date": "2025-11-17", - "last_updated": "2025-11-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-04-28", + "last_updated": "2025-04-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.19999999999999998, "output": 0.5, "cache_read": 0.049999999999999996 }, - "limit": { "context": 2000000, "output": 2000000 } + "limit": { + "context": 122880, + "output": 8192 + }, + "cost": { + "input": 2.5, + "output": 10 + } }, - "gpt-5.1": { - "id": "gpt-5.1", - "name": "OpenAI GPT-5.1", - "family": "gpt", - "attachment": false, - "reasoning": false, + "anthropic.claude-opus-4-1-20250805-v1:0": { + "id": "anthropic.claude-opus-4-1-20250805-v1:0", + "name": "Claude Opus 4.1", + "family": "claude-opus", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2025-01", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text", "image"], "output": ["text", "image"] }, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.12500000000000003 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "claude-3.5-haiku": { - "id": "claude-3.5-haiku", - "name": "Anthropic: Claude 3.5 Haiku", - "family": "claude-haiku", + "us.deepseek.r1-v1:0": { + "id": "us.deepseek.r1-v1:0", + "name": "DeepSeek-R1 (US)", + "family": "deepseek-thinking", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-07", + "release_date": "2025-01-20", + "last_updated": "2025-05-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.7999999999999999, "output": 4, "cache_read": 0.08, "cache_write": 1 }, - "limit": { "context": 200000, "output": 8192 } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 1.35, + "output": 5.4 + } }, - "mistral-small": { - "id": "mistral-small", - "name": "Mistral Small", - "family": "mistral-small", - "attachment": false, - "reasoning": false, - "tool_call": false, + "eu.anthropic.claude-opus-4-6-v1": { + "id": "eu.anthropic.claude-opus-4-6-v1", + "name": "Claude Opus 4.6 (EU)", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-02", - "release_date": "2024-02-26", - "last_updated": "2024-02-26", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 75, "output": 200 }, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "gpt-4.1-mini": { - "id": "gpt-4.1-mini", - "name": "OpenAI GPT-4.1 Mini", - "family": "gpt-mini", - "attachment": false, + "us.meta.llama4-maverick-17b-instruct-v1:0": { + "id": "us.meta.llama4-maverick-17b-instruct-v1:0", + "name": "Llama 4 Maverick 17B Instruct (US)", + "family": "llama", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.39999999999999997, "output": 1.5999999999999999, "cache_read": 0.09999999999999999 }, - "limit": { "context": 1047576, "output": 32768 } + "knowledge": "2024-08", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 16384 + }, + "cost": { + "input": 0.24, + "output": 0.97 + } }, - "gpt-5-chat-latest": { - "id": "gpt-5-chat-latest", - "name": "OpenAI GPT-5 Chat Latest", - "family": "gpt-codex", - "attachment": false, - "reasoning": false, + "au.anthropic.claude-haiku-4-5-20251001-v1:0": { + "id": "au.anthropic.claude-haiku-4-5-20251001-v1:0", + "name": "Claude Haiku 4.5 (AU)", + "family": "claude-haiku", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2024-09", - "release_date": "2024-09-30", - "last_updated": "2024-09-30", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.12500000000000003 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } }, - "grok-4-fast-reasoning": { - "id": "grok-4-fast-reasoning", - "name": "xAI: Grok 4 Fast Reasoning", - "family": "grok", - "attachment": false, + "jp.anthropic.claude-sonnet-4-5-20250929-v1:0": { + "id": "jp.anthropic.claude-sonnet-4-5-20250929-v1:0", + "name": "Claude Sonnet 4.5 (JP)", + "family": "claude-sonnet", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-09-01", - "last_updated": "2025-09-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.19999999999999998, "output": 0.5, "cache_read": 0.049999999999999996 }, - "limit": { "context": 2000000, "output": 2000000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "gpt-5-nano": { - "id": "gpt-5-nano", - "name": "OpenAI GPT-5 Nano", - "family": "gpt-nano", - "attachment": false, - "reasoning": false, + "anthropic.claude-sonnet-4-6": { + "id": "anthropic.claude-sonnet-4-6", + "name": "Claude Sonnet 4.6", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2025-01", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.049999999999999996, "output": 0.39999999999999997, "cache_read": 0.005 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "grok-3": { - "id": "grok-3", - "name": "xAI Grok 3", - "family": "grok", - "attachment": false, - "reasoning": false, + "jp.anthropic.claude-sonnet-4-6": { + "id": "jp.anthropic.claude-sonnet-4-6", + "name": "Claude Sonnet 4.6 (JP)", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-06", - "release_date": "2024-06-01", - "last_updated": "2024-06-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.75 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "deepseek-tng-r1t2-chimera": { - "id": "deepseek-tng-r1t2-chimera", - "name": "DeepSeek TNG R1T2 Chimera", - "family": "deepseek-thinking", - "attachment": false, - "reasoning": false, + "global.anthropic.claude-sonnet-4-6": { + "id": "global.anthropic.claude-sonnet-4-6", + "name": "Claude Sonnet 4.6 (Global)", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-07-02", - "last_updated": "2025-07-02", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 130000, "output": 163840 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "claude-haiku-4-5-20251001": { - "id": "claude-haiku-4-5-20251001", - "name": "Anthropic: Claude 4.5 Haiku (20251001)", - "family": "claude-haiku", - "attachment": false, - "reasoning": false, + "us.anthropic.claude-sonnet-4-6": { + "id": "us.anthropic.claude-sonnet-4-6", + "name": "Claude Sonnet 4.6 (US)", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-10", - "release_date": "2025-10-01", - "last_updated": "2025-10-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.09999999999999999, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 8192 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "claude-4.5-sonnet": { - "id": "claude-4.5-sonnet", - "name": "Anthropic: Claude Sonnet 4.5", - "family": "claude-sonnet", - "attachment": false, + "global.anthropic.claude-opus-4-6-v1": { + "id": "global.anthropic.claude-opus-4-6-v1", + "name": "Claude Opus 4.6 (Global)", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.30000000000000004, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "o3-pro": { - "id": "o3-pro", - "name": "OpenAI o3 Pro", - "family": "o-pro", - "attachment": false, - "reasoning": false, + "us.anthropic.claude-opus-4-6-v1": { + "id": "us.anthropic.claude-opus-4-6-v1", + "name": "Claude Opus 4.6 (US)", + "family": "claude-opus", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2024-06", - "release_date": "2024-06-01", - "last_updated": "2024-06-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 20, "output": 80 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "gpt-oss-20b": { - "id": "gpt-oss-20b", - "name": "OpenAI GPT-OSS 20b", - "family": "gpt-oss", - "attachment": false, + "us.anthropic.claude-opus-4-1-20250805-v1:0": { + "id": "us.anthropic.claude-opus-4-1-20250805-v1:0", + "name": "Claude Opus 4.1 (US)", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-06", - "release_date": "2024-06-01", - "last_updated": "2024-06-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.049999999999999996, "output": 0.19999999999999998 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "qwen3-32b": { - "id": "qwen3-32b", - "name": "Qwen3 32B", - "family": "qwen", - "attachment": false, + "au.anthropic.claude-sonnet-4-5-20250929-v1:0": { + "id": "au.anthropic.claude-sonnet-4-5-20250929-v1:0", + "name": "Claude Sonnet 4.5 (AU)", + "family": "claude-sonnet", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04-28", - "last_updated": "2025-04-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.29, "output": 0.59 }, - "limit": { "context": 131072, "output": 40960 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "gpt-4.1-nano": { - "id": "gpt-4.1-nano", - "name": "OpenAI GPT-4.1 Nano", - "family": "gpt-nano", - "attachment": false, - "reasoning": false, + "eu.anthropic.claude-sonnet-4-6": { + "id": "eu.anthropic.claude-sonnet-4-6", + "name": "Claude Sonnet 4.6 (EU)", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.09999999999999999, "output": 0.39999999999999997, "cache_read": 0.024999999999999998 }, - "limit": { "context": 1047576, "output": 32768 } - }, - "gemma-3-12b-it": { - "id": "gemma-3-12b-it", - "name": "Google Gemma 3 12B", - "family": "gemma", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-12", - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.049999999999999996, "output": 0.09999999999999999 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "claude-4.5-haiku": { - "id": "claude-4.5-haiku", - "name": "Anthropic: Claude 4.5 Haiku", - "family": "claude-haiku", - "attachment": false, + "us.meta.llama4-scout-17b-instruct-v1:0": { + "id": "us.meta.llama4-scout-17b-instruct-v1:0", + "name": "Llama 4 Scout 17B Instruct (US)", + "family": "llama", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-10", - "release_date": "2025-10-01", - "last_updated": "2025-10-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.09999999999999999, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 8192 } + "knowledge": "2024-08", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 3500000, + "output": 16384 + }, + "cost": { + "input": 0.17, + "output": 0.66 + } }, - "deepseek-v3.2": { - "id": "deepseek-v3.2", - "name": "DeepSeek V3.2", - "family": "deepseek", - "attachment": false, - "reasoning": false, + "anthropic.claude-opus-4-6-v1": { + "id": "anthropic.claude-opus-4-6-v1", + "name": "Claude Opus 4.6", + "family": "claude-opus", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-09-22", - "last_updated": "2025-09-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.27, "output": 0.41 }, - "limit": { "context": 163840, "output": 65536 } - }, - "llama-prompt-guard-2-86m": { - "id": "llama-prompt-guard-2-86m", - "name": "Meta Llama Prompt Guard 2 86M", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-10-01", - "last_updated": "2024-10-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.01, "output": 0.01 }, - "limit": { "context": 512, "output": 2 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "gpt-5-codex": { - "id": "gpt-5-codex", - "name": "OpenAI: GPT-5 Codex", - "family": "gpt-codex", - "attachment": false, - "reasoning": false, + "jp.anthropic.claude-opus-4-7": { + "id": "jp.anthropic.claude-opus-4-7", + "name": "Claude Opus 4.7 (JP)", + "family": "claude-opus", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": false, - "knowledge": "2025-01", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.12500000000000003 }, - "limit": { "context": 400000, "output": 128000 } - }, - "o4-mini": { - "id": "o4-mini", - "name": "OpenAI o4 Mini", - "family": "o-mini", + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } + } + } + }, + "the-grid-ai": { + "id": "the-grid-ai", + "env": ["THEGRIDAI_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.thegrid.ai/v1", + "name": "The Grid AI", + "doc": "https://thegrid.ai/docs", + "models": { + "text-prime": { + "id": "text-prime", + "name": "Text Prime", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2024-06", - "release_date": "2024-06-01", - "last_updated": "2024-06-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "temperature": true, + "release_date": "2026-02-26", + "last_updated": "2026-02-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.275 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 128000, + "output": 30000 + }, + "status": "beta" }, - "gpt-5.1-codex": { - "id": "gpt-5.1-codex", - "name": "OpenAI: GPT-5.1 Codex", - "family": "gpt-codex", + "text-standard": { + "id": "text-standard", + "name": "Text Standard", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2025-01", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text", "image"], "output": ["text", "image"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.12500000000000003 }, - "limit": { "context": 400000, "output": 128000 } - }, - "o1-mini": { - "id": "o1-mini", - "name": "OpenAI: o1-mini", - "family": "o-mini", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "knowledge": "2025-01", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "temperature": true, + "release_date": "2026-02-26", + "last_updated": "2026-02-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.55 }, - "limit": { "context": 128000, "output": 65536 } + "limit": { + "context": 128000, + "output": 16000 + }, + "status": "beta" }, - "claude-opus-4": { - "id": "claude-opus-4", - "name": "Anthropic: Claude Opus 4", - "family": "claude-opus", + "text-max": { + "id": "text-max", + "name": "Text Max", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-05", - "release_date": "2025-05-14", - "last_updated": "2025-05-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-03-24", + "last_updated": "2026-03-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "status": "beta" } } }, - "minimax": { - "id": "minimax", - "env": ["MINIMAX_API_KEY"], - "npm": "@ai-sdk/anthropic", - "api": "https://api.minimax.io/anthropic/v1", - "name": "MiniMax (minimax.io)", - "doc": "https://platform.minimax.io/docs/guides/quickstart", + "baseten": { + "id": "baseten", + "env": ["BASETEN_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://inference.baseten.co/v1", + "name": "Baseten", + "doc": "https://docs.baseten.co/development/model-apis/overview", "models": { - "MiniMax-M2.7": { - "id": "MiniMax-M2.7", - "name": "MiniMax-M2.7", - "family": "minimax", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.06, "cache_write": 0.375 }, - "limit": { "context": 204800, "output": 131072 } - }, - "MiniMax-M2.1": { - "id": "MiniMax-M2.1", - "name": "MiniMax-M2.1", - "family": "minimax", + "zai-org/GLM-4.7": { + "id": "zai-org/GLM-4.7", + "name": "GLM-4.7", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 2.2 + } }, - "MiniMax-M2.5": { - "id": "MiniMax-M2.5", - "name": "MiniMax-M2.5", - "family": "minimax", + "zai-org/GLM-5": { + "id": "zai-org/GLM-5", + "name": "GLM-5", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, + "knowledge": "2026-01", "release_date": "2026-02-12", "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.03, "cache_write": 0.375 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 202752, + "output": 131072 + }, + "cost": { + "input": 0.95, + "output": 3.15 + } }, - "MiniMax-M2": { - "id": "MiniMax-M2", - "name": "MiniMax-M2", - "family": "minimax", + "zai-org/GLM-4.6": { + "id": "zai-org/GLM-4.6", + "name": "GLM 4.6", + "family": "glm", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2025-10-27", - "last_updated": "2025-10-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2025-09-16", + "last_updated": "2025-09-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 196608, "output": 128000 } + "limit": { + "context": 200000, + "output": 200000 + }, + "cost": { + "input": 0.6, + "output": 2.2 + } }, - "MiniMax-M2.5-highspeed": { - "id": "MiniMax-M2.5-highspeed", - "name": "MiniMax-M2.5-highspeed", - "family": "minimax", + "nvidia/Nemotron-120B-A12B": { + "id": "nvidia/Nemotron-120B-A12B", + "name": "Nemotron 3 Super", + "family": "nemotron", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2026-02-13", - "last_updated": "2026-02-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2026-02", + "release_date": "2026-03-11", + "last_updated": "2026-03-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.4, "cache_read": 0.06, "cache_write": 0.375 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 262144, + "output": 32678 + }, + "cost": { + "input": 0.3, + "output": 0.75 + } }, - "MiniMax-M2.7-highspeed": { - "id": "MiniMax-M2.7-highspeed", - "name": "MiniMax-M2.7-highspeed", - "family": "minimax", + "deepseek-ai/DeepSeek-V3.1": { + "id": "deepseek-ai/DeepSeek-V3.1", + "name": "DeepSeek V3.1", + "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-08-25", + "last_updated": "2025-08-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.4, "cache_read": 0.06, "cache_write": 0.375 }, - "limit": { "context": 204800, "output": 131072 } - } - } - }, - "alibaba-coding-plan-cn": { - "id": "alibaba-coding-plan-cn", - "env": ["ALIBABA_CODING_PLAN_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://coding.dashscope.aliyuncs.com/v1", - "name": "Alibaba Coding Plan (China)", - "doc": "https://help.aliyun.com/zh/model-studio/coding-plan", - "models": { - "qwen3.5-plus": { - "id": "qwen3.5-plus", - "name": "Qwen3.5 Plus", - "family": "qwen", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-02-16", - "last_updated": "2026-02-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 1000000, "output": 65536 } + "limit": { + "context": 164000, + "output": 131000 + }, + "cost": { + "input": 0.5, + "output": 1.5 + } }, - "qwen3-coder-next": { - "id": "qwen3-coder-next", - "name": "Qwen3 Coder Next", - "family": "qwen", + "deepseek-ai/DeepSeek-V3-0324": { + "id": "deepseek-ai/DeepSeek-V3-0324", + "name": "DeepSeek V3 0324", + "family": "deepseek", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-02-03", - "last_updated": "2026-02-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2025-03-24", + "last_updated": "2025-03-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 164000, + "output": 131000 + }, + "cost": { + "input": 0.77, + "output": 0.77 + } }, - "kimi-k2.5": { - "id": "kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi", - "attachment": true, + "deepseek-ai/DeepSeek-V3.2": { + "id": "deepseek-ai/DeepSeek-V3.2", + "name": "DeepSeek V3.2", + "family": "deepseek", + "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-10", + "release_date": "2025-12-01", + "last_updated": "2026-03-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 262144, "output": 32768 } + "limit": { + "context": 163800, + "output": 131100 + }, + "status": "deprecated", + "cost": { + "input": 0.3, + "output": 0.45 + } }, - "MiniMax-M2.5": { - "id": "MiniMax-M2.5", - "name": "MiniMax-M2.5", - "family": "minimax", + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "name": "GPT OSS 120B", + "family": "gpt-oss", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-08", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 196608, "output": 24576 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.1, + "output": 0.5 + } }, - "glm-5": { - "id": "glm-5", - "name": "GLM-5", - "family": "glm", + "moonshotai/Kimi-K2-Thinking": { + "id": "moonshotai/Kimi-K2-Thinking", + "name": "Kimi K2 Thinking", + "family": "kimi-thinking", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 202752, "output": 16384 } - }, - "qwen3-coder-plus": { - "id": "qwen3-coder-plus", - "name": "Qwen3 Coder Plus", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-08", + "release_date": "2025-11-06", + "last_updated": "2026-03-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 1000000, "output": 65536 } + "limit": { + "context": 262144, + "output": 262144 + }, + "status": "deprecated", + "cost": { + "input": 0.6, + "output": 2.5 + } }, - "glm-4.7": { - "id": "glm-4.7", - "name": "GLM-4.7", - "family": "glm", - "attachment": false, + "moonshotai/Kimi-K2.6": { + "id": "moonshotai/Kimi-K2.6", + "name": "Kimi K2.6", + "family": "kimi-k2.6", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 202752, "output": 16384 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.95, + "output": 4, + "cache_read": 0.16 + } }, - "qwen3-max-2026-01-23": { - "id": "qwen3-max-2026-01-23", - "name": "Qwen3 Max", - "family": "qwen", + "moonshotai/Kimi-K2-Instruct-0905": { + "id": "moonshotai/Kimi-K2-Instruct-0905", + "name": "Kimi K2 Instruct 0905", + "family": "kimi", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-01-23", - "last_updated": "2026-01-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 262144, "output": 32768 } - } - } - }, - "deepinfra": { - "id": "deepinfra", - "env": ["DEEPINFRA_API_KEY"], - "npm": "@ai-sdk/deepinfra", - "name": "Deep Infra", - "doc": "https://deepinfra.com/models", - "models": { - "openai/gpt-oss-120b": { - "id": "openai/gpt-oss-120b", - "name": "GPT OSS 120B", - "family": "gpt-oss", - "attachment": false, + "knowledge": "2025-08", + "release_date": "2025-09-05", + "last_updated": "2026-03-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "status": "deprecated", + "cost": { + "input": 0.6, + "output": 2.5 + } + }, + "moonshotai/Kimi-K2.5": { + "id": "moonshotai/Kimi-K2.5", + "name": "Kimi K2.5", + "family": "kimi", + "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-12", + "release_date": "2026-01-30", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.05, "output": 0.24 }, - "limit": { "context": 131072, "output": 16384 } + "limit": { + "context": 262144, + "output": 8192 + }, + "cost": { + "input": 0.6, + "output": 3 + } }, - "openai/gpt-oss-20b": { - "id": "openai/gpt-oss-20b", - "name": "GPT OSS 20B", - "family": "gpt-oss", + "MiniMaxAI/MiniMax-M2.5": { + "id": "MiniMaxAI/MiniMax-M2.5", + "name": "MiniMax-M2.5", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2026-01", + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.03, "output": 0.14 }, - "limit": { "context": 131072, "output": 16384 } + "limit": { + "context": 204000, + "output": 204000 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "anthropic/claude-4-opus": { - "id": "anthropic/claude-4-opus", - "name": "Claude Opus 4", - "family": "claude-opus", + "deepseek-ai/DeepSeek-V4-Pro": { + "id": "deepseek-ai/DeepSeek-V4-Pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 1.74, + "output": 3.48, + "cache_read": 0.15 + } + } + } + }, + "frogbot": { + "id": "frogbot", + "env": ["FROGBOT_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://app.frogbot.ai/api/v1", + "name": "FrogBot", + "doc": "https://docs.frogbot.ai", + "models": { + "grok-4-1-fast-reasoning": { + "id": "grok-4-1-fast-reasoning", + "name": "Grok 4.1 Fast (Reasoning)", + "family": "grok", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-06-12", - "last_updated": "2025-06-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-11", + "release_date": "2025-11-25", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 16.5, "output": 82.5 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 2000000, + "output": 128000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "anthropic/claude-3-7-sonnet-latest": { - "id": "anthropic/claude-3-7-sonnet-latest", - "name": "Claude Sonnet 3.7 (Latest)", - "family": "claude-sonnet", + "claude-haiku-4-5": { + "id": "claude-haiku-4-5", + "name": "Claude Haiku 4.5", + "family": "claude-haiku", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10-31", - "release_date": "2025-03-13", - "last_updated": "2025-03-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3.3, "output": 16.5, "cache_read": 0.33 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } }, - "zai-org/GLM-4.7": { - "id": "zai-org/GLM-4.7", - "name": "GLM-4.7", - "family": "glm", + "kimi-k2.5": { + "id": "kimi-k2.5", + "name": "Kimi-K2.5", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.43, "output": 1.75, "cache_read": 0.08 }, - "limit": { "context": 202752, "output": 16384 } + "release_date": "1970-01-01", + "last_updated": "1970-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 128000 + }, + "cost": { + "input": 0.6, + "output": 3, + "cache_read": 0.1 + } }, - "zai-org/GLM-4.6V": { - "id": "zai-org/GLM-4.6V", - "name": "GLM-4.6V", - "family": "glm", + "claude-sonnet-4-6": { + "id": "claude-sonnet-4-6", + "name": "Claude Sonnet 4.6", + "family": "claude-sonnet", "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 0.9 }, - "limit": { "context": 204800, "output": 131072 } + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-02-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "zai-org/GLM-4.7-Flash": { - "id": "zai-org/GLM-4.7-Flash", - "name": "GLM-4.7-Flash", - "family": "glm-flash", - "attachment": false, + "gemini-3-flash-preview": { + "id": "gemini-3-flash-preview", + "name": "Gemini 3 Flash Preview", + "family": "gemini-flash", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-01-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.06, "output": 0.4 }, - "limit": { "context": 202752, "output": 16384 } + "knowledge": "2025-01", + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.05 + } }, - "zai-org/GLM-4.5": { - "id": "zai-org/GLM-4.5", - "name": "GLM-4.5", - "family": "glm", - "attachment": false, - "reasoning": false, + "claude-opus-4-7": { + "id": "claude-opus-4-7", + "name": "Claude Opus 4.7", + "family": "claude-opus", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.2 }, - "limit": { "context": 131072, "output": 98304 }, - "status": "deprecated" + "temperature": false, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "zai-org/GLM-5": { - "id": "zai-org/GLM-5", - "name": "GLM-5", + "zai-glm-5-1": { + "id": "zai-glm-5-1", + "name": "Z.AI GLM-5.1", "family": "glm", - "attachment": false, - "reasoning": true, + "attachment": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2025-12", - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-01-20", + "last_updated": "2025-02-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.8, "output": 2.56, "cache_read": 0.16 }, - "limit": { "context": 202752, "output": 16384 } + "limit": { + "context": 198000, + "output": 8192 + }, + "cost": { + "input": 1.4, + "output": 4.4, + "cache_read": 0.26 + } }, - "zai-org/GLM-4.6": { - "id": "zai-org/GLM-4.6", - "name": "GLM-4.6", - "family": "glm", - "attachment": false, + "gemini-2.5-pro": { + "id": "gemini-2.5-pro", + "name": "Gemini 2.5 Pro", + "family": "gemini-pro", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.43, "output": 1.74, "cache_read": 0.08 }, - "limit": { "context": 204800, "output": 131072 } + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.31 + } }, - "meta-llama/Llama-4-Scout-17B-16E-Instruct": { - "id": "meta-llama/Llama-4-Scout-17B-16E-Instruct", - "name": "Llama 4 Scout 17B", - "family": "llama", - "attachment": false, + "grok-4-1-fast-non-reasoning": { + "id": "grok-4-1-fast-non-reasoning", + "name": "Grok 4.1 Fast (Non-Reasoning)", + "family": "grok", + "attachment": true, "reasoning": false, "tool_call": true, - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.08, "output": 0.3 }, - "limit": { "context": 10000000, "output": 16384 } + "temperature": true, + "knowledge": "2025-11", + "release_date": "2025-11-25", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 128000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "meta-llama/Llama-3.1-8B-Instruct": { - "id": "meta-llama/Llama-3.1-8B-Instruct", - "name": "Llama 3.1 8B", - "family": "llama", - "attachment": false, - "reasoning": false, + "gpt-5-4-nano": { + "id": "gpt-5-4-nano", + "name": "GPT-5.4 Nano", + "family": "gpt-nano", + "attachment": true, + "reasoning": true, "tool_call": true, - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.02, "output": 0.05 }, - "limit": { "context": 131072, "output": 16384 } + "structured_output": true, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.2, + "output": 1.25, + "cache_read": 0.02 + } }, - "meta-llama/Llama-3.1-8B-Instruct-Turbo": { - "id": "meta-llama/Llama-3.1-8B-Instruct-Turbo", - "name": "Llama 3.1 8B Turbo", - "family": "llama", - "attachment": false, - "reasoning": false, + "gemini-2.5-flash": { + "id": "gemini-2.5-flash", + "name": "Gemini 2.5 Flash", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, "tool_call": true, - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.02, "output": 0.03 }, - "limit": { "context": 131072, "output": 16384 } + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-07-17", + "last_updated": "2025-07-17", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.075 + } }, - "meta-llama/Llama-3.1-70B-Instruct-Turbo": { - "id": "meta-llama/Llama-3.1-70B-Instruct-Turbo", - "name": "Llama 3.1 70B Turbo", - "family": "llama", + "grok-code-fast-1": { + "id": "grok-code-fast-1", + "name": "Grok 4.1 Fast (Reasoning)", + "family": "grok", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.4, "output": 0.4 }, - "limit": { "context": 131072, "output": 16384 } + "temperature": true, + "knowledge": "2023-10", + "release_date": "2025-08-28", + "last_updated": "2025-08-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 128000 + }, + "cost": { + "input": 0.2, + "output": 1.5, + "cache_read": 0.02 + } }, - "meta-llama/Llama-3.1-70B-Instruct": { - "id": "meta-llama/Llama-3.1-70B-Instruct", - "name": "Llama 3.1 70B", - "family": "llama", - "attachment": false, - "reasoning": false, + "gpt-5-5": { + "id": "gpt-5-5", + "name": "GPT-5.5", + "family": "gpt", + "attachment": true, + "reasoning": true, "tool_call": true, - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.4, "output": 0.4 }, - "limit": { "context": 131072, "output": 16384 } + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 272000, + "output": 128000 + }, + "cost": { + "input": 2.5, + "output": 15, + "cache_read": 0.25 + } }, - "meta-llama/Llama-3.3-70B-Instruct-Turbo": { - "id": "meta-llama/Llama-3.3-70B-Instruct-Turbo", - "name": "Llama 3.3 70B Turbo", - "family": "llama", - "attachment": false, - "reasoning": false, + "grok-4-3": { + "id": "grok-4-3", + "name": "Grok 4.3", + "family": "grok", + "attachment": true, + "reasoning": true, "tool_call": true, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.32 }, - "limit": { "context": 131072, "output": 16384 } + "structured_output": true, + "temperature": true, + "knowledge": "2024-11", + "release_date": "2026-04-30", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 2.5, + "cache_read": 0.2 + } }, - "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8": { - "id": "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8", - "name": "Llama 4 Maverick 17B FP8", - "family": "llama", - "attachment": false, - "reasoning": false, + "gpt-5-4-mini": { + "id": "gpt-5-4-mini", + "name": "GPT-5.4 Mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": true, "tool_call": true, - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 1000000, "output": 16384 } + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.75, + "output": 4.5, + "cache_read": 0.075 + } }, - "MiniMaxAI/MiniMax-M2.1": { - "id": "MiniMaxAI/MiniMax-M2.1", - "name": "MiniMax M2.1", - "attachment": false, + "claude-opus-4-6": { + "id": "claude-opus-4-6", + "name": "Claude Opus 4.6", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.28, "output": 1.2 }, - "limit": { "context": 196608, "output": 196608 } + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "MiniMaxAI/MiniMax-M2.5": { - "id": "MiniMaxAI/MiniMax-M2.5", - "name": "MiniMax M2.5", - "family": "minimax", - "attachment": false, - "reasoning": true, + "deepseek-v4-pro": { + "id": "deepseek-v4-pro", + "name": "DeepSeek v4 Pro", + "family": "deepseek", + "attachment": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2025-06", - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.27, "output": 0.95, "cache_read": 0.03, "cache_write": 0.375 }, - "limit": { "context": 204800, "output": 131072 } + "knowledge": "2026-01", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 1.74, + "output": 3.48, + "cache_read": 0.14 + } }, - "MiniMaxAI/MiniMax-M2": { - "id": "MiniMaxAI/MiniMax-M2", - "name": "MiniMax M2", - "family": "minimax", + "gpt-oss-20b": { + "id": "gpt-oss-20b", + "name": "GPT OSS 20B", + "family": "gpt-oss", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "1970-01-01", + "last_updated": "1970-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.254, "output": 1.02 }, - "limit": { "context": 262144, "output": 32768 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.07, + "output": 0.2 + } }, - "deepseek-ai/DeepSeek-R1-0528": { - "id": "deepseek-ai/DeepSeek-R1-0528", - "name": "DeepSeek-R1-0528", - "attachment": false, + "qwen-3-6-plus": { + "id": "qwen-3-6-plus", + "name": "Qwen 3.6 Plus", + "family": "qwen", + "attachment": true, "reasoning": true, - "tool_call": false, - "interleaved": { "field": "reasoning_content" }, + "tool_call": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-05-28", - "last_updated": "2025-05-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-02", + "last_updated": "2026-04-03", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 2.15, "cache_read": 0.35 }, - "limit": { "context": 163840, "output": 64000 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.1 + } }, - "deepseek-ai/DeepSeek-V3.2": { - "id": "deepseek-ai/DeepSeek-V3.2", - "name": "DeepSeek-V3.2", - "attachment": false, - "reasoning": true, + "minimax-m2-7": { + "id": "minimax-m2-7", + "name": "MiniMax-M2.7", + "family": "minimax", + "attachment": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-12-02", - "last_updated": "2025-12-02", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-09", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.26, "output": 0.38, "cache_read": 0.13 }, - "limit": { "context": 163840, "output": 64000 } + "limit": { + "context": 192000, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06 + } }, - "Qwen/Qwen3-Coder-480B-A35B-Instruct-Turbo": { - "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct-Turbo", - "name": "Qwen3 Coder 480B A35B Instruct Turbo", - "family": "qwen", - "attachment": false, + "minimax-m2-5": { + "id": "minimax-m2-5", + "name": "MiniMax-M2.5", + "family": "minimax", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 262144, "output": 66536 } + "knowledge": "2024-09", + "release_date": "2025-01-15", + "last_updated": "2025-02-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 192000, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03 + } }, - "Qwen/Qwen3-Coder-480B-A35B-Instruct": { - "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct", - "name": "Qwen3 Coder 480B A35B Instruct", - "family": "qwen", - "attachment": false, + "gpt-4o": { + "id": "gpt-4o", + "name": "GPT-4o", + "family": "gpt", + "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.4, "output": 1.6 }, - "limit": { "context": 262144, "output": 66536 } + "knowledge": "2023-09", + "release_date": "2024-05-13", + "last_updated": "2024-08-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10, + "cache_read": 1.25 + } }, - "moonshotai/Kimi-K2-Instruct": { - "id": "moonshotai/Kimi-K2-Instruct", - "name": "Kimi K2", - "family": "kimi", + "gpt-oss-120b": { + "id": "gpt-oss-120b", + "name": "GPT OSS 120B", + "family": "gpt-oss", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-07-11", - "last_updated": "2025-07-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "1970-01-01", + "last_updated": "1970-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.5, "output": 2 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "moonshotai/Kimi-K2.5": { - "id": "moonshotai/Kimi-K2.5", - "name": "Kimi K2.5", - "family": "kimi", + "gemini-3-1-pro-preview": { + "id": "gemini-3-1-pro-preview", + "name": "Gemini 3.1 Pro Preview", + "family": "gemini-pro", "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.5, "output": 2.8 }, - "limit": { "context": 262144, "output": 32768 } + "knowledge": "2026-01", + "release_date": "2026-02-18", + "last_updated": "2026-02-18", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2 + } }, - "moonshotai/Kimi-K2-Instruct-0905": { - "id": "moonshotai/Kimi-K2-Instruct-0905", - "name": "Kimi K2 0905", - "family": "kimi", + "kimi-k2-6": { + "id": "kimi-k2-6", + "name": "Kimi-K2.6", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.4, "output": 2, "cache_read": 0.15 }, - "limit": { "context": 262144, "output": 262144 } + "release_date": "1970-01-01", + "last_updated": "1970-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 128000 + }, + "cost": { + "input": 0.95, + "output": 4, + "cache_read": 0.16 + } }, - "moonshotai/Kimi-K2-Thinking": { - "id": "moonshotai/Kimi-K2-Thinking", - "name": "Kimi K2 Thinking", - "family": "kimi-thinking", - "attachment": false, + "gpt-5-3-codex": { + "id": "gpt-5-3-codex", + "name": "GPT-5.3 Codex", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-11-06", - "last_updated": "2025-11-07", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.47, "output": 2 }, - "limit": { "context": 131072, "output": 32768 } + "temperature": false, + "knowledge": "2026-01-31", + "release_date": "2026-02-15", + "last_updated": "2026-02-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } } } }, - "xiaomi": { - "id": "xiaomi", - "env": ["XIAOMI_API_KEY"], + "zhipuai-coding-plan": { + "id": "zhipuai-coding-plan", + "env": ["ZHIPU_API_KEY"], "npm": "@ai-sdk/openai-compatible", - "api": "https://api.xiaomimimo.com/v1", - "name": "Xiaomi", - "doc": "https://platform.xiaomimimo.com/#/docs", + "api": "https://open.bigmodel.cn/api/coding/paas/v4", + "name": "Zhipu AI Coding Plan", + "doc": "https://docs.bigmodel.cn/cn/coding-plan/overview", "models": { - "mimo-v2-pro": { - "id": "mimo-v2-pro", - "name": "MiMo-V2-Pro", - "family": "mimo", + "glm-5v-turbo": { + "id": "glm-5v-turbo", + "name": "GLM-5V-Turbo", + "family": "glm", "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-12", - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1, "output": 3, "cache_read": 0.2 }, - "limit": { "context": 1000000, "output": 128000 } + "release_date": "2026-04-01", + "last_updated": "2026-04-01", + "modalities": { + "input": ["text", "image", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "mimo-v2-omni": { - "id": "mimo-v2-omni", - "name": "MiMo-V2-Omni", - "family": "mimo", - "attachment": true, + "glm-5.1": { + "id": "glm-5.1", + "name": "GLM-5.1", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "release_date": "2026-03-27", + "last_updated": "2026-03-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } + }, + "glm-5-turbo": { + "id": "glm-5-turbo", + "name": "GLM-5-Turbo", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "release_date": "2026-03-16", + "last_updated": "2026-03-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } + }, + "glm-4.5-air": { + "id": "glm-4.5-air", + "name": "GLM-4.5-Air", + "family": "glm-air", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.4, "output": 2, "cache_read": 0.08 }, - "limit": { "context": 256000, "output": 128000 } + "limit": { + "context": 131072, + "output": 98304 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "mimo-v2-flash": { - "id": "mimo-v2-flash", - "name": "MiMo-V2-Flash", - "family": "mimo", + "glm-4.7": { + "id": "glm-4.7", + "name": "GLM-4.7", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-12-01", - "release_date": "2025-12-16", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.3, "cache_read": 0.01 }, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } } } }, - "amazon-bedrock": { - "id": "amazon-bedrock", - "env": ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION", "AWS_BEARER_TOKEN_BEDROCK"], - "npm": "@ai-sdk/amazon-bedrock", - "name": "Amazon Bedrock", - "doc": "https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html", + "alibaba-coding-plan": { + "id": "alibaba-coding-plan", + "env": ["ALIBABA_CODING_PLAN_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://coding-intl.dashscope.aliyuncs.com/v1", + "name": "Alibaba Coding Plan", + "doc": "https://www.alibabacloud.com/help/en/model-studio/coding-plan", "models": { - "google.gemma-3-27b-it": { - "id": "google.gemma-3-27b-it", - "name": "Google Gemma 3 27B Instruct", - "family": "gemma", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-07-27", - "last_updated": "2025-07-27", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.12, "output": 0.2 }, - "limit": { "context": 202752, "output": 8192 } - }, - "qwen.qwen3-coder-480b-a35b-v1:0": { - "id": "qwen.qwen3-coder-480b-a35b-v1:0", - "name": "Qwen3 Coder 480B A35B Instruct", + "qwen3-coder-plus": { + "id": "qwen3-coder-plus", + "name": "Qwen3 Coder Plus", "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-09-18", - "last_updated": "2025-09-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.22, "output": 1.8 }, - "limit": { "context": 131072, "output": 65536 } + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "moonshotai.kimi-k2.5": { - "id": "moonshotai.kimi-k2.5", + "kimi-k2.5": { + "id": "kimi-k2.5", "name": "Kimi K2.5", "family": "kimi", - "attachment": false, + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": true, - "temperature": true, - "release_date": "2026-02-06", - "last_updated": "2026-02-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 3 }, - "limit": { "context": 256000, "output": 256000 } - }, - "meta.llama3-1-405b-instruct-v1:0": { - "id": "meta.llama3-1-405b-instruct-v1:0", - "name": "Llama 3.1 405B Instruct", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 2.4, "output": 2.4 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "deepseek.v3.2": { - "id": "deepseek.v3.2", - "name": "DeepSeek-V3.2", - "family": "deepseek", + "glm-4.7": { + "id": "glm-4.7", + "name": "GLM-4.7", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-07", - "release_date": "2026-02-06", - "last_updated": "2026-02-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.62, "output": 1.85 }, - "limit": { "context": 163840, "output": 81920 } + "limit": { + "context": 202752, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "global.anthropic.claude-sonnet-4-6": { - "id": "global.anthropic.claude-sonnet-4-6", - "name": "Claude Sonnet 4.6 (Global)", - "family": "claude-sonnet", - "attachment": true, + "glm-5": { + "id": "glm-5", + "name": "GLM-5", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-08", - "release_date": "2026-02-17", - "last_updated": "2026-03-18", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 1000000, "output": 64000 } + "limit": { + "context": 202752, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "nvidia.nemotron-super-3-120b": { - "id": "nvidia.nemotron-super-3-120b", - "name": "NVIDIA Nemotron 3 Super 120B A12B", - "family": "nemotron", + "MiniMax-M2.5": { + "id": "MiniMax-M2.5", + "name": "MiniMax-M2.5", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2026-03-11", - "last_updated": "2026-03-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.65 }, - "limit": { "context": 262144, "output": 131072 } + "limit": { + "context": 196608, + "input": 196601, + "output": 24576 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "minimax.minimax-m2.1": { - "id": "minimax.minimax-m2.1", - "name": "MiniMax M2.1", - "family": "minimax", + "qwen3.6-plus": { + "id": "qwen3.6-plus", + "name": "Qwen3.6 Plus", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": false, "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 204800, "output": 131072 } + "knowledge": "2025-04", + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "us.anthropic.claude-sonnet-4-20250514-v1:0": { - "id": "us.anthropic.claude-sonnet-4-20250514-v1:0", - "name": "Claude Sonnet 4 (US)", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, + "qwen3-max-2026-01-23": { + "id": "qwen3-max-2026-01-23", + "name": "Qwen3 Max", + "family": "qwen", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2026-01-23", + "last_updated": "2026-01-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "meta.llama3-3-70b-instruct-v1:0": { - "id": "meta.llama3-3-70b-instruct-v1:0", - "name": "Llama 3.3 70B Instruct", - "family": "llama", + "qwen3-coder-next": { + "id": "qwen3-coder-next", + "name": "Qwen3 Coder Next", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-03", + "last_updated": "2026-02-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.72, "output": 0.72 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "google.gemma-3-12b-it": { - "id": "google.gemma-3-12b-it", - "name": "Google Gemma 3 12B", - "family": "gemma", + "qwen3.5-plus": { + "id": "qwen3.5-plus", + "name": "Qwen3.5 Plus", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-12", - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.049999999999999996, "output": 0.09999999999999999 }, - "limit": { "context": 131072, "output": 8192 } - }, - "us.anthropic.claude-opus-4-1-20250805-v1:0": { - "id": "us.anthropic.claude-opus-4-1-20250805-v1:0", - "name": "Claude Opus 4.1 (US)", - "family": "claude-opus", - "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2026-02-16", + "last_updated": "2026-02-16", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } - }, - "anthropic.claude-haiku-4-5-20251001-v1:0": { - "id": "anthropic.claude-haiku-4-5-20251001-v1:0", - "name": "Claude Haiku 4.5", - "family": "claude-haiku", + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } + } + } + }, + "venice": { + "id": "venice", + "env": ["VENICE_API_KEY"], + "npm": "venice-ai-sdk-provider", + "name": "Venice AI", + "doc": "https://docs.venice.ai", + "models": { + "openai-gpt-4o-mini-2024-07-18": { + "id": "openai-gpt-4o-mini-2024-07-18", + "name": "GPT-4o Mini", + "family": "gpt-mini", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-02-28", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-02-28", + "last_updated": "2026-03-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.1875, + "output": 0.75, + "cache_read": 0.09375 + } }, - "mistral.pixtral-large-2502-v1:0": { - "id": "mistral.pixtral-large-2502-v1:0", - "name": "Pixtral Large (25.02)", - "family": "mistral", + "qwen3-next-80b": { + "id": "qwen3-next-80b", + "name": "Qwen 3 Next 80b", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-04-08", - "last_updated": "2025-04-08", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 6 }, - "limit": { "context": 128000, "output": 8192 } + "knowledge": "2025-07", + "release_date": "2025-04-29", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 16384 + }, + "cost": { + "input": 0.35, + "output": 1.9 + } }, - "anthropic.claude-3-5-sonnet-20240620-v1:0": { - "id": "anthropic.claude-3-5-sonnet-20240620-v1:0", - "name": "Claude Sonnet 3.5", - "family": "claude-sonnet", + "grok-4-20-multi-agent": { + "id": "grok-4-20-multi-agent", + "name": "Grok 4.20 Multi-Agent", + "family": "grok", "attachment": true, - "reasoning": false, - "tool_call": true, + "reasoning": true, + "tool_call": false, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-06-20", - "last_updated": "2024-06-20", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-03-12", + "last_updated": "2026-05-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 8192 } + "limit": { + "context": 2000000, + "output": 128000 + }, + "cost": { + "input": 1.42, + "output": 2.83, + "cache_read": 0.23, + "tiers": [ + { + "input": 2.83, + "output": 5.67, + "cache_read": 0.45, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 2.83, + "output": 5.67, + "cache_read": 0.45 + } + } }, - "mistral.ministral-3-8b-instruct": { - "id": "mistral.ministral-3-8b-instruct", - "name": "Ministral 3 8B", - "family": "ministral", + "qwen3-235b-a22b-instruct-2507": { + "id": "qwen3-235b-a22b-instruct-2507", + "name": "Qwen 3 235B A22B Instruct 2507", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.15 }, - "limit": { "context": 128000, "output": 4096 } + "knowledge": "2025-07", + "release_date": "2025-04-29", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.75 + } }, - "us.anthropic.claude-haiku-4-5-20251001-v1:0": { - "id": "us.anthropic.claude-haiku-4-5-20251001-v1:0", - "name": "Claude Haiku 4.5 (US)", - "family": "claude-haiku", + "z-ai-glm-5v-turbo": { + "id": "z-ai-glm-5v-turbo", + "name": "GLM 5V Turbo", + "family": "glmv", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-02-28", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-04-01", + "last_updated": "2026-04-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 200000, + "output": 32768 + }, + "cost": { + "input": 1.5, + "output": 5, + "cache_read": 0.3 + } }, - "openai.gpt-oss-120b-1:0": { - "id": "openai.gpt-oss-120b-1:0", - "name": "gpt-oss-120b", - "family": "gpt-oss", - "attachment": false, + "gemma-4-uncensored": { + "id": "gemma-4-uncensored", + "name": "Gemma 4 Uncensored", + "family": "gemma", + "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2026-04-13", + "last_updated": "2026-04-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 8192 + }, + "cost": { + "input": 0.1625, + "output": 0.5 + } }, - "openai.gpt-oss-safeguard-120b": { - "id": "openai.gpt-oss-safeguard-120b", - "name": "GPT OSS Safeguard 120B", - "family": "gpt-oss", - "attachment": false, - "reasoning": false, + "grok-41-fast": { + "id": "grok-41-fast", + "name": "Grok 4.1 Fast", + "family": "grok", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-12-01", + "last_updated": "2026-04-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 1000000, + "output": 30000 + }, + "cost": { + "input": 0.23, + "output": 0.57, + "cache_read": 0.06 + } }, - "anthropic.claude-3-5-sonnet-20241022-v2:0": { - "id": "anthropic.claude-3-5-sonnet-20241022-v2:0", - "name": "Claude Sonnet 3.5 v2", + "claude-sonnet-4-6": { + "id": "claude-sonnet-4-6", + "name": "Claude Sonnet 4.6", "family": "claude-sonnet", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-03-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 8192 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 3.6, + "output": 18, + "cache_read": 0.36, + "cache_write": 4.5 + } }, - "deepseek.v3-v1:0": { - "id": "deepseek.v3-v1:0", - "name": "DeepSeek-V3.1", - "family": "deepseek", + "nvidia-nemotron-cascade-2-30b-a3b": { + "id": "nvidia-nemotron-cascade-2-30b-a3b", + "name": "Nemotron Cascade 2 30B A3B", + "family": "nemotron", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-09-18", - "last_updated": "2025-09-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-24", + "last_updated": "2026-04-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.58, "output": 1.68 }, - "limit": { "context": 163840, "output": 81920 } + "limit": { + "context": 256000, + "output": 32768 + }, + "cost": { + "input": 0.14, + "output": 0.8 + } }, - "minimax.minimax-m2": { - "id": "minimax.minimax-m2", - "name": "MiniMax M2", - "family": "minimax", - "attachment": false, + "gemini-3-flash-preview": { + "id": "gemini-3-flash-preview", + "name": "Gemini 3 Flash Preview", + "family": "gemini-flash", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": false, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-10-27", - "last_updated": "2025-10-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 204608, "output": 128000 } + "knowledge": "2025-01", + "release_date": "2025-12-19", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 65536 + }, + "cost": { + "input": 0.7, + "output": 3.75, + "cache_read": 0.07 + } }, - "qwen.qwen3-vl-235b-a22b": { - "id": "qwen.qwen3-vl-235b-a22b", - "name": "Qwen/Qwen3-VL-235B-A22B-Instruct", - "family": "qwen", + "grok-4-20": { + "id": "grok-4-20", + "name": "Grok 4.20", + "family": "grok", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-10-04", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-03-12", + "last_updated": "2026-05-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.5 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 2000000, + "output": 128000 + }, + "cost": { + "input": 1.42, + "output": 2.83, + "cache_read": 0.23, + "tiers": [ + { + "input": 2.83, + "output": 5.67, + "cache_read": 0.45, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 2.83, + "output": 5.67, + "cache_read": 0.45 + } + } }, - "meta.llama3-2-1b-instruct-v1:0": { - "id": "meta.llama3-2-1b-instruct-v1:0", - "name": "Llama 3.2 1B Instruct", - "family": "llama", - "attachment": false, - "reasoning": false, + "google-gemma-4-26b-a4b-it": { + "id": "google-gemma-4-26b-a4b-it", + "name": "Google Gemma 4 26B A4B Instruct", + "family": "gemma", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-02", + "last_updated": "2026-04-12", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.1 }, - "limit": { "context": 131000, "output": 4096 } - }, - "openai.gpt-oss-safeguard-20b": { - "id": "openai.gpt-oss-safeguard-20b", - "name": "GPT OSS Safeguard 20B", - "family": "gpt-oss", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.07, "output": 0.2 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 256000, + "output": 8192 + }, + "cost": { + "input": 0.1625, + "output": 0.5 + } }, - "us.anthropic.claude-opus-4-5-20251101-v1:0": { - "id": "us.anthropic.claude-opus-4-5-20251101-v1:0", - "name": "Claude Opus 4.5 (US)", + "claude-opus-4-7": { + "id": "claude-opus-4-7", + "name": "Claude Opus 4.7", "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-11-24", - "last_updated": "2025-08-01", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 6, + "output": 30, + "cache_read": 0.6, + "cache_write": 7.5 + } }, - "nvidia.nemotron-nano-3-30b": { - "id": "nvidia.nemotron-nano-3-30b", - "name": "NVIDIA Nemotron Nano 3 30B", - "family": "nemotron", + "qwen3-coder-480b-a35b-instruct-turbo": { + "id": "qwen3-coder-480b-a35b-instruct-turbo", + "name": "Qwen 3 Coder 480B Turbo", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01-27", + "last_updated": "2026-02-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.06, "output": 0.24 }, - "limit": { "context": 128000, "output": 4096 } - }, - "anthropic.claude-opus-4-1-20250805-v1:0": { - "id": "anthropic.claude-opus-4-1-20250805-v1:0", - "name": "Claude Opus 4.1", - "family": "claude-opus", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 256000, + "output": 65536 + }, + "cost": { + "input": 0.35, + "output": 1.5, + "cache_read": 0.04 + } }, - "eu.anthropic.claude-sonnet-4-6": { - "id": "eu.anthropic.claude-sonnet-4-6", - "name": "Claude Sonnet 4.6 (EU)", - "family": "claude-sonnet", + "qwen3-5-397b-a17b": { + "id": "qwen3-5-397b-a17b", + "name": "Qwen 3.5 397B", + "family": "qwen", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-08", - "release_date": "2026-02-17", - "last_updated": "2026-03-18", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 1000000, "output": 64000 } + "release_date": "2026-02-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.75, + "output": 4.5 + } }, - "qwen.qwen3-235b-a22b-2507-v1:0": { - "id": "qwen.qwen3-235b-a22b-2507-v1:0", - "name": "Qwen3 235B A22B 2507", - "family": "qwen", + "zai-org-glm-4.7": { + "id": "zai-org-glm-4.7", + "name": "GLM 4.7", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-09-18", - "last_updated": "2025-09-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-12-24", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.22, "output": 0.88 }, - "limit": { "context": 262144, "output": 131072 } + "limit": { + "context": 198000, + "output": 16384 + }, + "cost": { + "input": 0.55, + "output": 2.65, + "cache_read": 0.11 + } }, - "writer.palmyra-x5-v1:0": { - "id": "writer.palmyra-x5-v1:0", - "name": "Palmyra X5", - "family": "palmyra", - "attachment": false, + "openai-gpt-54": { + "id": "openai-gpt-54", + "name": "GPT-5.4", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-04-28", - "last_updated": "2025-04-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-05", + "last_updated": "2026-03-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 6 }, - "limit": { "context": 1040000, "output": 8192 } + "limit": { + "context": 1000000, + "output": 131072 + }, + "cost": { + "input": 3.13, + "output": 18.8, + "cache_read": 0.313 + } }, - "zai.glm-5": { - "id": "zai.glm-5", - "name": "GLM-5", - "family": "glm", + "zai-org-glm-4.7-flash": { + "id": "zai-org-glm-4.7-flash", + "name": "GLM 4.7 Flash", + "family": "glm-flash", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "structured_output": true, "temperature": true, - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01-29", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1, "output": 3.2 }, - "limit": { "context": 202752, "output": 101376 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.125, + "output": 0.5 + } }, - "amazon.nova-micro-v1:0": { - "id": "amazon.nova-micro-v1:0", - "name": "Nova Micro", - "family": "nova-micro", + "nvidia-nemotron-3-nano-30b-a3b": { + "id": "nvidia-nemotron-3-nano-30b-a3b", + "name": "NVIDIA Nemotron 3 Nano 30B", + "family": "nemotron", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-12-03", - "last_updated": "2024-12-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.035, "output": 0.14, "cache_read": 0.00875 }, - "limit": { "context": 128000, "output": 8192 } + "release_date": "2026-01-27", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.075, + "output": 0.3 + } }, - "writer.palmyra-x4-v1:0": { - "id": "writer.palmyra-x4-v1:0", - "name": "Palmyra X4", - "family": "palmyra", - "attachment": false, + "qwen3-vl-235b-a22b": { + "id": "qwen3-vl-235b-a22b", + "name": "Qwen3 VL 235B", + "family": "qwen", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2026-01-16", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 16384 + }, + "cost": { + "input": 0.25, + "output": 1.5 + } + }, + "openai-gpt-53-codex": { + "id": "openai-gpt-53-codex", + "name": "GPT-5.3 Codex", + "family": "gpt-codex", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-04-28", - "last_updated": "2025-04-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-24", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 122880, "output": 8192 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 2.19, + "output": 17.5, + "cache_read": 0.219 + } }, - "mistral.voxtral-mini-3b-2507": { - "id": "mistral.voxtral-mini-3b-2507", - "name": "Voxtral Mini 3B 2507", - "family": "mistral", - "attachment": false, + "venice-uncensored-1-2": { + "id": "venice-uncensored-1-2", + "name": "Venice Uncensored 1.2", + "family": "venice", + "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["audio", "text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.04, "output": 0.04 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2026-04-01", + "last_updated": "2026-04-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.2, + "output": 0.9 + } }, - "eu.anthropic.claude-sonnet-4-5-20250929-v1:0": { - "id": "eu.anthropic.claude-sonnet-4-5-20250929-v1:0", - "name": "Claude Sonnet 4.5 (EU)", - "family": "claude-sonnet", - "attachment": true, + "openai-gpt-52": { + "id": "openai-gpt-52", + "name": "GPT-5.2", + "family": "gpt", + "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2025-12-13", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 256000, + "output": 65536 + }, + "cost": { + "input": 2.19, + "output": 17.5, + "cache_read": 0.219 + } }, - "mistral.mistral-large-3-675b-instruct": { - "id": "mistral.mistral-large-3-675b-instruct", - "name": "Mistral Large 3", - "family": "mistral", + "mistral-small-3-2-24b-instruct": { + "id": "mistral-small-3-2-24b-instruct", + "name": "Mistral Small 3.2 24B Instruct", + "family": "mistral-small", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-12-02", - "last_updated": "2025-12-02", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-01-15", + "last_updated": "2026-03-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.5, "output": 1.5 }, - "limit": { "context": 256000, "output": 8192 } + "limit": { + "context": 256000, + "output": 16384 + }, + "cost": { + "input": 0.09375, + "output": 0.25 + } }, - "zai.glm-4.7": { - "id": "zai.glm-4.7", - "name": "GLM-4.7", - "family": "glm", + "minimax-m27": { + "id": "minimax-m27", + "name": "MiniMax M2.7", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.2 }, - "limit": { "context": 204800, "output": 131072 } + "release_date": "2026-03-18", + "last_updated": "2026-04-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 198000, + "output": 32768 + }, + "cost": { + "input": 0.375, + "output": 1.5, + "cache_read": 0.075 + } }, - "qwen.qwen3-32b-v1:0": { - "id": "qwen.qwen3-32b-v1:0", - "name": "Qwen3 32B (dense)", + "qwen3-235b-a22b-thinking-2507": { + "id": "qwen3-235b-a22b-thinking-2507", + "name": "Qwen 3 235B A22B Thinking 2507", "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-09-18", - "last_updated": "2025-09-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-04-29", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 16384, "output": 16384 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.45, + "output": 3.5 + } }, - "us.anthropic.claude-opus-4-6-v1": { - "id": "us.anthropic.claude-opus-4-6-v1", - "name": "Claude Opus 4.6 (US)", - "family": "claude-opus", + "qwen3-5-35b-a3b": { + "id": "qwen3-5-35b-a3b", + "name": "Qwen 3.5 35B A3B", + "family": "qwen", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-05", - "release_date": "2026-02-05", - "last_updated": "2026-03-18", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 1000000, "output": 128000 } + "release_date": "2026-02-25", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 65536 + }, + "cost": { + "input": 0.3125, + "output": 1.25, + "cache_read": 0.15625 + } }, - "anthropic.claude-sonnet-4-5-20250929-v1:0": { - "id": "anthropic.claude-sonnet-4-5-20250929-v1:0", - "name": "Claude Sonnet 4.5", - "family": "claude-sonnet", - "attachment": true, + "mercury-2": { + "id": "mercury-2", + "name": "Mercury 2", + "family": "mercury", + "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-02-20", + "last_updated": "2026-04-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 128000, + "output": 50000 + }, + "cost": { + "input": 0.3125, + "output": 0.9375, + "cache_read": 0.03125 + } }, - "meta.llama3-2-11b-instruct-v1:0": { - "id": "meta.llama3-2-11b-instruct-v1:0", - "name": "Llama 3.2 11B Instruct", - "family": "llama", + "google-gemma-3-27b-it": { + "id": "google-gemma-3-27b-it", + "name": "Google Gemma 3 27B Instruct", + "family": "gemma", "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-11-04", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.16, "output": 0.16 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 198000, + "output": 16384 + }, + "cost": { + "input": 0.12, + "output": 0.2 + } }, - "global.anthropic.claude-opus-4-6-v1": { - "id": "global.anthropic.claude-opus-4-6-v1", - "name": "Claude Opus 4.6 (Global)", - "family": "claude-opus", - "attachment": true, + "olafangensan-glm-4.7-flash-heretic": { + "id": "olafangensan-glm-4.7-flash-heretic", + "name": "GLM 4.7 Flash Heretic", + "family": "glm-flash", + "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-05", - "release_date": "2026-02-05", - "last_updated": "2026-03-18", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 1000000, "output": 128000 } + "release_date": "2026-02-04", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 200000, + "output": 24000 + }, + "cost": { + "input": 0.14, + "output": 0.8 + } }, - "global.anthropic.claude-sonnet-4-5-20250929-v1:0": { - "id": "global.anthropic.claude-sonnet-4-5-20250929-v1:0", - "name": "Claude Sonnet 4.5 (Global)", - "family": "claude-sonnet", + "openai-gpt-55-pro": { + "id": "openai-gpt-55-pro", + "name": "GPT-5.5 Pro", + "family": "gpt-pro", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-04-24", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 37.5, + "output": 225 + } }, - "anthropic.claude-sonnet-4-20250514-v1:0": { - "id": "anthropic.claude-sonnet-4-20250514-v1:0", - "name": "Claude Sonnet 4", - "family": "claude-sonnet", + "openai-gpt-52-codex": { + "id": "openai-gpt-52-codex", + "name": "GPT-5.2 Codex", + "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-08", + "release_date": "2025-01-15", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } - }, - "mistral.ministral-3-3b-instruct": { - "id": "mistral.ministral-3-3b-instruct", - "name": "Ministral 3 3B", - "family": "ministral", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2025-12-02", - "last_updated": "2025-12-02", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.1 }, - "limit": { "context": 256000, "output": 8192 } + "limit": { + "context": 256000, + "output": 65536 + }, + "cost": { + "input": 2.19, + "output": 17.5, + "cache_read": 0.219 + } }, - "meta.llama4-maverick-17b-instruct-v1:0": { - "id": "meta.llama4-maverick-17b-instruct-v1:0", - "name": "Llama 4 Maverick 17B Instruct", - "family": "llama", + "venice-uncensored-role-play": { + "id": "venice-uncensored-role-play", + "name": "Venice Role Play Uncensored", + "family": "venice", "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-02-20", + "last_updated": "2026-03-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.24, "output": 0.97 }, - "limit": { "context": 1000000, "output": 16384 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.5, + "output": 2 + } }, - "moonshot.kimi-k2-thinking": { - "id": "moonshot.kimi-k2-thinking", - "name": "Kimi K2 Thinking", - "family": "kimi-thinking", + "zai-org-glm-5": { + "id": "zai-org-glm-5", + "name": "GLM 5", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-12-02", - "last_updated": "2025-12-02", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-11", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.5 }, - "limit": { "context": 256000, "output": 256000 } + "limit": { + "context": 198000, + "output": 32000 + }, + "cost": { + "input": 1, + "output": 3.2, + "cache_read": 0.2 + } }, - "mistral.magistral-small-2509": { - "id": "mistral.magistral-small-2509", - "name": "Magistral Small 1.2", - "family": "magistral", + "zai-org-glm-4.6": { + "id": "zai-org-glm-4.6", + "name": "GLM 4.6", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-12-02", - "last_updated": "2025-12-02", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2024-04-01", + "last_updated": "2026-04-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.5, "output": 1.5 }, - "limit": { "context": 128000, "output": 40000 } + "limit": { + "context": 198000, + "output": 16384 + }, + "cost": { + "input": 0.85, + "output": 2.75, + "cache_read": 0.3 + } }, - "us.anthropic.claude-opus-4-20250514-v1:0": { - "id": "us.anthropic.claude-opus-4-20250514-v1:0", - "name": "Claude Opus 4 (US)", - "family": "claude-opus", + "grok-4-3": { + "id": "grok-4-3", + "name": "Grok 4.3", + "family": "grok", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-04-18", + "last_updated": "2026-05-04", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 1000000, + "output": 32000 + }, + "cost": { + "input": 1.42, + "output": 2.83, + "cache_read": 0.23, + "tiers": [ + { + "input": 2.83, + "output": 5.67, + "cache_read": 0.45, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 2.83, + "output": 5.67, + "cache_read": 0.45 + } + } }, - "eu.anthropic.claude-sonnet-4-20250514-v1:0": { - "id": "eu.anthropic.claude-sonnet-4-20250514-v1:0", - "name": "Claude Sonnet 4 (EU)", - "family": "claude-sonnet", + "mistral-small-2603": { + "id": "mistral-small-2603", + "name": "Mistral Small 4", + "family": "mistral-small", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "release_date": "2026-03-16", + "last_updated": "2026-04-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 65536 + }, + "cost": { + "input": 0.1875, + "output": 0.75 + } }, - "nvidia.nemotron-nano-12b-v2": { - "id": "nvidia.nemotron-nano-12b-v2", - "name": "NVIDIA Nemotron Nano 12B v2 VL BF16", - "family": "nemotron", + "openai-gpt-oss-120b": { + "id": "openai-gpt-oss-120b", + "name": "OpenAI GPT OSS 120B", + "family": "gpt-oss", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.6 }, - "limit": { "context": 128000, "output": 4096 } + "knowledge": "2025-07", + "release_date": "2025-11-06", + "last_updated": "2026-05-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.07, + "output": 0.3 + } }, - "zai.glm-4.7-flash": { - "id": "zai.glm-4.7-flash", - "name": "GLM-4.7-Flash", - "family": "glm-flash", - "attachment": false, + "claude-opus-4-5": { + "id": "claude-opus-4-5", + "name": "Claude Opus 4.5", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-01-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.07, "output": 0.4 }, - "limit": { "context": 200000, "output": 131072 } + "release_date": "2025-12-06", + "last_updated": "2026-04-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 198000, + "output": 32768 + }, + "cost": { + "input": 6, + "output": 30, + "cache_read": 0.6, + "cache_write": 7.5 + } }, - "mistral.voxtral-small-24b-2507": { - "id": "mistral.voxtral-small-24b-2507", - "name": "Voxtral Small 24B 2507", - "family": "mistral", + "qwen3-5-9b": { + "id": "qwen3-5-9b", + "name": "Qwen 3.5 9B", + "family": "qwen", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-07-01", - "last_updated": "2025-07-01", - "modalities": { "input": ["text", "audio"], "output": ["text"] }, + "release_date": "2026-03-05", + "last_updated": "2026-04-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.35 }, - "limit": { "context": 32000, "output": 8192 } + "limit": { + "context": 256000, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.15 + } }, - "eu.anthropic.claude-opus-4-6-v1": { - "id": "eu.anthropic.claude-opus-4-6-v1", - "name": "Claude Opus 4.6 (EU)", - "family": "claude-opus", - "attachment": true, + "deepseek-v4-flash": { + "id": "deepseek-v4-flash", + "name": "DeepSeek V4 Flash", + "family": "deepseek-flash", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2025-05", - "release_date": "2026-02-05", - "last_updated": "2026-03-18", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 1000000, "output": 128000 } + "release_date": "2026-04-24", + "last_updated": "2026-04-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 32768 + }, + "cost": { + "input": 0.17, + "output": 0.35, + "cache_read": 0.028 + } }, - "anthropic.claude-3-7-sonnet-20250219-v1:0": { - "id": "anthropic.claude-3-7-sonnet-20250219-v1:0", - "name": "Claude Sonnet 3.7", - "family": "claude-sonnet", + "openai-gpt-54-pro": { + "id": "openai-gpt-54-pro", + "name": "GPT-5.4 Pro", + "family": "gpt-pro", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-03-05", + "last_updated": "2026-03-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 8192 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 37.5, + "output": 225, + "tiers": [ + { + "input": 75, + "output": 337.5, + "tier": { + "type": "context", + "size": 272000 + } + } + ], + "context_over_200k": { + "input": 75, + "output": 337.5 + } + } }, - "openai.gpt-oss-20b-1:0": { - "id": "openai.gpt-oss-20b-1:0", - "name": "gpt-oss-20b", - "family": "gpt-oss", - "attachment": false, - "reasoning": false, + "openai-gpt-54-mini": { + "id": "openai-gpt-54-mini", + "name": "GPT-5.4 Mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-27", + "last_updated": "2026-03-31", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.07, "output": 0.3 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.9375, + "output": 5.625, + "cache_read": 0.09375 + } }, - "qwen.qwen3-coder-30b-a3b-v1:0": { - "id": "qwen.qwen3-coder-30b-a3b-v1:0", - "name": "Qwen3 Coder 30B A3B Instruct", - "family": "qwen", + "minimax-m25": { + "id": "minimax-m25", + "name": "MiniMax M2.5", + "family": "minimax", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-09-18", - "last_updated": "2025-09-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-12", + "last_updated": "2026-04-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 262144, "output": 131072 } + "limit": { + "context": 198000, + "output": 32768 + }, + "cost": { + "input": 0.34, + "output": 1.19, + "cache_read": 0.04 + } }, - "meta.llama3-1-8b-instruct-v1:0": { - "id": "meta.llama3-1-8b-instruct-v1:0", - "name": "Llama 3.1 8B Instruct", - "family": "llama", + "zai-org-glm-5-1": { + "id": "zai-org-glm-5-1", + "name": "GLM 5.1", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-07", + "last_updated": "2026-04-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.22, "output": 0.22 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 200000, + "output": 24000 + }, + "cost": { + "input": 1.75, + "output": 5.5, + "cache_read": 0.325 + } }, - "eu.anthropic.claude-haiku-4-5-20251001-v1:0": { - "id": "eu.anthropic.claude-haiku-4-5-20251001-v1:0", - "name": "Claude Haiku 4.5 (EU)", - "family": "claude-haiku", + "openai-gpt-55": { + "id": "openai-gpt-55", + "name": "GPT-5.5", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-02-28", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-04-23", + "last_updated": "2026-04-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 1000000, + "output": 131072 + }, + "cost": { + "input": 6.25, + "output": 37.5, + "cache_read": 0.625, + "tiers": [ + { + "input": 12.5, + "output": 56.25, + "cache_read": 1.25, + "tier": { + "type": "context", + "size": 272000 + } + } + ], + "context_over_200k": { + "input": 12.5, + "output": 56.25, + "cache_read": 1.25 + } + } }, - "meta.llama3-2-90b-instruct-v1:0": { - "id": "meta.llama3-2-90b-instruct-v1:0", - "name": "Llama 3.2 90B Instruct", - "family": "llama", + "qwen3-6-27b": { + "id": "qwen3-6-27b", + "name": "Qwen 3.6 27B", + "family": "qwen", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.72, "output": 0.72 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2026-04-24", + "last_updated": "2026-04-29", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 65536 + }, + "cost": { + "input": 0.325, + "output": 3.25 + } }, - "anthropic.claude-opus-4-6-v1": { - "id": "anthropic.claude-opus-4-6-v1", + "claude-opus-4-6": { + "id": "claude-opus-4-6", "name": "Claude Opus 4.6", "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-05", + "knowledge": "2025-05-31", "release_date": "2026-02-05", - "last_updated": "2026-03-18", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "last_updated": "2026-03-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 1000000, "output": 128000 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 6, + "output": 30, + "cache_read": 0.6, + "cache_write": 7.5 + } }, - "anthropic.claude-3-5-haiku-20241022-v1:0": { - "id": "anthropic.claude-3-5-haiku-20241022-v1:0", - "name": "Claude Haiku 3.5", - "family": "claude-haiku", - "attachment": true, - "reasoning": false, + "deepseek-v4-pro": { + "id": "deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek", + "attachment": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.8, "output": 4, "cache_read": 0.08, "cache_write": 1 }, - "limit": { "context": 200000, "output": 8192 } + "release_date": "2026-04-24", + "last_updated": "2026-04-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 32768 + }, + "cost": { + "input": 1.73, + "output": 3.796, + "cache_read": 0.33 + } }, - "meta.llama3-2-3b-instruct-v1:0": { - "id": "meta.llama3-2-3b-instruct-v1:0", - "name": "Llama 3.2 3B Instruct", - "family": "llama", + "deepseek-v3.2": { + "id": "deepseek-v3.2", + "name": "DeepSeek V3.2", + "family": "deepseek", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-10", + "release_date": "2025-12-04", + "last_updated": "2026-03-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.15 }, - "limit": { "context": 131000, "output": 4096 } + "limit": { + "context": 160000, + "output": 32768 + }, + "cost": { + "input": 0.33, + "output": 0.48, + "cache_read": 0.16 + } }, - "us.anthropic.claude-sonnet-4-5-20250929-v1:0": { - "id": "us.anthropic.claude-sonnet-4-5-20250929-v1:0", - "name": "Claude Sonnet 4.5 (US)", - "family": "claude-sonnet", + "qwen-3-6-plus": { + "id": "qwen-3-6-plus", + "name": "Qwen 3.6 Plus Uncensored", + "family": "qwen", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-04-06", + "last_updated": "2026-04-12", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.625, + "output": 3.75, + "cache_read": 0.0625, + "cache_write": 0.78, + "tiers": [ + { + "input": 2.5, + "output": 7.5, + "cache_read": 0.0625, + "cache_write": 0.78, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 2.5, + "output": 7.5, + "cache_read": 0.0625, + "cache_write": 0.78 + } + } }, - "deepseek.r1-v1:0": { - "id": "deepseek.r1-v1:0", - "name": "DeepSeek-R1", - "family": "deepseek-thinking", + "aion-labs-aion-2-0": { + "id": "aion-labs-aion-2-0", + "name": "Aion 2.0", + "family": "o", "attachment": false, "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-01-20", - "last_updated": "2025-05-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.35, "output": 5.4 }, - "limit": { "context": 128000, "output": 32768 } - }, - "mistral.ministral-3-14b-instruct": { - "id": "mistral.ministral-3-14b-instruct", - "name": "Ministral 14B 3.0", - "family": "ministral", - "attachment": false, - "reasoning": false, - "tool_call": true, + "tool_call": false, "temperature": true, - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-24", + "last_updated": "2026-04-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 1, + "output": 2, + "cache_read": 0.25 + } }, - "amazon.nova-lite-v1:0": { - "id": "amazon.nova-lite-v1:0", - "name": "Nova Lite", - "family": "nova-lite", + "claude-sonnet-4-5": { + "id": "claude-sonnet-4-5", + "name": "Claude Sonnet 4.5", + "family": "claude-sonnet", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-12-03", - "last_updated": "2024-12-03", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "release_date": "2025-01-15", + "last_updated": "2026-04-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.06, "output": 0.24, "cache_read": 0.015 }, - "limit": { "context": 300000, "output": 8192 } + "limit": { + "context": 198000, + "output": 64000 + }, + "cost": { + "input": 3.75, + "output": 18.75, + "cache_read": 0.375, + "cache_write": 4.69 + } }, - "amazon.nova-pro-v1:0": { - "id": "amazon.nova-pro-v1:0", - "name": "Nova Pro", - "family": "nova-pro", + "openai-gpt-4o-2024-11-20": { + "id": "openai-gpt-4o-2024-11-20", + "name": "GPT-4o", + "family": "gpt", "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-12-03", - "last_updated": "2024-12-03", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "release_date": "2026-02-28", + "last_updated": "2026-03-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.8, "output": 3.2, "cache_read": 0.2 }, - "limit": { "context": 300000, "output": 8192 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 3.125, + "output": 12.5 + } }, - "mistral.devstral-2-123b": { - "id": "mistral.devstral-2-123b", - "name": "Devstral 2 123B", - "family": "devstral", + "llama-3.3-70b": { + "id": "llama-3.3-70b", + "name": "Llama 3.3 70B", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2026-02-17", - "last_updated": "2026-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2025-04-06", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 256000, "output": 8192 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.7, + "output": 2.8 + } }, - "qwen.qwen3-next-80b-a3b": { - "id": "qwen.qwen3-next-80b-a3b", - "name": "Qwen/Qwen3-Next-80B-A3B-Instruct", - "family": "qwen", + "kimi-k2-5": { + "id": "kimi-k2-5", + "name": "Kimi K2.5", + "family": "kimi", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2026-01-27", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 65536 + }, + "cost": { + "input": 0.56, + "output": 3.5, + "cache_read": 0.22 + } + }, + "llama-3.2-3b": { + "id": "llama-3.2-3b", + "name": "Llama 3.2 3B", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-09-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.14, "output": 1.4 }, - "limit": { "context": 262000, "output": 262000 } + "knowledge": "2023-12", + "release_date": "2024-10-03", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "minimax.minimax-m2.5": { - "id": "minimax.minimax-m2.5", - "name": "MiniMax M2.5", - "family": "minimax", + "arcee-trinity-large-thinking": { + "id": "arcee-trinity-large-thinking", + "name": "Trinity Large Thinking", + "family": "trinity", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": false, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-02", + "last_updated": "2026-04-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 196608, "output": 98304 } + "limit": { + "context": 256000, + "output": 65536 + }, + "cost": { + "input": 0.3125, + "output": 1.125, + "cache_read": 0.075 + } }, - "anthropic.claude-3-haiku-20240307-v1:0": { - "id": "anthropic.claude-3-haiku-20240307-v1:0", - "name": "Claude Haiku 3", - "family": "claude-haiku", - "attachment": true, + "hermes-3-llama-3.1-405b": { + "id": "hermes-3-llama-3.1-405b", + "name": "Hermes 3 Llama 3.1 405b", + "family": "hermes", + "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2024-02", - "release_date": "2024-03-13", - "last_updated": "2024-03-13", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 1.25 }, - "limit": { "context": 200000, "output": 4096 } + "knowledge": "2024-04", + "release_date": "2025-09-25", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.1, + "output": 3 + } }, - "eu.anthropic.claude-opus-4-5-20251101-v1:0": { - "id": "eu.anthropic.claude-opus-4-5-20251101-v1:0", - "name": "Claude Opus 4.5 (EU)", - "family": "claude-opus", + "gemini-3-1-pro-preview": { + "id": "gemini-3-1-pro-preview", + "name": "Gemini 3.1 Pro Preview", + "family": "gemini-pro", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-11-24", - "last_updated": "2025-08-01", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-02-19", + "last_updated": "2026-03-12", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 1000000, + "output": 32768 + }, + "cost": { + "input": 2.5, + "output": 15, + "cache_read": 0.5, + "cache_write": 0.5, + "tiers": [ + { + "input": 5, + "output": 22.5, + "cache_read": 0.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 5, + "output": 22.5, + "cache_read": 0.5 + } + } }, - "meta.llama4-scout-17b-instruct-v1:0": { - "id": "meta.llama4-scout-17b-instruct-v1:0", - "name": "Llama 4 Scout 17B Instruct", - "family": "llama", + "kimi-k2-6": { + "id": "kimi-k2-6", + "name": "Kimi K2.6", + "family": "kimi", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-04-20", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.17, "output": 0.66 }, - "limit": { "context": 3500000, "output": 16384 } - }, - "amazon.nova-2-lite-v1:0": { - "id": "amazon.nova-2-lite-v1:0", - "name": "Nova 2 Lite", - "family": "nova", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.33, "output": 2.75 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 256000, + "output": 65536 + }, + "cost": { + "input": 0.85, + "output": 4.655, + "cache_read": 0.22 + } }, - "global.anthropic.claude-opus-4-5-20251101-v1:0": { - "id": "global.anthropic.claude-opus-4-5-20251101-v1:0", - "name": "Claude Opus 4.5 (Global)", + "claude-opus-4-6-fast": { + "id": "claude-opus-4-6-fast", + "name": "Claude Opus 4.6 Fast", "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-11-24", - "last_updated": "2025-08-01", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-05-31", + "release_date": "2026-04-08", + "last_updated": "2026-04-08", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 36, + "output": 180, + "cache_read": 3.6, + "cache_write": 45 + } }, - "amazon.nova-premier-v1:0": { - "id": "amazon.nova-premier-v1:0", - "name": "Nova Premier", - "family": "nova", - "attachment": true, + "z-ai-glm-5-turbo": { + "id": "z-ai-glm-5-turbo", + "name": "GLM 5 Turbo", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-12-03", - "last_updated": "2024-12-03", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 12.5 }, - "limit": { "context": 1000000, "output": 16384 } + "release_date": "2026-03-15", + "last_updated": "2026-04-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 200000, + "output": 32768 + }, + "cost": { + "input": 1.2, + "output": 4, + "cache_read": 0.24 + } }, - "anthropic.claude-sonnet-4-6": { - "id": "anthropic.claude-sonnet-4-6", - "name": "Claude Sonnet 4.6", - "family": "claude-sonnet", + "google-gemma-4-31b-it": { + "id": "google-gemma-4-31b-it", + "name": "Google Gemma 4 31B Instruct", + "family": "gemma", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-08", - "release_date": "2026-02-17", - "last_updated": "2026-03-18", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 1000000, "output": 64000 } - }, - "anthropic.claude-opus-4-20250514-v1:0": { - "id": "anthropic.claude-opus-4-20250514-v1:0", - "name": "Claude Opus 4", - "family": "claude-opus", - "attachment": true, + "release_date": "2026-04-03", + "last_updated": "2026-04-12", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 8192 + }, + "cost": { + "input": 0.175, + "output": 0.5 + } + } + } + }, + "aihubmix": { + "id": "aihubmix", + "env": ["AIHUBMIX_API_KEY"], + "npm": "@aihubmix/ai-sdk-provider", + "name": "AIHubMix", + "doc": "https://docs.aihubmix.com", + "models": { + "minimax-m2.7": { + "id": "minimax-m2.7", + "name": "MiniMax-M2.7", + "family": "minimax", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 0.2958, + "output": 1.1832, + "cache_read": 0.05916 + } }, - "nvidia.nemotron-nano-9b-v2": { - "id": "nvidia.nemotron-nano-9b-v2", - "name": "NVIDIA Nemotron Nano 9B v2", - "family": "nemotron", + "coding-glm-5.1-free": { + "id": "coding-glm-5.1-free", + "name": "Coding GLM 5.1 (free)", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-11", + "last_updated": "2026-04-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.06, "output": 0.23 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 204800, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } }, - "google.gemma-3-4b-it": { - "id": "google.gemma-3-4b-it", - "name": "Gemma 3 4B IT", - "family": "gemma", - "attachment": false, - "reasoning": false, + "gemini-3.1-pro-preview-customtools": { + "id": "gemini-3.1-pro-preview-customtools", + "name": "Gemini 3.1 Pro Preview Custom Tools", + "family": "gemini-pro", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-02-19", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.04, "output": 0.08 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2, + "tiers": [ + { + "input": 4, + "output": 18, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 18, + "cache_read": 0.4 + } + } }, - "meta.llama3-1-70b-instruct-v1:0": { - "id": "meta.llama3-1-70b-instruct-v1:0", - "name": "Llama 3.1 70B Instruct", - "family": "llama", + "kimi-k2.5": { + "id": "kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi-k2.5", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.72, "output": 0.72 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 256000, + "output": 0 + }, + "cost": { + "input": 0.6, + "output": 3, + "cache_read": 0.105 + } }, - "global.anthropic.claude-haiku-4-5-20251001-v1:0": { - "id": "global.anthropic.claude-haiku-4-5-20251001-v1:0", - "name": "Claude Haiku 4.5 (Global)", - "family": "claude-haiku", + "glm-5v-turbo": { + "id": "glm-5v-turbo", + "name": "GLM 5 Vision Turbo", + "family": "glm", "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2025-02-28", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-05-09", + "last_updated": "2026-05-09", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 0.7042, + "output": 3.09848, + "cache_read": 0.169008 + } }, - "anthropic.claude-opus-4-5-20251101-v1:0": { - "id": "anthropic.claude-opus-4-5-20251101-v1:0", - "name": "Claude Opus 4.5", - "family": "claude-opus", + "grok-4.3": { + "id": "grok-4.3", + "name": "Grok 4.3", + "family": "grok", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-11-24", - "last_updated": "2025-08-01", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-05-01", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 1000000, + "output": 1000000 + }, + "cost": { + "input": 1.25, + "output": 2.5, + "cache_read": 0.2, + "tiers": [ + { + "input": 2.5, + "output": 5, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 2.5, + "output": 5, + "cache_read": 0.4 + } + } }, - "us.anthropic.claude-sonnet-4-6": { - "id": "us.anthropic.claude-sonnet-4-6", - "name": "Claude Sonnet 4.6 (US)", - "family": "claude-sonnet", - "attachment": true, + "coding-minimax-m2.7-highspeed": { + "id": "coding-minimax-m2.7-highspeed", + "name": "Coding MiniMax M2.7 Highspeed", + "family": "minimax", + "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-08", - "release_date": "2026-02-17", + "release_date": "2026-03-18", "last_updated": "2026-03-18", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 1000000, "output": 64000 } + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 13100 + }, + "cost": { + "input": 0.2, + "output": 0.2 + } }, - "global.anthropic.claude-sonnet-4-20250514-v1:0": { - "id": "global.anthropic.claude-sonnet-4-20250514-v1:0", - "name": "Claude Sonnet 4 (Global)", + "claude-sonnet-4-6": { + "id": "claude-sonnet-4-6", + "name": "Claude Sonnet 4.6", "family": "claude-sonnet", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-02-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } - } - } - }, - "huggingface": { - "id": "huggingface", - "env": ["HF_TOKEN"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://router.huggingface.co/v1", - "name": "Hugging Face", - "doc": "https://huggingface.co/docs/inference-providers", - "models": { - "zai-org/GLM-4.7": { - "id": "zai-org/GLM-4.7", - "name": "GLM-4.7", - "family": "glm", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.2, "cache_read": 0.11 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75, + "tiers": [ + { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5 + } + } }, - "zai-org/GLM-4.7-Flash": { - "id": "zai-org/GLM-4.7-Flash", - "name": "GLM-4.7-Flash", - "family": "glm", - "attachment": false, + "gemini-3.1-pro-preview": { + "id": "gemini-3.1-pro-preview", + "name": "Gemini 3.1 Pro Preview", + "family": "gemini-pro", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-08-08", - "last_updated": "2025-08-08", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 200000, "output": 128000 } + "knowledge": "2025-01", + "release_date": "2026-02-19", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2 + } }, - "zai-org/GLM-5": { - "id": "zai-org/GLM-5", - "name": "GLM-5", + "coding-glm-5.1": { + "id": "coding-glm-5.1", + "name": "Coding-GLM-5.1", "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1, "output": 3.2, "cache_read": 0.2 }, - "limit": { "context": 202752, "output": 131072 } + "release_date": "2026-04-11", + "last_updated": "2026-04-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 0.06, + "output": 0.22 + } }, - "MiniMaxAI/MiniMax-M2.1": { - "id": "MiniMaxAI/MiniMax-M2.1", - "name": "MiniMax-M2.1", - "family": "minimax", - "attachment": false, + "gemini-3-flash-preview": { + "id": "gemini-3-flash-preview", + "name": "Gemini 3 Flash Preview", + "family": "gemini-flash", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "structured_output": true, "temperature": true, - "knowledge": "2025-10", - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 204800, "output": 131072 } + "knowledge": "2025-01", + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.05 + } }, - "MiniMaxAI/MiniMax-M2.5": { - "id": "MiniMaxAI/MiniMax-M2.5", - "name": "MiniMax-M2.5", - "family": "minimax", - "attachment": false, + "gpt-5.5": { + "id": "gpt-5.5", + "name": "GPT-5.5", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.03 }, - "limit": { "context": 204800, "output": 131072 } + "structured_output": true, + "temperature": false, + "knowledge": "2025-12-01", + "release_date": "2026-04-23", + "last_updated": "2026-04-23", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5 + } }, - "deepseek-ai/DeepSeek-R1-0528": { - "id": "deepseek-ai/DeepSeek-R1-0528", - "name": "DeepSeek-R1-0528", - "family": "deepseek-thinking", - "attachment": false, + "claude-opus-4-7": { + "id": "claude-opus-4-7", + "name": "Claude Opus 4.7", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-05", - "release_date": "2025-05-28", - "last_updated": "2025-05-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 3, "output": 5 }, - "limit": { "context": 163840, "output": 163840 } + "temperature": false, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "deepseek-ai/DeepSeek-V3.2": { - "id": "deepseek-ai/DeepSeek-V3.2", - "name": "DeepSeek-V3.2", + "deepseek-v4-flash-think": { + "id": "deepseek-v4-flash-think", + "name": "DeepSeek V4 Flash Think", "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.28, "output": 0.4 }, - "limit": { "context": 163840, "output": 65536 } + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 0.154, + "output": 0.308, + "cache_read": 0.0308 + } }, - "Qwen/Qwen3-Next-80B-A3B-Thinking": { - "id": "Qwen/Qwen3-Next-80B-A3B-Thinking", - "name": "Qwen3-Next-80B-A3B-Thinking", - "family": "qwen", - "attachment": false, - "reasoning": false, + "gpt-5.3-codex": { + "id": "gpt-5.3-codex", + "name": "GPT-5.3 Codex", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-11", - "last_updated": "2025-09-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 2 }, - "limit": { "context": 262144, "output": 131072 } + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "Qwen/Qwen3-Coder-Next": { - "id": "Qwen/Qwen3-Coder-Next", - "name": "Qwen3-Coder-Next", - "family": "qwen", - "attachment": false, - "reasoning": false, + "gemini-2.5-pro": { + "id": "gemini-2.5-pro", + "name": "Gemini 2.5 Pro", + "family": "gemini-pro", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-02-03", - "last_updated": "2026-02-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 1.5 }, - "limit": { "context": 262144, "output": 65536 } - }, - "Qwen/Qwen3.5-397B-A17B": { - "id": "Qwen/Qwen3.5-397B-A17B", - "name": "Qwen3.5-397B-A17B", - "family": "qwen", + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } + }, + "gpt-5.2": { + "id": "gpt-5.2", + "name": "GPT-5.2", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-02-01", - "last_updated": "2026-02-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 3.6 }, - "limit": { "context": 262144, "output": 32768 } + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "Qwen/Qwen3-235B-A22B-Thinking-2507": { - "id": "Qwen/Qwen3-235B-A22B-Thinking-2507", - "name": "Qwen3-235B-A22B-Thinking-2507", + "qwen3.6-plus": { + "id": "qwen3.6-plus", + "name": "Qwen3.6 Plus", "family": "qwen", - "attachment": false, + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, "knowledge": "2025-04", - "release_date": "2025-07-25", - "last_updated": "2025-07-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 3 }, - "limit": { "context": 262144, "output": 131072 } + "release_date": "2026-05-09", + "last_updated": "2026-05-09", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 991000, + "output": 64000 + }, + "cost": { + "input": 0.282, + "output": 1.692, + "cache_read": 0.0282, + "cache_write": 0.3525 + } }, - "Qwen/Qwen3-Next-80B-A3B-Instruct": { - "id": "Qwen/Qwen3-Next-80B-A3B-Instruct", - "name": "Qwen3-Next-80B-A3B-Instruct", - "family": "qwen", - "attachment": false, + "gpt-5.4-mini": { + "id": "gpt-5.4-mini", + "name": "GPT-5.4-Mini", + "family": "gpt-mini", + "attachment": true, "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-11", - "last_updated": "2025-09-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.25, "output": 1 }, - "limit": { "context": 262144, "output": 66536 } - }, - "Qwen/Qwen3-Embedding-4B": { - "id": "Qwen/Qwen3-Embedding-4B", - "name": "Qwen 3 Embedding 4B", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": false, + "structured_output": true, "temperature": false, - "knowledge": "2024-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.01, "output": 0 }, - "limit": { "context": 32000, "output": 2048 } + "release_date": "2026-03-11", + "last_updated": "2026-03-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.75, + "output": 4.5, + "cache_read": 0.075 + } }, - "Qwen/Qwen3-Coder-480B-A35B-Instruct": { - "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct", - "name": "Qwen3-Coder-480B-A35B-Instruct", - "family": "qwen", + "glm-5.1": { + "id": "glm-5.1", + "name": "GLM-5.1", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2, "output": 2 }, - "limit": { "context": 262144, "output": 66536 } + "release_date": "2026-03-27", + "last_updated": "2026-03-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 0.845, + "output": 3.38, + "cache_read": 0.183112 + } }, - "Qwen/Qwen3-Embedding-8B": { - "id": "Qwen/Qwen3-Embedding-8B", - "name": "Qwen 3 Embedding 8B", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": false, + "o4-mini": { + "id": "o4-mini", + "name": "o4-mini", + "family": "o-mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": false, - "knowledge": "2024-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.01, "output": 0 }, - "limit": { "context": 32000, "output": 4096 } + "knowledge": "2024-05", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.275 + } }, - "moonshotai/Kimi-K2-Instruct": { - "id": "moonshotai/Kimi-K2-Instruct", - "name": "Kimi-K2-Instruct", - "family": "kimi", - "attachment": false, - "reasoning": false, + "gpt-5.2-codex": { + "id": "gpt-5.2-codex", + "name": "GPT-5.2-Codex", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-07-14", - "last_updated": "2025-07-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1, "output": 3 }, - "limit": { "context": 131072, "output": 16384 } + "structured_output": true, + "knowledge": "2025-08-31", + "release_date": "2026-01-14", + "last_updated": "2026-01-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "moonshotai/Kimi-K2.5": { - "id": "moonshotai/Kimi-K2.5", - "name": "Kimi-K2.5", - "family": "kimi", + "gemini-2.5-flash": { + "id": "gemini-2.5-flash", + "name": "Gemini 2.5 Flash", + "family": "gemini-flash", "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "structured_output": true, "temperature": true, "knowledge": "2025-01", - "release_date": "2026-01-01", - "last_updated": "2026-01-01", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 3, "cache_read": 0.1 }, - "limit": { "context": 262144, "output": 262144 } + "release_date": "2025-03-20", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.499, + "cache_read": 0.03 + } }, - "moonshotai/Kimi-K2-Instruct-0905": { - "id": "moonshotai/Kimi-K2-Instruct-0905", - "name": "Kimi-K2-Instruct-0905", - "family": "kimi", - "attachment": false, - "reasoning": false, + "gpt-5.1-codex-mini": { + "id": "gpt-5.1-codex-mini", + "name": "GPT-5.1 Codex mini", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-09-04", - "last_updated": "2025-09-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1, "output": 3 }, - "limit": { "context": 262144, "output": 16384 } + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.025 + } }, - "moonshotai/Kimi-K2-Thinking": { - "id": "moonshotai/Kimi-K2-Thinking", - "name": "Kimi-K2-Thinking", - "family": "kimi-thinking", - "attachment": false, + "gemini-3.1-flash-lite": { + "id": "gemini-3.1-flash-lite", + "name": "Gemini 3.1 Flash Lite", + "family": "gemini-flash-lite", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "structured_output": true, "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-11-06", - "last_updated": "2025-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.5, "cache_read": 0.15 }, - "limit": { "context": 262144, "output": 262144 } + "knowledge": "2025-01", + "release_date": "2026-03-03", + "last_updated": "2026-03-03", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.25, + "output": 1.5, + "cache_read": 0.25 + } }, - "XiaomiMiMo/MiMo-V2-Flash": { - "id": "XiaomiMiMo/MiMo-V2-Flash", - "name": "MiMo-V2-Flash", - "family": "mimo", - "attachment": false, + "gpt-5.1": { + "id": "gpt-5.1", + "name": "GPT-5.1", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-12-16", - "last_updated": "2025-12-16", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 262144, "output": 4096 } - } - } - }, - "stepfun": { - "id": "stepfun", - "env": ["STEPFUN_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.stepfun.com/v1", - "name": "StepFun", - "doc": "https://platform.stepfun.com/docs/zh/overview/concept", - "models": { - "step-1-32k": { - "id": "step-1-32k", - "name": "Step 1 (32K)", - "attachment": false, + "knowledge": "2025-11", + "release_date": "2025-11-15", + "last_updated": "2025-11-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } + }, + "claude-opus-4-6-think": { + "id": "claude-opus-4-6-think", + "name": "Claude Opus 4.6", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-06", - "release_date": "2025-01-01", - "last_updated": "2026-02-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.05, "output": 9.59, "cache_read": 0.41 }, - "limit": { "context": 32768, "input": 32768, "output": 32768 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "step-3.5-flash": { - "id": "step-3.5-flash", - "name": "Step 3.5 Flash", + "coding-minimax-m2.7-free": { + "id": "coding-minimax-m2.7-free", + "name": "Coding-MiniMax-M2.7-Free", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-01-29", - "last_updated": "2026-02-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.096, "output": 0.288, "cache_read": 0.019 }, - "limit": { "context": 256000, "input": 256000, "output": 256000 } + "limit": { + "context": 204800, + "output": 13100 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "step-2-16k": { - "id": "step-2-16k", - "name": "Step 2 (16K)", + "deepseek-v4-flash": { + "id": "deepseek-v4-flash", + "name": "DeepSeek V4 Flash", + "family": "deepseek-flash", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2024-06", - "release_date": "2025-01-01", - "last_updated": "2026-02-13", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5.21, "output": 16.44, "cache_read": 1.04 }, - "limit": { "context": 16384, "input": 16384, "output": 8192 } - } - } - }, - "fastrouter": { - "id": "fastrouter", - "env": ["FASTROUTER_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://go.fastrouter.ai/api/v1", - "name": "FastRouter", - "doc": "https://fastrouter.ai/models", - "models": { - "openai/gpt-5": { - "id": "openai/gpt-5", - "name": "GPT-5", - "family": "gpt", + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 0.154, + "output": 0.308, + "cache_read": 0.0308 + } + }, + "kimi-k2.6": { + "id": "kimi-k2.6", + "name": "Kimi K2.6", + "family": "kimi-k2.6", "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2024-10-01", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } + "knowledge": "2025-01", + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.95, + "output": 3.9995, + "cache_read": 0.160835 + } }, - "openai/gpt-5-mini": { - "id": "openai/gpt-5-mini", - "name": "GPT-5 Mini", - "family": "gpt-mini", + "gpt-5.4": { + "id": "gpt-5.4", + "name": "GPT-5.4", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-10-01", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "release_date": "2026-03-11", + "last_updated": "2026-03-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.025 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 2.5, + "output": 15, + "cache_read": 0.25 + } }, - "openai/gpt-4.1": { - "id": "openai/gpt-4.1", - "name": "GPT-4.1", - "family": "gpt", + "claude-opus-4-6": { + "id": "claude-opus-4-6", + "name": "Claude Opus 4.6", + "family": "claude-opus", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "openai/gpt-oss-120b": { - "id": "openai/gpt-oss-120b", - "name": "GPT OSS 120B", - "family": "gpt-oss", + "deepseek-v4-pro": { + "id": "deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 0.478, + "output": 0.956, + "cache_read": 0.004302 + } }, - "openai/gpt-5-nano": { - "id": "openai/gpt-5-nano", - "name": "GPT-5 Nano", - "family": "gpt-nano", + "claude-opus-4-7-think": { + "id": "claude-opus-4-7-think", + "name": "Claude Opus 4.7 Thinking", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-10-01", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.05, "output": 0.4, "cache_read": 0.005 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "openai/gpt-oss-20b": { - "id": "openai/gpt-oss-20b", - "name": "GPT OSS 20B", - "family": "gpt-oss", + "coding-minimax-m2.7": { + "id": "coding-minimax-m2.7", + "name": "Coding MiniMax M2.7", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.05, "output": 0.2 }, - "limit": { "context": 131072, "output": 65536 } + "limit": { + "context": 204800, + "output": 13100 + }, + "cost": { + "input": 0.2, + "output": 0.2 + } }, - "x-ai/grok-4": { - "id": "x-ai/grok-4", - "name": "Grok 4", - "family": "grok", + "qwen3.6-max-preview": { + "id": "qwen3.6-max-preview", + "name": "Qwen3.6 Max Preview", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2026-05-09", + "last_updated": "2026-05-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.75, "cache_write": 15 }, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 240000, + "output": 64000 + }, + "cost": { + "input": 1.268, + "output": 7.608, + "cache_read": 0.1268, + "cache_write": 1.585 + } }, - "anthropic/claude-opus-4.1": { - "id": "anthropic/claude-opus-4.1", - "name": "Claude Opus 4.1", - "family": "claude-opus", + "gpt-4.1": { + "id": "gpt-4.1", + "name": "GPT-4.1", + "family": "gpt", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } }, - "anthropic/claude-sonnet-4": { - "id": "anthropic/claude-sonnet-4", - "name": "Claude Sonnet 4", - "family": "claude-sonnet", + "gpt-4.1-mini": { + "id": "gpt-4.1-mini", + "name": "GPT-4.1 mini", + "family": "gpt-mini", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } - }, - "z-ai/glm-5": { - "id": "z-ai/glm-5", - "name": "GLM-5", - "family": "glm", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.95, "output": 3.15 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 1.6, + "cache_read": 0.1 + } }, - "google/gemini-2.5-pro": { - "id": "google/gemini-2.5-pro", - "name": "Gemini 2.5 Pro", - "family": "gemini-pro", + "gpt-5.1-codex": { + "id": "gpt-5.1-codex", + "name": "GPT-5.1 Codex", + "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.31 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "google/gemini-2.5-flash": { - "id": "google/gemini-2.5-flash", - "name": "Gemini 2.5 Flash", - "family": "gemini-flash", + "claude-sonnet-4-6-think": { + "id": "claude-sonnet-4-6-think", + "name": "Claude Sonnet 4.6 Think", + "family": "claude-sonnet", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-02-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 2.5, "cache_read": 0.0375 }, - "limit": { "context": 1048576, "output": 65536 } - }, - "deepseek-ai/deepseek-r1-distill-llama-70b": { - "id": "deepseek-ai/deepseek-r1-distill-llama-70b", - "name": "DeepSeek R1 Distill Llama 70B", - "family": "deepseek-thinking", - "attachment": false, - "reasoning": true, - "tool_call": false, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-01-23", - "last_updated": "2025-01-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.03, "output": 0.14 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75, + "tiers": [ + { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5 + } + } }, - "qwen/qwen3-coder": { - "id": "qwen/qwen3-coder", - "name": "Qwen3 Coder", + "qwen3.6-flash": { + "id": "qwen3.6-flash", + "name": "Qwen3.6 Flash", "family": "qwen", - "attachment": false, - "reasoning": false, + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 262144, "output": 66536 } - }, - "moonshotai/kimi-k2": { - "id": "moonshotai/kimi-k2", - "name": "Kimi K2", - "family": "kimi", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-07-11", - "last_updated": "2025-07-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.55, "output": 2.2 }, - "limit": { "context": 131072, "output": 32768 } + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 991000, + "output": 64000 + }, + "cost": { + "input": 0.169, + "output": 1.014, + "cache_read": 0.0169, + "cache_write": 0.21125 + } } } }, - "baseten": { - "id": "baseten", - "env": ["BASETEN_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://inference.baseten.co/v1", - "name": "Baseten", - "doc": "https://docs.baseten.co/development/model-apis/overview", + "cerebras": { + "id": "cerebras", + "env": ["CEREBRAS_API_KEY"], + "npm": "@ai-sdk/cerebras", + "name": "Cerebras", + "doc": "https://inference-docs.cerebras.ai/models/overview", "models": { - "openai/gpt-oss-120b": { - "id": "openai/gpt-oss-120b", - "name": "GPT OSS 120B", - "family": "gpt-oss", + "llama3.1-8b": { + "id": "llama3.1-8b", + "name": "Llama 3.1 8B", + "family": "llama", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-08", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.5 }, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 32000, + "output": 8000 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "zai-org/GLM-4.7": { - "id": "zai-org/GLM-4.7", - "name": "GLM-4.7", - "family": "glm", + "qwen-3-235b-a22b-instruct-2507": { + "id": "qwen-3-235b-a22b-instruct-2507", + "name": "Qwen 3 235B Instruct", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.2 }, - "limit": { "context": 204800, "output": 131072 } - }, - "zai-org/GLM-5": { - "id": "zai-org/GLM-5", - "name": "GLM-5", - "family": "glm", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2026-01", - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-07-22", + "last_updated": "2025-07-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.95, "output": 3.15 }, - "limit": { "context": 202752, "output": 131072 } + "limit": { + "context": 131000, + "output": 32000 + }, + "cost": { + "input": 0.6, + "output": 1.2 + } }, - "zai-org/GLM-4.6": { - "id": "zai-org/GLM-4.6", - "name": "GLM 4.6", - "family": "glm", + "zai-glm-4.7": { + "id": "zai-glm-4.7", + "name": "Z.AI GLM-4.7", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-08-31", - "release_date": "2025-09-16", - "last_updated": "2025-09-16", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.2 }, - "limit": { "context": 200000, "output": 200000 } - }, - "nvidia/Nemotron-120B-A12B": { - "id": "nvidia/Nemotron-120B-A12B", - "name": "Nemotron 3 Super", - "family": "nemotron", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2026-02", - "release_date": "2026-03-11", - "last_updated": "2026-03-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 0.75 }, - "limit": { "context": 262144, "output": 32678 } - }, - "MiniMaxAI/MiniMax-M2.5": { - "id": "MiniMaxAI/MiniMax-M2.5", - "name": "MiniMax-M2.5", - "family": "minimax", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2026-01", - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01-10", + "last_updated": "2026-01-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 204000, "output": 204000 } + "limit": { + "context": 131072, + "output": 40000 + }, + "cost": { + "input": 2.25, + "output": 2.75, + "cache_read": 0, + "cache_write": 0 + } }, - "deepseek-ai/DeepSeek-V3.1": { - "id": "deepseek-ai/DeepSeek-V3.1", - "name": "DeepSeek V3.1", - "family": "deepseek", + "gpt-oss-120b": { + "id": "gpt-oss-120b", + "name": "GPT OSS 120B", + "family": "gpt-oss", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-08-25", - "last_updated": "2025-08-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.5, "output": 1.5 }, - "limit": { "context": 164000, "output": 131000 } - }, - "deepseek-ai/DeepSeek-V3-0324": { - "id": "deepseek-ai/DeepSeek-V3-0324", - "name": "DeepSeek V3 0324", - "family": "deepseek", + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.25, + "output": 0.69 + } + } + } + }, + "atomic-chat": { + "id": "atomic-chat", + "env": ["ATOMIC_CHAT_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "http://127.0.0.1:1337/v1", + "name": "Atomic Chat", + "doc": "https://atomic.chat", + "models": { + "gemma-4-E4B-it-IQ4_XS": { + "id": "gemma-4-E4B-it-IQ4_XS", + "name": "Gemma 4 E4B Instruct (IQ4_XS)", + "family": "gemma", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-03-24", - "last_updated": "2025-03-24", + "release_date": "2026-04-02", + "last_updated": "2026-04-02", "modalities": { "input": ["text"], "output": ["text"] }, "open_weights": true, - "cost": { "input": 0.77, "output": 0.77 }, - "limit": { "context": 164000, "output": 131000 } + "cost": { "input": 0, "output": 0 }, + "limit": { "context": 32768, "output": 8192 } }, - "deepseek-ai/DeepSeek-V3.2": { - "id": "deepseek-ai/DeepSeek-V3.2", - "name": "DeepSeek V3.2", - "family": "deepseek", + "gemma-4-E4B-it-MLX-4bit": { + "id": "gemma-4-E4B-it-MLX-4bit", + "name": "Gemma 4 E4B Instruct (MLX 4-bit)", + "family": "gemma", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2025-10", - "release_date": "2025-12-01", - "last_updated": "2026-03-06", + "release_date": "2026-04-02", + "last_updated": "2026-04-02", "modalities": { "input": ["text"], "output": ["text"] }, "open_weights": true, - "cost": { "input": 0.3, "output": 0.45 }, - "limit": { "context": 163800, "output": 131100 }, - "status": "deprecated" + "cost": { "input": 0, "output": 0 }, + "limit": { "context": 32768, "output": 8192 } }, - "moonshotai/Kimi-K2.5": { - "id": "moonshotai/Kimi-K2.5", - "name": "Kimi K2.5", - "family": "kimi", + "Qwen3_5-9B-Q4_K_M": { + "id": "Qwen3_5-9B-Q4_K_M", + "name": "Qwen 3.5 9B (Q4_K_M)", + "family": "qwen", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2025-12", - "release_date": "2026-01-30", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-05", + "last_updated": "2026-04-04", + "modalities": { "input": ["text", "image"], "output": ["text"] }, "open_weights": true, - "cost": { "input": 0.6, "output": 3 }, - "limit": { "context": 262144, "output": 8192 } + "cost": { "input": 0, "output": 0 }, + "limit": { "context": 32768, "output": 8192 } }, - "moonshotai/Kimi-K2-Instruct-0905": { - "id": "moonshotai/Kimi-K2-Instruct-0905", - "name": "Kimi K2 Instruct 0905", - "family": "kimi", + "Meta-Llama-3_1-8B-Instruct-GGUF": { + "id": "Meta-Llama-3_1-8B-Instruct-GGUF", + "name": "Meta Llama 3.1 8B Instruct (GGUF)", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-08", - "release_date": "2025-09-05", - "last_updated": "2026-03-06", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", "modalities": { "input": ["text"], "output": ["text"] }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.5 }, - "limit": { "context": 262144, "output": 262144 }, - "status": "deprecated" + "cost": { "input": 0, "output": 0 }, + "limit": { "context": 131072, "output": 4096 } }, - "moonshotai/Kimi-K2-Thinking": { - "id": "moonshotai/Kimi-K2-Thinking", - "name": "Kimi K2 Thinking", - "family": "kimi-thinking", - "attachment": false, - "reasoning": true, + "Qwen3_5-9B-MLX-4bit": { + "id": "Qwen3_5-9B-MLX-4bit", + "name": "Qwen 3.5 9B (MLX 4-bit)", + "family": "qwen", + "attachment": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-11-06", - "last_updated": "2026-03-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-05", + "last_updated": "2026-04-04", + "modalities": { "input": ["text", "image"], "output": ["text"] }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.5 }, - "limit": { "context": 262144, "output": 262144 }, - "status": "deprecated" + "cost": { "input": 0, "output": 0 }, + "limit": { "context": 32768, "output": 8192 } } } }, - "vercel": { - "id": "vercel", - "env": ["AI_GATEWAY_API_KEY"], - "npm": "@ai-sdk/gateway", - "name": "Vercel AI Gateway", - "doc": "https://github.com/vercel/ai/tree/5eb85cc45a259553501f535b8ac79a77d0e79223/packages/gateway", + "lmstudio": { + "id": "lmstudio", + "env": ["LMSTUDIO_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "http://127.0.0.1:1234/v1", + "name": "LMStudio", + "doc": "https://lmstudio.ai/models", "models": { - "meituan/longcat-flash-thinking": { - "id": "meituan/longcat-flash-thinking", - "name": "LongCat Flash Thinking", - "family": "longcat", + "openai/gpt-oss-20b": { + "id": "openai/gpt-oss-20b", + "name": "GPT OSS 20B", + "family": "gpt-oss", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-09-23", - "last_updated": "2025-09-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 1.5 }, - "limit": { "context": 128000, "output": 8192 } + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "meituan/longcat-flash-thinking-2601": { - "id": "meituan/longcat-flash-thinking-2601", - "name": "LongCat Flash Thinking 2601", - "family": "longcat", + "qwen/qwen3-coder-30b": { + "id": "qwen/qwen3-coder-30b", + "name": "Qwen3 Coder 30B", + "family": "qwen", "attachment": false, - "reasoning": true, - "tool_call": false, + "reasoning": false, + "tool_call": true, "temperature": true, - "release_date": "2026-03-13", - "last_updated": "2026-03-13", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "limit": { "context": 32768, "output": 32768 } + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "meituan/longcat-flash-chat": { - "id": "meituan/longcat-flash-chat", - "name": "LongCat Flash Chat", - "family": "longcat", + "qwen/qwen3-30b-a3b-2507": { + "id": "qwen/qwen3-30b-a3b-2507", + "name": "Qwen3 30B A3B 2507", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-08-30", - "last_updated": "2025-08-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "limit": { "context": 128000, "output": 8192 } - }, - "openai/gpt-5.2-codex": { - "id": "openai/gpt-5.2-codex", - "name": "GPT-5.2-Codex", - "family": "gpt-codex", + "knowledge": "2025-04", + "release_date": "2025-07-30", + "last_updated": "2025-07-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } + } + } + }, + "lucidquery": { + "id": "lucidquery", + "env": ["LUCIDQUERY_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://lucidquery.com/api/v1", + "name": "LucidQuery AI", + "doc": "https://lucidquery.com/api/docs", + "models": { + "lucidnova-rf1-100b": { + "id": "lucidnova-rf1-100b", + "name": "LucidNova RF1 100B", + "family": "nova", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-12", - "last_updated": "2025-12", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } - }, - "openai/text-embedding-3-large": { - "id": "openai/text-embedding-3-large", - "name": "text-embedding-3-large", - "family": "text-embedding", - "attachment": false, - "reasoning": false, - "tool_call": false, "temperature": false, - "release_date": "2024-01-25", - "last_updated": "2024-01-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.13, "output": 0 }, - "limit": { "context": 8192, "input": 6656, "output": 1536 } - }, - "openai/gpt-5.1-codex-mini": { - "id": "openai/gpt-5.1-codex-mini", - "name": "GPT-5.1 Codex mini", - "family": "gpt", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-05-16", - "last_updated": "2025-05-16", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-09-16", + "release_date": "2024-12-28", + "last_updated": "2025-09-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.03 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 120000, + "output": 8000 + }, + "cost": { + "input": 2, + "output": 5 + } }, - "openai/gpt-5.4-pro": { - "id": "openai/gpt-5.4-pro", - "name": "GPT 5.4 Pro", - "family": "gpt", + "lucidquery-nexus-coder": { + "id": "lucidquery-nexus-coder", + "name": "LucidQuery Nexus Coder", + "family": "lucid", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2026-03-05", - "last_updated": "2026-03-06", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-01", + "release_date": "2025-09-01", + "last_updated": "2025-09-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 30, "output": 180 }, - "limit": { "context": 1050000, "input": 922000, "output": 128000 } - }, - "openai/gpt-5.4-mini": { - "id": "openai/gpt-5.4-mini", - "name": "GPT 5.4 Mini", - "family": "gpt", - "attachment": true, + "limit": { + "context": 250000, + "output": 60000 + }, + "cost": { + "input": 2, + "output": 5 + } + } + } + }, + "moonshotai-cn": { + "id": "moonshotai-cn", + "env": ["MOONSHOT_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.moonshot.cn/v1", + "name": "Moonshot AI (China)", + "doc": "https://platform.moonshot.cn/docs/api/chat", + "models": { + "kimi-k2-thinking": { + "id": "kimi-k2-thinking", + "name": "Kimi K2 Thinking", + "family": "kimi-thinking", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2026-03-17", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.75, "output": 4.5, "cache_read": 0.075 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "knowledge": "2024-08", + "release_date": "2025-11-06", + "last_updated": "2025-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 2.5, + "cache_read": 0.15 + } }, - "openai/gpt-5-pro": { - "id": "openai/gpt-5-pro", - "name": "GPT-5 pro", - "family": "gpt", - "attachment": true, - "reasoning": true, + "kimi-k2-0711-preview": { + "id": "kimi-k2-0711-preview", + "name": "Kimi K2 0711", + "family": "kimi", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, "knowledge": "2024-10", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text", "image"] }, - "open_weights": false, - "cost": { "input": 15, "output": 120 }, - "limit": { "context": 400000, "input": 128000, "output": 272000 } + "release_date": "2025-07-14", + "last_updated": "2025-07-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.6, + "output": 2.5, + "cache_read": 0.15 + } }, - "openai/gpt-5.1-thinking": { - "id": "openai/gpt-5.1-thinking", - "name": "GPT 5.1 Thinking", - "family": "gpt", - "attachment": true, - "reasoning": true, + "kimi-k2-turbo-preview": { + "id": "kimi-k2-turbo-preview", + "name": "Kimi K2 Turbo", + "family": "kimi", + "attachment": false, + "reasoning": false, "tool_call": true, - "temperature": false, + "temperature": true, "knowledge": "2024-10", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text", "image"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.13 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 2.4, + "output": 10, + "cache_read": 0.6 + } }, - "openai/gpt-5.3-codex": { - "id": "openai/gpt-5.3-codex", - "name": "GPT 5.3 Codex", - "family": "gpt", + "kimi-k2.6": { + "id": "kimi-k2.6", + "name": "Kimi K2.6", + "family": "kimi-k2.6", "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2026-02-24", - "last_updated": "2026-02-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "knowledge": "2025-01", + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.95, + "output": 4, + "cache_read": 0.16 + } }, - "openai/gpt-5.1-codex-max": { - "id": "openai/gpt-5.1-codex-max", - "name": "GPT 5.1 Codex Max", - "family": "gpt", - "attachment": true, + "kimi-k2-thinking-turbo": { + "id": "kimi-k2-thinking-turbo", + "name": "Kimi K2 Thinking Turbo", + "family": "kimi-thinking", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.13 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "knowledge": "2024-08", + "release_date": "2025-11-06", + "last_updated": "2025-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 1.15, + "output": 8, + "cache_read": 0.15 + } }, - "openai/codex-mini": { - "id": "openai/codex-mini", - "name": "Codex Mini", - "family": "gpt-codex-mini", - "attachment": true, + "kimi-k2.5": { + "id": "kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi-k2.5", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-05-16", - "last_updated": "2025-05-16", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.5, "output": 6, "cache_read": 0.38 }, - "limit": { "context": 200000, "input": 100000, "output": 100000 } - }, - "openai/gpt-3.5-turbo": { - "id": "openai/gpt-3.5-turbo", - "name": "GPT-3.5 Turbo", - "family": "gpt", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2021-09", - "release_date": "2023-03-01", - "last_updated": "2023-03-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 1.5 }, - "limit": { "context": 16385, "input": 12289, "output": 4096 } + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 3, + "cache_read": 0.1 + } }, - "openai/gpt-3.5-turbo-instruct": { - "id": "openai/gpt-3.5-turbo-instruct", - "name": "GPT-3.5 Turbo Instruct", - "family": "gpt", + "kimi-k2-0905-preview": { + "id": "kimi-k2-0905-preview", + "name": "Kimi K2 0905", + "family": "kimi", "attachment": false, "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2021-09", - "release_date": "2023-03-01", - "last_updated": "2023-03-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.5, "output": 2 }, - "limit": { "context": 8192, "input": 4096, "output": 4096 } - }, - "openai/gpt-oss-120b": { - "id": "openai/gpt-oss-120b", - "name": "GPT OSS 120B", - "family": "gpt-oss", - "attachment": false, - "reasoning": true, "tool_call": true, "temperature": true, "knowledge": "2024-10", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.5 }, - "limit": { "context": 131072, "output": 131072 } - }, - "openai/gpt-5.4": { - "id": "openai/gpt-5.4", - "name": "GPT 5.4", - "family": "gpt", + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 2.5, + "cache_read": 0.15 + } + } + } + }, + "azure-cognitive-services": { + "id": "azure-cognitive-services", + "env": ["AZURE_COGNITIVE_SERVICES_RESOURCE_NAME", "AZURE_COGNITIVE_SERVICES_API_KEY"], + "npm": "@ai-sdk/azure", + "name": "Azure Cognitive Services", + "doc": "https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models", + "models": { + "claude-haiku-4-5": { + "id": "claude-haiku-4-5", + "name": "Claude Haiku 4.5", + "family": "claude-haiku", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-03-05", - "last_updated": "2026-03-06", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-02-31", + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 15, "cache_read": 0.25 }, - "limit": { "context": 1050000, "input": 922000, "output": 128000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://${AZURE_COGNITIVE_SERVICES_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } }, - "openai/gpt-5-chat": { - "id": "openai/gpt-5-chat", - "name": "GPT-5 Chat", - "family": "gpt", + "claude-opus-4-1": { + "id": "claude-opus-4-1", + "name": "Claude Opus 4.1", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text", "image"] }, + "knowledge": "2025-03-31", + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.13 }, - "limit": { "context": 128000, "input": 111616, "output": 16384 } + "limit": { + "context": 200000, + "output": 32000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://${AZURE_COGNITIVE_SERVICES_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "openai/gpt-5.4-nano": { - "id": "openai/gpt-5.4-nano", - "name": "GPT 5.4 Nano", - "family": "gpt", + "claude-opus-4-5": { + "id": "claude-opus-4-5", + "name": "Claude Opus 4.5", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-03-17", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-03-31", + "release_date": "2025-11-24", + "last_updated": "2025-08-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.19999999999999998, "output": 1.25, "cache_read": 0.02 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://${AZURE_COGNITIVE_SERVICES_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "openai/gpt-5.3-chat": { - "id": "openai/gpt-5.3-chat", - "name": "GPT-5.3 Chat", - "family": "gpt", - "attachment": true, + "kimi-k2.6": { + "id": "kimi-k2.6", + "name": "Kimi K2.6", + "family": "kimi", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": true, + "structured_output": true, "temperature": true, - "release_date": "2026-03-03", - "last_updated": "2026-03-06", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 128000, "input": 111616, "output": 16384 } - }, - "openai/text-embedding-ada-002": { - "id": "openai/text-embedding-ada-002", - "name": "text-embedding-ada-002", - "family": "text-embedding", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2022-12-15", - "last_updated": "2022-12-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0 }, - "limit": { "context": 8192, "input": 6656, "output": 1536 } + "knowledge": "2025-01", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible", + "api": "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/models", + "shape": "completions" + }, + "cost": { + "input": 0.95, + "output": 4 + } }, - "openai/gpt-5.2-chat": { - "id": "openai/gpt-5.2-chat", - "name": "GPT-5.2 Chat", - "family": "gpt", + "claude-opus-4-6": { + "id": "claude-opus-4-6", + "name": "Claude Opus 4.6", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.18 }, - "limit": { "context": 128000, "input": 111616, "output": 16384 } + "limit": { + "context": 200000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://${AZURE_COGNITIVE_SERVICES_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25, + "tiers": [ + { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5 + } + } }, - "openai/gpt-5.2": { - "id": "openai/gpt-5.2", - "name": "GPT-5.2", - "family": "gpt", + "claude-sonnet-4-5": { + "id": "claude-sonnet-4-5", + "name": "Claude Sonnet 4.5", + "family": "claude-sonnet", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-07-31", + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.18 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://${AZURE_COGNITIVE_SERVICES_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "openai/gpt-4o-mini-search-preview": { - "id": "openai/gpt-4o-mini-search-preview", - "name": "GPT 4o Mini Search Preview", - "family": "gpt-mini", + "mai-ds-r1": { + "id": "mai-ds-r1", + "name": "MAI-DS-R1", + "family": "mai", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": false, - "structured_output": false, "temperature": true, - "knowledge": "2023-09", - "release_date": "2025-01", - "last_updated": "2025-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-06", + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 128000, "input": 111616, "output": 16384 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 1.35, + "output": 5.4 + } }, - "openai/gpt-5.1-instant": { - "id": "openai/gpt-5.1-instant", - "name": "GPT-5.1 Instant", - "family": "gpt", + "llama-4-maverick-17b-128e-instruct-fp8": { + "id": "llama-4-maverick-17b-128e-instruct-fp8", + "name": "Llama 4 Maverick 17B 128E Instruct FP8", + "family": "llama", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text", "image"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.13 }, - "limit": { "context": 128000, "input": 111616, "output": 16384 } + "knowledge": "2024-08", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.25, + "output": 1 + } }, - "openai/gpt-oss-safeguard-20b": { - "id": "openai/gpt-oss-safeguard-20b", - "name": "gpt-oss-safeguard-20b", - "family": "gpt-oss", + "codestral-2501": { + "id": "codestral-2501", + "name": "Codestral 25.01", + "family": "codestral", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-03", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.08, "output": 0.3, "cache_read": 0.04 }, - "limit": { "context": 131072, "input": 65536, "output": 65536 } + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.3, + "output": 0.9 + } }, - "openai/o3-pro": { - "id": "openai/o3-pro", - "name": "o3 Pro", - "family": "o-pro", - "attachment": true, + "gpt-5.1-codex": { + "id": "gpt-5.1-codex", + "name": "GPT-5.1 Codex", + "family": "gpt-codex", + "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": false, - "knowledge": "2024-10", - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2024-09-30", + "release_date": "2025-11-14", + "last_updated": "2025-11-14", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text", "image", "audio"] + }, "open_weights": false, - "cost": { "input": 20, "output": 80 }, - "limit": { "context": 200000, "input": 100000, "output": 100000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "openai/gpt-oss-20b": { - "id": "openai/gpt-oss-20b", - "name": "GPT OSS 20B", - "family": "gpt-oss", + "kimi-k2-thinking": { + "id": "kimi-k2-thinking", + "name": "Kimi K2 Thinking", + "family": "kimi-thinking", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-08", + "release_date": "2025-11-06", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.07, "output": 0.3 }, - "limit": { "context": 131072, "input": 98304, "output": 32768 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 2.5, + "cache_read": 0.15 + } }, - "openai/text-embedding-3-small": { - "id": "openai/text-embedding-3-small", - "name": "text-embedding-3-small", - "family": "text-embedding", + "deepseek-r1-0528": { + "id": "deepseek-r1-0528", + "name": "DeepSeek-R1-0528", + "family": "deepseek-thinking", "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2024-01-25", - "last_updated": "2024-01-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.02, "output": 0 }, - "limit": { "context": 8192, "input": 6656, "output": 1536 } - }, - "openai/o3-deep-research": { - "id": "openai/o3-deep-research", - "name": "o3-deep-research", - "family": "o", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2024-10", - "release_date": "2024-06-26", - "last_updated": "2024-06-26", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 10, "output": 40, "cache_read": 2.5 }, - "limit": { "context": 200000, "input": 100000, "output": 100000 } - }, - "openai/gpt-5.1-codex": { - "id": "openai/gpt-5.1-codex", - "name": "GPT-5.1-Codex", - "family": "gpt", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.13 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } - }, - "openai/gpt-5.2-pro": { - "id": "openai/gpt-5.2-pro", - "name": "GPT 5.2 ", - "family": "gpt", - "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 21, "output": 168 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } - }, - "openai/o4-mini": { - "id": "openai/o4-mini", - "name": "o4-mini", - "family": "o-mini", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.28 }, - "limit": { "context": 200000, "output": 100000 } + "knowledge": "2024-07", + "release_date": "2025-05-28", + "last_updated": "2025-05-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 163840 + }, + "cost": { + "input": 1.35, + "output": 5.4 + } }, - "openai/gpt-5-codex": { - "id": "openai/gpt-5-codex", - "name": "GPT-5-Codex", - "family": "gpt-codex", + "gpt-3.5-turbo-instruct": { + "id": "gpt-3.5-turbo-instruct", + "name": "GPT-3.5 Turbo Instruct", + "family": "gpt", "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "temperature": true, + "knowledge": "2021-08", + "release_date": "2023-09-21", + "last_updated": "2023-09-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 4096, + "output": 4096 + }, + "cost": { + "input": 1.5, + "output": 2 + } }, - "openai/gpt-4.1-nano": { - "id": "openai/gpt-4.1-nano", - "name": "GPT-4.1 nano", - "family": "gpt-nano", + "mistral-medium-2505": { + "id": "mistral-medium-2505", + "name": "Mistral Medium 3", + "family": "mistral-medium", "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2025-05-07", + "last_updated": "2025-05-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.03 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.4, + "output": 2 + } }, - "openai/gpt-5-nano": { - "id": "openai/gpt-5-nano", - "name": "GPT-5 Nano", - "family": "gpt-nano", - "attachment": true, + "phi-4-reasoning-plus": { + "id": "phi-4-reasoning-plus", + "name": "Phi-4-reasoning-plus", + "family": "phi", + "attachment": false, "reasoning": true, - "tool_call": true, - "structured_output": true, + "tool_call": false, + "temperature": true, + "knowledge": "2023-10", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32000, + "output": 4096 + }, + "cost": { + "input": 0.125, + "output": 0.5 + } + }, + "cohere-embed-v3-english": { + "id": "cohere-embed-v3-english", + "name": "Embed v3 English", + "family": "cohere-embed", + "attachment": false, + "reasoning": false, + "tool_call": false, "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.05, "output": 0.4, "cache_read": 0.005 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "release_date": "2023-11-07", + "last_updated": "2023-11-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 512, + "output": 1024 + }, + "cost": { + "input": 0.1, + "output": 0 + } }, - "openai/gpt-4.1-mini": { - "id": "openai/gpt-4.1-mini", - "name": "GPT-4.1 mini", - "family": "gpt-mini", - "attachment": true, + "gpt-4-32k": { + "id": "gpt-4-32k", + "name": "GPT-4 32K", + "family": "gpt", + "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2023-11", + "release_date": "2023-03-14", + "last_updated": "2023-03-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 1.6, "cache_read": 0.1 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 60, + "output": 120 + } }, - "openai/o3": { - "id": "openai/o3", - "name": "o3", - "family": "o", + "gpt-5": { + "id": "gpt-5", + "name": "GPT-5", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": false, - "knowledge": "2024-05", - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-09-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.13 + } }, - "openai/o1": { - "id": "openai/o1", - "name": "o1", - "family": "o", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2023-09", - "release_date": "2024-12-05", - "last_updated": "2024-12-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 60, "cache_read": 7.5 }, - "limit": { "context": 200000, "output": 100000 } + "phi-4": { + "id": "phi-4", + "name": "Phi-4", + "family": "phi", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "knowledge": "2023-10", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.125, + "output": 0.5 + } }, - "openai/gpt-4.1": { - "id": "openai/gpt-4.1", - "name": "GPT-4.1", + "gpt-3.5-turbo-0613": { + "id": "gpt-3.5-turbo-0613", + "name": "GPT-3.5 Turbo 0613", "family": "gpt", - "attachment": true, + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, + "tool_call": false, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2021-08", + "release_date": "2023-06-13", + "last_updated": "2023-06-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 16384, + "output": 16384 + }, + "cost": { + "input": 3, + "output": 4 + } }, - "openai/gpt-4o-mini": { - "id": "openai/gpt-4o-mini", - "name": "GPT-4o mini", - "family": "gpt-mini", - "attachment": true, + "phi-3-medium-128k-instruct": { + "id": "phi-3-medium-128k-instruct", + "name": "Phi-3-medium-instruct (128k)", + "family": "phi", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, + "tool_call": false, "temperature": true, - "knowledge": "2023-09", - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.6, "cache_read": 0.08 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2023-10", + "release_date": "2024-04-23", + "last_updated": "2024-04-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.17, + "output": 0.68 + } }, - "openai/gpt-5-mini": { - "id": "openai/gpt-5-mini", - "name": "GPT-5 Mini", - "family": "gpt-mini", - "attachment": true, + "deepseek-v3.2": { + "id": "deepseek-v3.2", + "name": "DeepSeek-V3.2", + "family": "deepseek", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.025 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "temperature": true, + "knowledge": "2024-07", + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.58, + "output": 1.68 + } }, - "openai/gpt-4o": { - "id": "openai/gpt-4o", - "name": "GPT-4o", + "phi-3-small-128k-instruct": { + "id": "phi-3-small-128k-instruct", + "name": "Phi-3-small-instruct (128k)", + "family": "phi", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "knowledge": "2023-10", + "release_date": "2024-04-23", + "last_updated": "2024-04-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } + }, + "gpt-3.5-turbo-0301": { + "id": "gpt-3.5-turbo-0301", + "name": "GPT-3.5 Turbo 0301", "family": "gpt", - "attachment": true, + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, + "tool_call": false, "temperature": true, - "knowledge": "2023-09", - "release_date": "2024-05-13", - "last_updated": "2024-08-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2021-08", + "release_date": "2023-03-01", + "last_updated": "2023-03-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 10, "cache_read": 1.25 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 4096, + "output": 4096 + }, + "cost": { + "input": 1.5, + "output": 2 + } }, - "openai/gpt-4-turbo": { - "id": "openai/gpt-4-turbo", - "name": "GPT-4 Turbo", - "family": "gpt", - "attachment": true, + "phi-4-mini": { + "id": "phi-4-mini", + "name": "Phi-4-mini", + "family": "phi", + "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": false, "temperature": true, - "knowledge": "2023-12", - "release_date": "2023-11-06", - "last_updated": "2024-04-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 10, "output": 30 }, - "limit": { "context": 128000, "output": 4096 } + "knowledge": "2023-10", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.075, + "output": 0.3 + } }, - "openai/gpt-5": { - "id": "openai/gpt-5", - "name": "GPT-5", - "family": "gpt", - "attachment": true, + "gpt-5-codex": { + "id": "gpt-5-codex", + "name": "GPT-5-Codex", + "family": "gpt-codex", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": false, "knowledge": "2024-09-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-09-15", + "last_updated": "2025-09-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.13 + } }, - "openai/o3-mini": { - "id": "openai/o3-mini", - "name": "o3-mini", - "family": "o-mini", + "meta-llama-3-8b-instruct": { + "id": "meta-llama-3-8b-instruct", + "name": "Meta-Llama-3-8B-Instruct", + "family": "llama", "attachment": false, - "reasoning": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2024-04-18", + "last_updated": "2024-04-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 2048 + }, + "cost": { + "input": 0.3, + "output": 0.61 + } + }, + "gpt-4": { + "id": "gpt-4", + "name": "GPT-4", + "family": "gpt", + "attachment": false, + "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2024-12-20", - "last_updated": "2025-01-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2023-11", + "release_date": "2023-03-14", + "last_updated": "2023-03-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.55 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 60, + "output": 120 + } }, - "prime-intellect/intellect-3": { - "id": "prime-intellect/intellect-3", - "name": "INTELLECT 3", - "family": "intellect", + "phi-4-mini-reasoning": { + "id": "phi-4-mini-reasoning", + "name": "Phi-4-mini-reasoning", + "family": "phi", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-11-26", - "last_updated": "2025-11-26", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 1.1 }, - "limit": { "context": 131072, "output": 131072 } + "knowledge": "2023-10", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.075, + "output": 0.3 + } }, - "bfl/flux-pro-1.1": { - "id": "bfl/flux-pro-1.1", - "name": "FLUX1.1 [pro]", - "family": "flux", + "meta-llama-3.1-70b-instruct": { + "id": "meta-llama-3.1-70b-instruct", + "name": "Meta-Llama-3.1-70B-Instruct", + "family": "llama", "attachment": false, "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2024-10", - "last_updated": "2024-10", - "modalities": { "input": ["text"], "output": ["image"] }, - "open_weights": false, - "limit": { "context": 512, "output": 0 } + "tool_call": true, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 2.68, + "output": 3.54 + } }, - "bfl/flux-pro-1.1-ultra": { - "id": "bfl/flux-pro-1.1-ultra", - "name": "FLUX1.1 [pro] Ultra", - "family": "flux", + "phi-3-mini-4k-instruct": { + "id": "phi-3-mini-4k-instruct", + "name": "Phi-3-mini-instruct (4k)", + "family": "phi", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": false, - "release_date": "2024-11", - "last_updated": "2024-11", - "modalities": { "input": ["text"], "output": ["image"] }, - "open_weights": false, - "limit": { "context": 512, "output": 0 } + "temperature": true, + "knowledge": "2023-10", + "release_date": "2024-04-23", + "last_updated": "2024-04-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 4096, + "output": 1024 + }, + "cost": { + "input": 0.13, + "output": 0.52 + } }, - "bfl/flux-pro-1.0-fill": { - "id": "bfl/flux-pro-1.0-fill", - "name": "FLUX.1 Fill [pro]", - "family": "flux", + "deepseek-v3.1": { + "id": "deepseek-v3.1", + "name": "DeepSeek-V3.1", + "family": "deepseek", "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2024-10", - "last_updated": "2024-10", - "modalities": { "input": ["text"], "output": ["image"] }, - "open_weights": false, - "limit": { "context": 512, "output": 0 } + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-07", + "release_date": "2025-08-21", + "last_updated": "2025-08-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.56, + "output": 1.68 + } }, - "bfl/flux-kontext-max": { - "id": "bfl/flux-kontext-max", - "name": "FLUX.1 Kontext Max", - "family": "flux", + "text-embedding-3-small": { + "id": "text-embedding-3-small", + "name": "text-embedding-3-small", + "family": "text-embedding", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": false, - "release_date": "2025-06", - "last_updated": "2025-06", - "modalities": { "input": ["text"], "output": ["image"] }, + "release_date": "2024-01-25", + "last_updated": "2024-01-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 512, "output": 0 } + "limit": { + "context": 8191, + "output": 1536 + }, + "cost": { + "input": 0.02, + "output": 0 + } }, - "bfl/flux-kontext-pro": { - "id": "bfl/flux-kontext-pro", - "name": "FLUX.1 Kontext Pro", - "family": "flux", + "gpt-3.5-turbo-1106": { + "id": "gpt-3.5-turbo-1106", + "name": "GPT-3.5 Turbo 1106", + "family": "gpt", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": false, - "release_date": "2025-06", - "last_updated": "2025-06", - "modalities": { "input": ["text"], "output": ["image"] }, + "temperature": true, + "knowledge": "2021-08", + "release_date": "2023-11-06", + "last_updated": "2023-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 512, "output": 0 } + "limit": { + "context": 16384, + "output": 16384 + }, + "cost": { + "input": 1, + "output": 2 + } }, - "cohere/embed-v4.0": { - "id": "cohere/embed-v4.0", - "name": "Embed v4.0", - "family": "cohere-embed", - "attachment": false, + "model-router": { + "id": "model-router", + "name": "Model Router", + "family": "model-router", + "attachment": true, "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2025-04-15", - "last_updated": "2025-04-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "release_date": "2025-05-19", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.12, "output": 0 }, - "limit": { "context": 8192, "output": 1536 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.14, + "output": 0 + } }, - "cohere/command-a": { - "id": "cohere/command-a", - "name": "Command A", - "family": "command", - "attachment": false, + "mistral-small-2503": { + "id": "mistral-small-2503", + "name": "Mistral Small 3.1", + "family": "mistral-small", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-03-13", - "last_updated": "2025-03-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-09", + "release_date": "2025-03-01", + "last_updated": "2025-03-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 256000, "output": 8000 } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } }, - "kwaipilot/kat-coder-pro-v1": { - "id": "kwaipilot/kat-coder-pro-v1", - "name": "KAT-Coder-Pro V1", - "family": "kat-coder", + "o1": { + "id": "o1", + "name": "o1", + "family": "o", "attachment": false, "reasoning": true, - "tool_call": false, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-10-24", - "last_updated": "2025-10-24", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": false, + "knowledge": "2023-09", + "release_date": "2024-12-05", + "last_updated": "2024-12-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 256000, "output": 32000 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 15, + "output": 60, + "cache_read": 7.5 + } }, - "kwaipilot/kat-coder-pro-v2": { - "id": "kwaipilot/kat-coder-pro-v2", - "name": "Kat Coder Pro V2", - "family": "kat-coder", - "attachment": false, + "grok-4-fast-reasoning": { + "id": "grok-4-fast-reasoning", + "name": "Grok 4 Fast (Reasoning)", + "family": "grok", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-03-27", - "last_updated": "2026-03-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-09-19", + "last_updated": "2025-09-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.06 }, - "limit": { "context": 256000, "output": 256000 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "morph/morph-v3-fast": { - "id": "morph/morph-v3-fast", - "name": "Morph v3 Fast", - "family": "morph", - "attachment": false, - "reasoning": false, - "tool_call": false, + "gpt-5.1": { + "id": "gpt-5.1", + "name": "GPT-5.1", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": false, - "release_date": "2024-08-15", - "last_updated": "2024-08-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-09-30", + "release_date": "2025-11-14", + "last_updated": "2025-11-14", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text", "image", "audio"] + }, "open_weights": false, - "cost": { "input": 0.8, "output": 1.2 }, - "limit": { "context": 16000, "output": 16000 } + "limit": { + "context": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "morph/morph-v3-large": { - "id": "morph/morph-v3-large", - "name": "Morph v3 Large", - "family": "morph", + "cohere-embed-v3-multilingual": { + "id": "cohere-embed-v3-multilingual", + "name": "Embed v3 Multilingual", + "family": "cohere-embed", "attachment": false, "reasoning": false, "tool_call": false, "temperature": false, - "release_date": "2024-08-15", - "last_updated": "2024-08-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.9, "output": 1.9 }, - "limit": { "context": 32000, "output": 32000 } + "release_date": "2023-11-07", + "last_updated": "2023-11-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 512, + "output": 1024 + }, + "cost": { + "input": 0.1, + "output": 0 + } }, - "anthropic/claude-opus-4.6": { - "id": "anthropic/claude-opus-4.6", - "name": "Claude Opus 4.6", - "family": "claude-opus", - "attachment": true, + "o1-preview": { + "id": "o1-preview", + "name": "o1-preview", + "family": "o", + "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": true, - "temperature": true, - "knowledge": "2025-05", - "release_date": "2026-02", - "last_updated": "2026-02", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": false, + "knowledge": "2023-09", + "release_date": "2024-09-12", + "last_updated": "2024-09-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 1000000, "output": 128000 } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 16.5, + "output": 66, + "cache_read": 8.25 + } }, - "anthropic/claude-haiku-4.5": { - "id": "anthropic/claude-haiku-4.5", - "name": "Claude Haiku 4.5", - "family": "claude-haiku", - "attachment": true, - "reasoning": true, - "tool_call": true, - "interleaved": true, + "gpt-3.5-turbo-0125": { + "id": "gpt-3.5-turbo-0125", + "name": "GPT-3.5 Turbo 0125", + "family": "gpt", + "attachment": false, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2025-02-28", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2021-08", + "release_date": "2024-01-25", + "last_updated": "2024-01-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 16384, + "output": 16384 + }, + "cost": { + "input": 0.5, + "output": 1.5 + } }, - "anthropic/claude-sonnet-4.6": { - "id": "anthropic/claude-sonnet-4.6", - "name": "Claude Sonnet 4.6", - "family": "claude-sonnet", - "attachment": true, + "gpt-5.1-codex-mini": { + "id": "gpt-5.1-codex-mini", + "name": "GPT-5.1 Codex Mini", + "family": "gpt-codex", + "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": true, - "temperature": true, - "knowledge": "2025-08", - "release_date": "2026-02-17", - "last_updated": "2026-02-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-14", + "last_updated": "2025-11-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75, - "context_over_200k": { "input": 6, "output": 22.5, "cache_read": 0.6, "cache_write": 7.5 } + "limit": { + "context": 400000, + "output": 128000 }, - "limit": { "context": 1000000, "output": 128000 } + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.025 + } }, - "anthropic/claude-opus-4.5": { - "id": "anthropic/claude-opus-4.5", - "name": "Claude Opus 4.5", - "family": "claude-opus", + "cohere-embed-v-4-0": { + "id": "cohere-embed-v-4-0", + "name": "Embed v4", + "family": "cohere-embed", "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2025-04-15", + "last_updated": "2025-04-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 1536 + }, + "cost": { + "input": 0.12, + "output": 0 + } + }, + "gpt-5.2-codex": { + "id": "gpt-5.2-codex", + "name": "GPT-5.2 Codex", + "family": "gpt-codex", + "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-11-24", - "last_updated": "2025-11-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-01-14", + "last_updated": "2026-01-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "anthropic/claude-3.5-sonnet-20240620": { - "id": "anthropic/claude-3.5-sonnet-20240620", - "name": "Claude 3.5 Sonnet (2024-06-20)", - "family": "claude-sonnet", + "gpt-4-turbo-vision": { + "id": "gpt-4-turbo-vision", + "name": "GPT-4 Turbo Vision", + "family": "gpt", "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-06-20", - "last_updated": "2024-06-20", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2023-11", + "release_date": "2023-11-06", + "last_updated": "2024-04-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 200000, "output": 8192 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 10, + "output": 30 + } }, - "anthropic/claude-opus-4": { - "id": "anthropic/claude-opus-4", - "name": "Claude Opus 4", - "family": "claude-opus", + "gpt-5.1-chat": { + "id": "gpt-5.1-chat", + "name": "GPT-5.1 Chat", + "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-14", + "last_updated": "2025-11-14", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text", "image", "audio"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "anthropic/claude-sonnet-4.5": { - "id": "anthropic/claude-sonnet-4.5", - "name": "Claude Sonnet 4.5", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, + "meta-llama-3.1-405b-instruct": { + "id": "meta-llama-3.1-405b-instruct", + "name": "Meta-Llama-3.1-405B-Instruct", + "family": "llama", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "knowledge": "2023-12", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 5.33, + "output": 16 + } }, - "anthropic/claude-3-haiku": { - "id": "anthropic/claude-3-haiku", - "name": "Claude Haiku 3", - "family": "claude-haiku", + "llama-3.2-11b-vision-instruct": { + "id": "llama-3.2-11b-vision-instruct", + "name": "Llama-3.2-11B-Vision-Instruct", + "family": "llama", "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-08-31", - "release_date": "2024-03-13", - "last_updated": "2024-03-13", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 1.25, "cache_read": 0.03, "cache_write": 0.3 }, - "limit": { "context": 200000, "output": 4096 } + "knowledge": "2023-12", + "release_date": "2024-09-25", + "last_updated": "2024-09-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.37, + "output": 0.37 + } }, - "anthropic/claude-3.5-sonnet": { - "id": "anthropic/claude-3.5-sonnet", - "name": "Claude Sonnet 3.5 v2", - "family": "claude-sonnet", - "attachment": true, - "reasoning": false, + "cohere-command-a": { + "id": "cohere-command-a", + "name": "Command A", + "family": "command-a", + "attachment": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-04-30", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 8192 } + "knowledge": "2024-06-01", + "release_date": "2025-03-13", + "last_updated": "2025-03-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 8000 + }, + "cost": { + "input": 2.5, + "output": 10 + } }, - "anthropic/claude-3.5-haiku": { - "id": "anthropic/claude-3.5-haiku", - "name": "Claude Haiku 3.5", - "family": "claude-haiku", - "attachment": true, + "mistral-large-2411": { + "id": "mistral-large-2411", + "name": "Mistral Large 24.11", + "family": "mistral-large", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-07-31", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2024-09", + "release_date": "2024-11-01", + "last_updated": "2024-11-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.8, "output": 4, "cache_read": 0.08, "cache_write": 1 }, - "limit": { "context": 200000, "output": 8192 } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 6 + } }, - "anthropic/claude-sonnet-4": { - "id": "anthropic/claude-sonnet-4", - "name": "Claude Sonnet 4", - "family": "claude-sonnet", + "gpt-5.2": { + "id": "gpt-5.2", + "name": "GPT-5.2", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.125 + } }, - "anthropic/claude-3.7-sonnet": { - "id": "anthropic/claude-3.7-sonnet", - "name": "Claude Sonnet 3.7", - "family": "claude-sonnet", - "attachment": true, + "deepseek-v3.2-speciale": { + "id": "deepseek-v3.2-speciale", + "name": "DeepSeek-V3.2-Speciale", + "family": "deepseek", + "attachment": false, "reasoning": true, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2024-10-31", - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "knowledge": "2024-07", + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.58, + "output": 1.68 + } }, - "anthropic/claude-opus-4.1": { - "id": "anthropic/claude-opus-4.1", - "name": "Claude Opus 4", - "family": "claude-opus", - "attachment": true, + "deepseek-r1": { + "id": "deepseek-r1", + "name": "DeepSeek-R1", + "family": "deepseek-thinking", + "attachment": false, "reasoning": true, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "knowledge": "2024-07", + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 163840 + }, + "cost": { + "input": 1.35, + "output": 5.4 + } }, - "anthropic/claude-3-opus": { - "id": "anthropic/claude-3-opus", - "name": "Claude Opus 3", - "family": "claude-opus", + "llama-3.2-90b-vision-instruct": { + "id": "llama-3.2-90b-vision-instruct", + "name": "Llama-3.2-90B-Vision-Instruct", + "family": "llama", "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-08-31", - "release_date": "2024-02-29", - "last_updated": "2024-02-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 4096 } + "knowledge": "2023-12", + "release_date": "2024-09-25", + "last_updated": "2024-09-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 2.04, + "output": 2.04 + } }, - "recraft/recraft-v2": { - "id": "recraft/recraft-v2", - "name": "Recraft V2", - "family": "recraft", + "text-embedding-ada-002": { + "id": "text-embedding-ada-002", + "name": "text-embedding-ada-002", + "family": "text-embedding", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": false, - "release_date": "2024-03", - "last_updated": "2024-03", - "modalities": { "input": ["text"], "output": ["image"] }, + "release_date": "2022-12-15", + "last_updated": "2022-12-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 512, "output": 0 } + "limit": { + "context": 8192, + "output": 1536 + }, + "cost": { + "input": 0.1, + "output": 0 + } }, - "recraft/recraft-v3": { - "id": "recraft/recraft-v3", - "name": "Recraft V3", - "family": "recraft", + "gpt-5.3-codex": { + "id": "gpt-5.3-codex", + "name": "GPT-5.3 Codex", + "family": "gpt-codex", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": false, - "release_date": "2024-10", - "last_updated": "2024-10", - "modalities": { "input": ["text"], "output": ["image"] }, + "knowledge": "2025-08-31", + "release_date": "2026-02-24", + "last_updated": "2026-02-24", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 512, "output": 0 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "deepseek/deepseek-v3.1-terminus": { - "id": "deepseek/deepseek-v3.1-terminus", - "name": "DeepSeek V3.1 Terminus", - "family": "deepseek", + "phi-3-small-8k-instruct": { + "id": "phi-3-small-8k-instruct", + "name": "Phi-3-small-instruct (8k)", + "family": "phi", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-09-22", - "last_updated": "2025-09-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-10", + "release_date": "2024-04-23", + "last_updated": "2024-04-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 2048 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } + }, + "meta-llama-3-70b-instruct": { + "id": "meta-llama-3-70b-instruct", + "name": "Meta-Llama-3-70B-Instruct", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2024-04-18", + "last_updated": "2024-04-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.27, "output": 1 }, - "limit": { "context": 131072, "output": 65536 } + "limit": { + "context": 8192, + "output": 2048 + }, + "cost": { + "input": 2.68, + "output": 3.54 + } }, - "deepseek/deepseek-v3.2-thinking": { - "id": "deepseek/deepseek-v3.2-thinking", - "name": "DeepSeek V3.2 Thinking", - "family": "deepseek-thinking", - "attachment": false, + "gpt-5-nano": { + "id": "gpt-5-nano", + "name": "GPT-5 Nano", + "family": "gpt-nano", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": true, - "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.28, "output": 0.42, "cache_read": 0.03 }, - "limit": { "context": 128000, "output": 64000 } + "limit": { + "context": 272000, + "output": 128000 + }, + "cost": { + "input": 0.05, + "output": 0.4, + "cache_read": 0.01 + } }, - "deepseek/deepseek-v3": { - "id": "deepseek/deepseek-v3", - "name": "DeepSeek V3 0324", - "family": "deepseek", - "attachment": false, - "reasoning": false, + "gpt-5-mini": { + "id": "gpt-5-mini", + "name": "GPT-5 Mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-07", - "release_date": "2024-12-26", - "last_updated": "2024-12-26", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.77, "output": 0.77 }, - "limit": { "context": 163840, "output": 16384 } + "limit": { + "context": 272000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.03 + } }, - "deepseek/deepseek-v3.2-exp": { - "id": "deepseek/deepseek-v3.2-exp", - "name": "DeepSeek V3.2 Exp", - "family": "deepseek", + "phi-4-reasoning": { + "id": "phi-4-reasoning", + "name": "Phi-4-reasoning", + "family": "phi", "attachment": false, "reasoning": true, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.27, "output": 0.4 }, - "limit": { "context": 163840, "output": 163840 } + "knowledge": "2023-10", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32000, + "output": 4096 + }, + "cost": { + "input": 0.125, + "output": 0.5 + } }, - "deepseek/deepseek-v3.1": { - "id": "deepseek/deepseek-v3.1", - "name": "DeepSeek-V3.1", - "family": "deepseek", + "phi-3-mini-128k-instruct": { + "id": "phi-3-mini-128k-instruct", + "name": "Phi-3-mini-instruct (128k)", + "family": "phi", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-08-21", - "last_updated": "2025-08-21", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 1 }, - "limit": { "context": 163840, "output": 128000 } + "knowledge": "2023-10", + "release_date": "2024-04-23", + "last_updated": "2024-04-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.13, + "output": 0.52 + } }, - "deepseek/deepseek-v3.2": { - "id": "deepseek/deepseek-v3.2", - "name": "DeepSeek V3.2", - "family": "deepseek", + "text-embedding-3-large": { + "id": "text-embedding-3-large", + "name": "text-embedding-3-large", + "family": "text-embedding", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-01-25", + "last_updated": "2024-01-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.27, "output": 0.4, "cache_read": 0.22 }, - "limit": { "context": 163842, "output": 8000 } + "limit": { + "context": 8191, + "output": 3072 + }, + "cost": { + "input": 0.13, + "output": 0 + } }, - "deepseek/deepseek-r1": { - "id": "deepseek/deepseek-r1", - "name": "DeepSeek-R1", - "family": "deepseek-thinking", + "o1-mini": { + "id": "o1-mini", + "name": "o1-mini", + "family": "o-mini", "attachment": false, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-01-20", - "last_updated": "2025-05-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2023-09", + "release_date": "2024-09-12", + "last_updated": "2024-09-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.35, "output": 5.4 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 128000, + "output": 65536 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.55 + } }, - "zai/glm-4.7-flash": { - "id": "zai/glm-4.7-flash", - "name": "GLM 4.7 Flash", - "family": "glm", + "phi-3.5-moe-instruct": { + "id": "phi-3.5-moe-instruct", + "name": "Phi-3.5-MoE-instruct", + "family": "phi", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "release_date": "2026-03-13", - "last_updated": "2026-03-13", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.07, "output": 0.39999999999999997 }, - "limit": { "context": 200000, "output": 131000 } + "knowledge": "2023-10", + "release_date": "2024-08-20", + "last_updated": "2024-08-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.16, + "output": 0.64 + } }, - "zai/glm-5-turbo": { - "id": "zai/glm-5-turbo", - "name": "GLM 5 Turbo", - "family": "glm", - "attachment": false, + "gpt-5-chat": { + "id": "gpt-5-chat", + "name": "GPT-5 Chat", + "family": "gpt-codex", + "attachment": true, "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2026-03-15", - "last_updated": "2026-03-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "temperature": false, + "knowledge": "2024-10-24", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.2, "output": 4, "cache_read": 0.24 }, - "limit": { "context": 202800, "output": 131100 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.13 + } }, - "zai/glm-4.5": { - "id": "zai/glm-4.5", - "name": "GLM 4.5", - "family": "glm", + "deepseek-v3-0324": { + "id": "deepseek-v3-0324", + "name": "DeepSeek-V3-0324", + "family": "deepseek", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-07", + "release_date": "2025-03-24", + "last_updated": "2025-03-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.2 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 1.14, + "output": 4.56 + } }, - "zai/glm-4.7-flashx": { - "id": "zai/glm-4.7-flashx", - "name": "GLM 4.7 FlashX", - "family": "glm-flash", + "llama-3.3-70b-instruct": { + "id": "llama-3.3-70b-instruct", + "name": "Llama-3.3-70B-Instruct", + "family": "llama", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-01", - "last_updated": "2025-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.06, "output": 0.4, "cache_read": 0.01 }, - "limit": { "context": 200000, "output": 128000 } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.71, + "output": 0.71 + } }, - "zai/glm-4.6": { - "id": "zai/glm-4.6", - "name": "GLM 4.6", - "family": "glm", + "kimi-k2.5": { + "id": "kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi", "attachment": false, "reasoning": true, "tool_call": true, "interleaved": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-02-06", + "last_updated": "2026-02-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.45, "output": 1.8 }, - "limit": { "context": 200000, "output": 96000 } + "limit": { + "context": 262144, + "output": 262144 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible", + "api": "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/models", + "shape": "completions" + }, + "cost": { + "input": 0.6, + "output": 3 + } }, - "zai/glm-4.6v": { - "id": "zai/glm-4.6v", - "name": "GLM-4.6V", - "family": "glm", - "attachment": true, - "reasoning": true, + "meta-llama-3.1-8b-instruct": { + "id": "meta-llama-3.1-8b-instruct", + "name": "Meta-Llama-3.1-8B-Instruct", + "family": "llama", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 0.9, "cache_read": 0.05 }, - "limit": { "context": 128000, "output": 24000 } + "knowledge": "2023-12", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.3, + "output": 0.61 + } }, - "zai/glm-5": { - "id": "zai/glm-5", - "name": "GLM-5", - "family": "glm", + "ministral-3b": { + "id": "ministral-3b", + "name": "Ministral 3B", + "family": "ministral", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-03", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1, "output": 3.2, "cache_read": 0.2 }, - "limit": { "context": 202800, "output": 131072 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.04, + "output": 0.04 + } }, - "zai/glm-4.5-air": { - "id": "zai/glm-4.5-air", - "name": "GLM 4.5 Air", - "family": "glm-air", + "phi-3-medium-4k-instruct": { + "id": "phi-3-medium-4k-instruct", + "name": "Phi-3-medium-instruct (4k)", + "family": "phi", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-10", + "release_date": "2024-04-23", + "last_updated": "2024-04-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 1.1 }, - "limit": { "context": 128000, "output": 96000 } + "limit": { + "context": 4096, + "output": 1024 + }, + "cost": { + "input": 0.17, + "output": 0.68 + } }, - "zai/glm-4.5v": { - "id": "zai/glm-4.5v", - "name": "GLM 4.5V", - "family": "glm", + "llama-4-scout-17b-16e-instruct": { + "id": "llama-4-scout-17b-16e-instruct", + "name": "Llama 4 Scout 17B 16E Instruct", + "family": "llama", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-08", - "release_date": "2025-08-11", - "last_updated": "2025-08-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-08", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 1.8 }, - "limit": { "context": 66000, "output": 66000 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.2, + "output": 0.78 + } }, - "zai/glm-4.7": { - "id": "zai/glm-4.7", - "name": "GLM 4.7", - "family": "glm", + "phi-3.5-mini-instruct": { + "id": "phi-3.5-mini-instruct", + "name": "Phi-3.5-mini-instruct", + "family": "phi", "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.43, "output": 1.75, "cache_read": 0.08 }, - "limit": { "context": 202752, "output": 120000 } + "knowledge": "2023-10", + "release_date": "2024-08-20", + "last_updated": "2024-08-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.13, + "output": 0.52 + } }, - "zai/glm-4.6v-flash": { - "id": "zai/glm-4.6v-flash", - "name": "GLM-4.6V-Flash", - "family": "glm", + "phi-4-multimodal": { + "id": "phi-4-multimodal", + "name": "Phi-4-multimodal", + "family": "phi", "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "limit": { "context": 128000, "output": 24000 } - }, - "xai/grok-imagine-image": { - "id": "xai/grok-imagine-image", - "name": "Grok Imagine Image", - "family": "grok", - "attachment": false, "reasoning": false, "tool_call": false, "temperature": true, - "release_date": "2026-01-28", - "last_updated": "2026-02-19", - "modalities": { "input": ["text"], "output": ["text", "image"] }, - "open_weights": false, - "limit": { "context": 0, "output": 0 } + "knowledge": "2023-10", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.08, + "output": 0.32, + "input_audio": 4 + } }, - "xai/grok-4.20-reasoning-beta": { - "id": "xai/grok-4.20-reasoning-beta", - "name": "Grok 4.20 Beta Reasoning", - "family": "grok", + "codex-mini": { + "id": "codex-mini", + "name": "Codex Mini", + "family": "gpt-codex-mini", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2026-03-11", - "last_updated": "2026-03-13", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-04", + "release_date": "2025-05-16", + "last_updated": "2025-05-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 6, "cache_read": 0.19999999999999998 }, - "limit": { "context": 2000000, "output": 2000000 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.5, + "output": 6, + "cache_read": 0.375 + } }, - "xai/grok-4.20-non-reasoning": { - "id": "xai/grok-4.20-non-reasoning", - "name": "Grok 4.20 Non-Reasoning", - "family": "grok", + "gpt-5.2-chat": { + "id": "gpt-5.2-chat", + "name": "GPT-5.2 Chat", + "family": "gpt-codex", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2026-03-09", - "last_updated": "2026-03-23", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 6, "cache_read": 0.19999999999999998 }, - "limit": { "context": 2000000, "output": 2000000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "xai/grok-4.1-fast-non-reasoning": { - "id": "xai/grok-4.1-fast-non-reasoning", - "name": "Grok 4.1 Fast Non-Reasoning", - "family": "grok", + "mistral-nemo": { + "id": "mistral-nemo", + "name": "Mistral Nemo", + "family": "mistral-nemo", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-07", + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.15, + "output": 0.15 + } + }, + "gpt-5.4-mini": { + "id": "gpt-5.4-mini", + "name": "GPT-5.4 Mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.75, + "output": 4.5, + "cache_read": 0.075 + } }, - "xai/grok-4.1-fast-reasoning": { - "id": "xai/grok-4.1-fast-reasoning", - "name": "Grok 4.1 Fast Reasoning", - "family": "grok", - "attachment": false, + "gpt-5.4-nano": { + "id": "gpt-5.4-nano", + "name": "GPT-5.4 Nano", + "family": "gpt-nano", + "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.2, + "output": 1.25, + "cache_read": 0.02 + } }, - "xai/grok-4.20-multi-agent-beta": { - "id": "xai/grok-4.20-multi-agent-beta", - "name": "Grok 4.20 Multi Agent Beta", - "family": "grok", - "attachment": false, + "gpt-5.4-pro": { + "id": "gpt-5.4-pro", + "name": "GPT-5.4 Pro", + "family": "gpt-pro", + "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2026-03-11", - "last_updated": "2026-03-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 6, "cache_read": 0.19999999999999998 }, - "limit": { "context": 2000000, "output": 2000000 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 30, + "output": 180, + "context_over_200k": { + "input": 60, + "output": 270 + }, + "tiers": [ + { + "input": 60, + "output": 270, + "tier": { + "type": "context", + "size": 272000 + } + } + ] + } }, - "xai/grok-4.20-reasoning": { - "id": "xai/grok-4.20-reasoning", - "name": "Grok 4.20 Reasoning", - "family": "grok", + "gpt-5.4": { + "id": "gpt-5.4", + "name": "GPT-5.4", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2026-03-09", - "last_updated": "2026-03-23", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 6, "cache_read": 0.19999999999999998 }, - "limit": { "context": 2000000, "output": 2000000 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 2.5, + "output": 15, + "cache_read": 0.25, + "context_over_200k": { + "input": 5, + "output": 22.5, + "cache_read": 0.5 + }, + "tiers": [ + { + "input": 5, + "output": 22.5, + "cache_read": 0.5, + "tier": { + "type": "context", + "size": 272000 + } + } + ] + } }, - "xai/grok-4.20-multi-agent": { - "id": "xai/grok-4.20-multi-agent", - "name": "Grok 4.20 Multi-Agent", + "grok-4-fast-non-reasoning": { + "id": "grok-4-fast-non-reasoning", + "name": "Grok 4 Fast (Non-Reasoning)", "family": "grok", - "attachment": false, - "reasoning": true, + "attachment": true, + "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2026-03-09", - "last_updated": "2026-03-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-09-19", + "last_updated": "2025-09-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 6, "cache_read": 0.19999999999999998 }, - "limit": { "context": 2000000, "output": 2000000 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "xai/grok-4-fast-reasoning": { - "id": "xai/grok-4-fast-reasoning", - "name": "Grok 4 Fast Reasoning", + "grok-3": { + "id": "grok-3", + "name": "Grok 3", "family": "grok", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 256000 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75 + } }, - "xai/grok-imagine-image-pro": { - "id": "xai/grok-imagine-image-pro", - "name": "Grok Imagine Image Pro", - "family": "grok", - "attachment": false, + "gpt-4.1-mini": { + "id": "gpt-4.1-mini", + "name": "GPT-4.1 mini", + "family": "gpt-mini", + "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2026-01-28", - "last_updated": "2026-02-19", - "modalities": { "input": ["text"], "output": ["text", "image"] }, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 0, "output": 0 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 1.6, + "cache_read": 0.1 + } }, - "xai/grok-4.20-non-reasoning-beta": { - "id": "xai/grok-4.20-non-reasoning-beta", - "name": "Grok 4.20 Beta Non-Reasoning", - "family": "grok", + "gpt-4.1": { + "id": "gpt-4.1", + "name": "GPT-4.1", + "family": "gpt", "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2026-03-11", - "last_updated": "2026-03-13", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 6, "cache_read": 0.19999999999999998 }, - "limit": { "context": 2000000, "output": 2000000 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } }, - "xai/grok-3": { - "id": "xai/grok-3", - "name": "Grok 3", - "family": "grok", + "cohere-command-r-plus-08-2024": { + "id": "cohere-command-r-plus-08-2024", + "name": "Command R+", + "family": "command-r", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.75 }, - "limit": { "context": 131072, "output": 8192 } + "knowledge": "2024-06-01", + "release_date": "2024-08-30", + "last_updated": "2024-08-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4000 + }, + "cost": { + "input": 2.5, + "output": 10 + } }, - "xai/grok-3-fast": { - "id": "xai/grok-3-fast", - "name": "Grok 3 Fast", - "family": "grok", - "attachment": false, + "gpt-4o": { + "id": "gpt-4o", + "name": "GPT-4o", + "family": "gpt", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-09", + "release_date": "2024-05-13", + "last_updated": "2024-08-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 1.25 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10, + "cache_read": 1.25 + } }, - "xai/grok-2-vision": { - "id": "xai/grok-2-vision", - "name": "Grok 2 Vision", - "family": "grok", + "gpt-5-pro": { + "id": "gpt-5-pro", + "name": "GPT-5 Pro", + "family": "gpt-pro", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2024-08-20", - "last_updated": "2024-08-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-10-06", + "last_updated": "2025-10-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 10, "cache_read": 2 }, - "limit": { "context": 8192, "output": 4096 } + "limit": { + "context": 400000, + "output": 272000 + }, + "cost": { + "input": 15, + "output": 120 + } }, - "xai/grok-3-mini": { - "id": "xai/grok-3-mini", - "name": "Grok 3 Mini", - "family": "grok", - "attachment": false, + "o3": { + "id": "o3", + "name": "o3", + "family": "o", + "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 0.5, "reasoning": 0.5, "cache_read": 0.075 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } }, - "xai/grok-3-mini-fast": { - "id": "xai/grok-3-mini-fast", - "name": "Grok 3 Mini Fast", + "grok-3-mini": { + "id": "grok-3-mini", + "name": "Grok 3 Mini", "family": "grok", "attachment": false, "reasoning": true, @@ -40090,29 +76888,50 @@ "knowledge": "2024-11", "release_date": "2025-02-17", "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 4, "reasoning": 4, "cache_read": 0.15 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 0.5, + "reasoning": 0.5, + "cache_read": 0.075 + } }, - "xai/grok-code-fast-1": { - "id": "xai/grok-code-fast-1", - "name": "Grok Code Fast 1", - "family": "grok", - "attachment": false, - "reasoning": true, + "gpt-4.1-nano": { + "id": "gpt-4.1-nano", + "name": "GPT-4.1 nano", + "family": "gpt-nano", + "attachment": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2025-08-28", - "last_updated": "2025-08-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 1.5, "cache_read": 0.02 }, - "limit": { "context": 256000, "output": 10000 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.03 + } }, - "xai/grok-4": { - "id": "xai/grok-4", + "grok-4": { + "id": "grok-4", "name": "Grok 4", "family": "grok", "attachment": false, @@ -40122,1850 +76941,2740 @@ "knowledge": "2025-07", "release_date": "2025-07-09", "last_updated": "2025-07-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "reasoning": 15, "cache_read": 0.75 }, - "limit": { "context": 256000, "output": 64000 } - }, - "xai/grok-4-fast-non-reasoning": { - "id": "xai/grok-4-fast-non-reasoning", - "name": "Grok 4 Fast (Non-Reasoning)", - "family": "grok", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-09-19", - "last_updated": "2025-09-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "reasoning": 15, + "cache_read": 0.75 + } }, - "nvidia/nemotron-3-super-120b-a12b": { - "id": "nvidia/nemotron-3-super-120b-a12b", - "name": "NVIDIA Nemotron 3 Super 120B A12B", - "family": "nemotron", + "o3-mini": { + "id": "o3-mini", + "name": "o3-mini", + "family": "o-mini", "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2026-03-18", - "last_updated": "2026-03-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.65 }, - "limit": { "context": 256000, "output": 32000 } - }, - "nvidia/nemotron-nano-12b-v2-vl": { - "id": "nvidia/nemotron-nano-12b-v2-vl", - "name": "Nvidia Nemotron Nano 12B V2 VL", - "family": "nemotron", - "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-12", - "last_updated": "2024-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2024-12-20", + "last_updated": "2025-01-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.6 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.55 + } }, - "nvidia/nemotron-nano-9b-v2": { - "id": "nvidia/nemotron-nano-9b-v2", - "name": "Nvidia Nemotron Nano 9B V2", - "family": "nemotron", + "grok-code-fast-1": { + "id": "grok-code-fast-1", + "name": "Grok Code Fast 1", + "family": "grok", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-08-18", - "last_updated": "2025-08-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-10", + "release_date": "2025-08-28", + "last_updated": "2025-08-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.04, "output": 0.16 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 256000, + "output": 10000 + }, + "cost": { + "input": 0.2, + "output": 1.5, + "cache_read": 0.02 + } }, - "nvidia/nemotron-3-nano-30b-a3b": { - "id": "nvidia/nemotron-3-nano-30b-a3b", - "name": "Nemotron 3 Nano 30B A3B", - "family": "nemotron", - "attachment": false, + "o4-mini": { + "id": "o4-mini", + "name": "o4-mini", + "family": "o-mini", + "attachment": true, "reasoning": true, - "tool_call": false, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-12", - "last_updated": "2024-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.06, "output": 0.24 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.28 + } }, - "google/text-embedding-005": { - "id": "google/text-embedding-005", - "name": "Text Embedding 005", - "family": "text-embedding", + "cohere-command-r-08-2024": { + "id": "cohere-command-r-08-2024", + "name": "Command R", + "family": "command-r", "attachment": false, "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2024-08", - "last_updated": "2024-08", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.03, "output": 0 }, - "limit": { "context": 8192, "output": 1536 } + "tool_call": true, + "temperature": true, + "knowledge": "2024-06-01", + "release_date": "2024-08-30", + "last_updated": "2024-08-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4000 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "google/gemini-2.5-flash-lite": { - "id": "google/gemini-2.5-flash-lite", - "name": "Gemini 2.5 Flash Lite", - "family": "gemini-flash-lite", + "gpt-4o-mini": { + "id": "gpt-4o-mini", + "name": "GPT-4o mini", + "family": "gpt-mini", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "knowledge": "2023-09", + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.01 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.08 + } }, - "google/gemini-2.5-flash-lite-preview-09-2025": { - "id": "google/gemini-2.5-flash-lite-preview-09-2025", - "name": "Gemini 2.5 Flash Lite Preview 09-25", - "family": "gemini-flash-lite", + "gpt-4-turbo": { + "id": "gpt-4-turbo", + "name": "GPT-4 Turbo", + "family": "gpt", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2023-11-06", + "last_updated": "2024-04-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.01 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 10, + "output": 30 + } }, - "google/gemini-3.1-pro-preview": { - "id": "google/gemini-3.1-pro-preview", - "name": "Gemini 3.1 Pro Preview", - "family": "gemini", + "gpt-5.5": { + "id": "gpt-5.5", + "name": "GPT-5.5", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2026-02-19", - "last_updated": "2026-02-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-12-01", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 12, "cache_read": 0.2 }, - "limit": { "context": 1000000, "output": 64000 } - }, - "google/gemini-3-pro-preview": { - "id": "google/gemini-3-pro-preview", - "name": "Gemini 3 Pro Preview", - "family": "gemini-pro", + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5, + "context_over_200k": { + "input": 10, + "output": 45, + "cache_read": 1 + }, + "tiers": [ + { + "input": 10, + "output": 45, + "cache_read": 1, + "tier": { + "type": "context", + "size": 272000 + } + } + ] + } + } + } + }, + "abliteration-ai": { + "id": "abliteration-ai", + "env": ["ABLIT_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.abliteration.ai/v1", + "name": "abliteration.ai", + "doc": "https://docs.abliteration.ai/models", + "models": { + "abliterated-model": { + "id": "abliterated-model", + "name": "Abliterated Model", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, - "open_weights": false, + "release_date": "2026-01-06", + "last_updated": "2026-01-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 150000, + "input": 150000, + "output": 8192 + }, "cost": { - "input": 2, - "output": 12, - "cache_read": 0.2, - "context_over_200k": { "input": 4, "output": 18, "cache_read": 0.4 } + "input": 3, + "output": 3 + } + } + } + }, + "wafer.ai": { + "id": "wafer.ai", + "env": ["WAFER_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://pass.wafer.ai/v1", + "name": "Wafer", + "doc": "https://docs.wafer.ai/wafer-pass", + "models": { + "Qwen3.5-397B-A17B": { + "id": "Qwen3.5-397B-A17B", + "name": "Qwen3.5 397B A17B", + "family": "qwen", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-02-16", + "last_updated": "2026-02-16", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 }, - "limit": { "context": 1000000, "output": 64000 } + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "google/imagen-4.0-fast-generate-001": { - "id": "google/imagen-4.0-fast-generate-001", - "name": "Imagen 4 Fast", - "family": "imagen", + "GLM-5.1": { + "id": "GLM-5.1", + "name": "GLM-5.1", + "family": "glm", "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2025-06", - "last_updated": "2025-06", - "modalities": { "input": ["text"], "output": ["image"] }, - "open_weights": false, - "limit": { "context": 480, "output": 0 } - }, - "google/imagen-4.0-generate-001": { - "id": "google/imagen-4.0-generate-001", - "name": "Imagen 4", - "family": "imagen", + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-04-07", + "last_updated": "2026-04-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } + } + } + }, + "cohere": { + "id": "cohere", + "env": ["COHERE_API_KEY"], + "npm": "@ai-sdk/cohere", + "name": "Cohere", + "doc": "https://docs.cohere.com/docs/models", + "models": { + "command-a-reasoning-08-2025": { + "id": "command-a-reasoning-08-2025", + "name": "Command A Reasoning", + "family": "command-a", "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text"], "output": ["image"] }, - "open_weights": false, - "limit": { "context": 480, "output": 0 } - }, - "google/gemini-2.5-flash-preview-09-2025": { - "id": "google/gemini-2.5-flash-preview-09-2025", - "name": "Gemini 2.5 Flash Preview 09-25", - "family": "gemini-flash", - "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.5, "cache_read": 0.03, "cache_write": 0.383 }, - "limit": { "context": 1048576, "output": 65536 } + "knowledge": "2024-06-01", + "release_date": "2025-08-21", + "last_updated": "2025-08-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 32000 + }, + "cost": { + "input": 2.5, + "output": 10 + } }, - "google/text-multilingual-embedding-002": { - "id": "google/text-multilingual-embedding-002", - "name": "Text Multilingual Embedding 002", - "family": "text-embedding", + "command-r7b-12-2024": { + "id": "command-r7b-12-2024", + "name": "Command R7B", + "family": "command-r", "attachment": false, "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2024-03", - "last_updated": "2024-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.03, "output": 0 }, - "limit": { "context": 8192, "output": 1536 } - }, - "google/gemini-3.1-flash-lite-preview": { - "id": "google/gemini-3.1-flash-lite-preview", - "name": "Gemini 3.1 Flash Lite Preview", - "family": "gemini", - "attachment": true, - "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-03-03", - "last_updated": "2026-03-06", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 1.5, "cache_read": 0.025, "cache_write": 1 }, - "limit": { "context": 1000000, "output": 65000 } + "knowledge": "2024-06-01", + "release_date": "2024-02-27", + "last_updated": "2024-02-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4000 + }, + "cost": { + "input": 0.0375, + "output": 0.15 + } }, - "google/gemini-3.1-flash-image-preview": { - "id": "google/gemini-3.1-flash-image-preview", - "name": "Gemini 3.1 Flash Image Preview (Nano Banana 2)", - "family": "gemini", + "c4ai-aya-vision-8b": { + "id": "c4ai-aya-vision-8b", + "name": "Aya Vision 8B", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": false, "temperature": true, - "release_date": "2026-02-26", - "last_updated": "2026-03-06", - "modalities": { "input": ["text", "image"], "output": ["text", "image"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 3 }, - "limit": { "context": 131072, "output": 32768 } + "release_date": "2025-03-04", + "last_updated": "2025-05-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 16000, + "output": 4000 + } }, - "google/gemini-2.5-flash-image": { - "id": "google/gemini-2.5-flash-image", - "name": "Nano Banana (Gemini 2.5 Flash Image)", - "family": "gemini-flash", + "command-r-plus-08-2024": { + "id": "command-r-plus-08-2024", + "name": "Command R+", + "family": "command-r", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-03-20", - "last_updated": "2025-03-20", - "modalities": { "input": ["text"], "output": ["text", "image"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.5 }, - "limit": { "context": 32768, "output": 32768 } + "knowledge": "2024-06-01", + "release_date": "2024-08-30", + "last_updated": "2024-08-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4000 + }, + "cost": { + "input": 2.5, + "output": 10 + } }, - "google/gemini-embedding-001": { - "id": "google/gemini-embedding-001", - "name": "Gemini Embedding 001", - "family": "gemini-embedding", + "c4ai-aya-expanse-8b": { + "id": "c4ai-aya-expanse-8b", + "name": "Aya Expanse 8B", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": false, - "release_date": "2025-05-20", - "last_updated": "2025-05-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0 }, - "limit": { "context": 8192, "output": 1536 } + "temperature": true, + "release_date": "2024-10-24", + "last_updated": "2024-10-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8000, + "output": 4000 + } }, - "google/gemini-2.5-flash-image-preview": { - "id": "google/gemini-2.5-flash-image-preview", - "name": "Nano Banana Preview (Gemini 2.5 Flash Image Preview)", - "family": "gemini-flash", + "command-r7b-arabic-02-2025": { + "id": "command-r7b-arabic-02-2025", + "name": "Command R7B Arabic", + "family": "command-r", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-03-20", - "last_updated": "2025-03-20", - "modalities": { "input": ["text"], "output": ["text", "image"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.5 }, - "limit": { "context": 32768, "output": 32768 } + "knowledge": "2024-06-01", + "release_date": "2025-02-27", + "last_updated": "2025-02-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4000 + }, + "cost": { + "input": 0.0375, + "output": 0.15 + } }, - "google/gemini-3-pro-image": { - "id": "google/gemini-3-pro-image", - "name": "Nano Banana Pro (Gemini 3 Pro Image)", - "family": "gemini-pro", + "command-a-vision-07-2025": { + "id": "command-a-vision-07-2025", + "name": "Command A Vision", + "family": "command-a", "attachment": false, "reasoning": false, "tool_call": false, "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-09", - "last_updated": "2025-09", - "modalities": { "input": ["text"], "output": ["text", "image"] }, - "open_weights": false, - "cost": { "input": 2, "output": 120 }, - "limit": { "context": 65536, "output": 32768 } + "knowledge": "2024-06-01", + "release_date": "2025-07-31", + "last_updated": "2025-07-31", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8000 + }, + "cost": { + "input": 2.5, + "output": 10 + } }, - "google/gemini-embedding-2": { - "id": "google/gemini-embedding-2", - "name": "Gemini Embedding 2", - "family": "gemini-embedding", - "attachment": false, + "c4ai-aya-vision-32b": { + "id": "c4ai-aya-vision-32b", + "name": "Aya Vision 32B", + "attachment": true, "reasoning": false, "tool_call": false, "temperature": true, - "release_date": "2026-03-10", - "last_updated": "2026-03-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "limit": { "context": 0, "output": 0 } + "release_date": "2025-03-04", + "last_updated": "2025-05-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 16000, + "output": 4000 + } }, - "google/imagen-4.0-ultra-generate-001": { - "id": "google/imagen-4.0-ultra-generate-001", - "name": "Imagen 4 Ultra", - "family": "imagen", + "command-a-translate-08-2025": { + "id": "command-a-translate-08-2025", + "name": "Command A Translate", + "family": "command-a", "attachment": false, "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2025-05-24", - "last_updated": "2025-05-24", - "modalities": { "input": ["text"], "output": ["image"] }, - "open_weights": false, - "limit": { "context": 480, "output": 0 } - }, - "google/gemini-3-flash": { - "id": "google/gemini-3-flash", - "name": "Gemini 3 Flash", - "family": "gemini-flash", - "attachment": true, - "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 3, "cache_read": 0.05 }, - "limit": { "context": 1000000, "output": 64000 } + "knowledge": "2024-06-01", + "release_date": "2025-08-28", + "last_updated": "2025-08-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8000, + "output": 8000 + }, + "cost": { + "input": 2.5, + "output": 10 + } }, - "google/gemini-2.0-flash": { - "id": "google/gemini-2.0-flash", - "name": "Gemini 2.0 Flash", - "family": "gemini-flash", - "attachment": true, + "command-r-08-2024": { + "id": "command-r-08-2024", + "name": "Command R", + "family": "command-r", + "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2024-06", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.025 }, - "limit": { "context": 1048576, "output": 8192 } + "knowledge": "2024-06-01", + "release_date": "2024-08-30", + "last_updated": "2024-08-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4000 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "google/gemini-2.5-flash": { - "id": "google/gemini-2.5-flash", - "name": "Gemini 2.5 Flash", - "family": "gemini-flash", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, + "c4ai-aya-expanse-32b": { + "id": "c4ai-aya-expanse-32b", + "name": "Aya Expanse 32B", + "attachment": false, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-03-20", - "last_updated": "2025-06-05", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.5, "cache_read": 0.075, "input_audio": 1 }, - "limit": { "context": 1048576, "output": 65536 } + "release_date": "2024-10-24", + "last_updated": "2024-10-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4000 + } }, - "google/gemini-2.5-pro": { - "id": "google/gemini-2.5-pro", - "name": "Gemini 2.5 Pro", - "family": "gemini-pro", - "attachment": true, - "reasoning": true, + "command-a-03-2025": { + "id": "command-a-03-2025", + "name": "Command A", + "family": "command-a", + "attachment": false, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-03-20", - "last_updated": "2025-06-05", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.31 }, - "limit": { "context": 1048576, "output": 65536 } - }, - "google/gemini-2.0-flash-lite": { - "id": "google/gemini-2.0-flash-lite", - "name": "Gemini 2.0 Flash Lite", - "family": "gemini-flash-lite", - "attachment": true, + "knowledge": "2024-06-01", + "release_date": "2025-03-13", + "last_updated": "2025-03-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 8000 + }, + "cost": { + "input": 2.5, + "output": 10 + } + } + } + }, + "cloudferro-sherlock": { + "id": "cloudferro-sherlock", + "env": ["CLOUDFERRO_SHERLOCK_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api-sherlock.cloudferro.com/openai/v1/", + "name": "CloudFerro Sherlock", + "doc": "https://docs.sherlock.cloudferro.com/", + "models": { + "meta-llama/Llama-3.3-70B-Instruct": { + "id": "meta-llama/Llama-3.3-70B-Instruct", + "name": "Llama 3.3 70B Instruct", + "family": "llama", + "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2024-06", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.075, "output": 0.3 }, - "limit": { "context": 1048576, "output": 8192 } + "knowledge": "2024-10-09", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 70000, + "output": 70000 + }, + "cost": { + "input": 2.92, + "output": 2.92 + } }, - "amazon/titan-embed-text-v2": { - "id": "amazon/titan-embed-text-v2", - "name": "Titan Text Embeddings V2", - "family": "titan-embed", + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "name": "OpenAI GPT OSS 120B", + "family": "gpt-oss", "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2024-04", - "last_updated": "2024-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.02, "output": 0 }, - "limit": { "context": 8192, "output": 1536 } - }, - "amazon/nova-2-lite": { - "id": "amazon/nova-2-lite", - "name": "Nova 2 Lite", - "family": "nova", - "attachment": true, "reasoning": true, - "tool_call": false, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.5 }, - "limit": { "context": 1000000, "output": 1000000 } - }, - "amazon/nova-lite": { - "id": "amazon/nova-lite", - "name": "Nova Lite", - "family": "nova-lite", - "attachment": true, - "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-12-03", - "last_updated": "2024-12-03", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.06, "output": 0.24, "cache_read": 0.015 }, - "limit": { "context": 300000, "output": 8192 } + "release_date": "2025-08-28", + "last_updated": "2025-08-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 2.92, + "output": 2.92 + } }, - "amazon/nova-pro": { - "id": "amazon/nova-pro", - "name": "Nova Pro", - "family": "nova-pro", - "attachment": true, + "speakleash/Bielik-11B-v3.0-Instruct": { + "id": "speakleash/Bielik-11B-v3.0-Instruct", + "name": "Bielik 11B v3.0 Instruct", + "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-12-03", - "last_updated": "2024-12-03", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.8, "output": 3.2, "cache_read": 0.2 }, - "limit": { "context": 300000, "output": 8192 } + "knowledge": "2025-03", + "release_date": "2025-03-13", + "last_updated": "2025-03-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32000, + "output": 32000 + }, + "cost": { + "input": 0.67, + "output": 0.67 + } }, - "amazon/nova-micro": { - "id": "amazon/nova-micro", - "name": "Nova Micro", - "family": "nova-micro", + "speakleash/Bielik-11B-v2.6-Instruct": { + "id": "speakleash/Bielik-11B-v2.6-Instruct", + "name": "Bielik 11B v2.6 Instruct", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-12-03", - "last_updated": "2024-12-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.035, "output": 0.14, "cache_read": 0.00875 }, - "limit": { "context": 128000, "output": 8192 } + "knowledge": "2025-03", + "release_date": "2025-03-13", + "last_updated": "2025-03-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32000, + "output": 32000 + }, + "cost": { + "input": 0.67, + "output": 0.67 + } }, - "perplexity/sonar-reasoning": { - "id": "perplexity/sonar-reasoning", - "name": "Sonar Reasoning", - "family": "sonar-reasoning", + "MiniMaxAI/MiniMax-M2.5": { + "id": "MiniMaxAI/MiniMax-M2.5", + "name": "MiniMax-M2.5", + "family": "minimax", "attachment": false, "reasoning": true, - "tool_call": false, - "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1, "output": 5 }, - "limit": { "context": 127000, "output": 8000 } - }, - "perplexity/sonar": { - "id": "perplexity/sonar", - "name": "Sonar", - "family": "sonar", - "attachment": true, - "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-02", - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1, "output": 1 }, - "limit": { "context": 127000, "output": 8000 } - }, - "perplexity/sonar-reasoning-pro": { - "id": "perplexity/sonar-reasoning-pro", - "name": "Sonar Reasoning Pro", - "family": "sonar-reasoning", + "knowledge": "2026-01", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 196000, + "input": 180000, + "output": 16000 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } + } + } + }, + "kuae-cloud-coding-plan": { + "id": "kuae-cloud-coding-plan", + "env": ["KUAE_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://coding-plan-endpoint.kuaecloud.net/v1", + "name": "KUAE Cloud Coding Plan", + "doc": "https://docs.mthreads.com/kuaecloud/kuaecloud-doc-online/coding_plan/", + "models": { + "GLM-4.7": { + "id": "GLM-4.7", + "name": "GLM-4.7", + "family": "glm", "attachment": false, "reasoning": true, - "tool_call": false, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 8 }, - "limit": { "context": 127000, "output": 8000 } - }, - "perplexity/sonar-pro": { - "id": "perplexity/sonar-pro", - "name": "Sonar Pro", - "family": "sonar-pro", - "attachment": true, + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } + } + } + }, + "xai": { + "id": "xai", + "env": ["XAI_API_KEY"], + "npm": "@ai-sdk/xai", + "name": "xAI", + "doc": "https://docs.x.ai/docs/models", + "models": { + "grok-2-1212": { + "id": "grok-2-1212", + "name": "Grok 2 (1212)", + "family": "grok", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 200000, "output": 8000 } - }, - "voyage/voyage-code-3": { - "id": "voyage/voyage-code-3", - "name": "voyage-code-3", - "family": "voyage", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2024-09", - "last_updated": "2024-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.18, "output": 0 }, - "limit": { "context": 8192, "output": 1536 } - }, - "voyage/voyage-3.5-lite": { - "id": "voyage/voyage-3.5-lite", - "name": "voyage-3.5-lite", - "family": "voyage", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2025-05-20", - "last_updated": "2025-05-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.02, "output": 0 }, - "limit": { "context": 8192, "output": 1536 } - }, - "voyage/voyage-code-2": { - "id": "voyage/voyage-code-2", - "name": "voyage-code-2", - "family": "voyage", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2024-01", - "last_updated": "2024-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-08", + "release_date": "2024-12-12", + "last_updated": "2024-12-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.12, "output": 0 }, - "limit": { "context": 8192, "output": 1536 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 2, + "output": 10, + "cache_read": 2 + } }, - "voyage/voyage-4-lite": { - "id": "voyage/voyage-4-lite", - "name": "voyage-4-lite", - "family": "voyage", - "attachment": false, + "grok-vision-beta": { + "id": "grok-vision-beta", + "name": "Grok Vision Beta", + "family": "grok-vision", + "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2026-03-06", - "last_updated": "2026-03-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-08", + "release_date": "2024-11-01", + "last_updated": "2024-11-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 32000, "output": 0 } + "limit": { + "context": 8192, + "output": 4096 + }, + "cost": { + "input": 5, + "output": 15, + "cache_read": 5 + } }, - "voyage/voyage-finance-2": { - "id": "voyage/voyage-finance-2", - "name": "voyage-finance-2", - "family": "voyage", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2024-03", - "last_updated": "2024-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "grok-4.3": { + "id": "grok-4.3", + "name": "Grok 4.3", + "family": "grok", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-01", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.12, "output": 0 }, - "limit": { "context": 8192, "output": 1536 } + "limit": { + "context": 1000000, + "output": 30000 + }, + "cost": { + "input": 1.25, + "output": 2.5, + "cache_read": 0.2, + "tiers": [ + { + "input": 2.5, + "output": 5, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 2.5, + "output": 5, + "cache_read": 0.4 + } + } }, - "voyage/voyage-law-2": { - "id": "voyage/voyage-law-2", - "name": "voyage-law-2", - "family": "voyage", + "grok-3-mini-fast": { + "id": "grok-3-mini-fast", + "name": "Grok 3 Mini Fast", + "family": "grok", "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2024-03", - "last_updated": "2024-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.12, "output": 0 }, - "limit": { "context": 8192, "output": 1536 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.6, + "output": 4, + "reasoning": 4, + "cache_read": 0.15 + } }, - "voyage/voyage-4": { - "id": "voyage/voyage-4", - "name": "voyage-4", - "family": "voyage", + "grok-3-mini-latest": { + "id": "grok-3-mini-latest", + "name": "Grok 3 Mini Latest", + "family": "grok", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2026-03-06", - "last_updated": "2026-03-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 32000, "output": 0 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 0.5, + "reasoning": 0.5, + "cache_read": 0.075 + } }, - "voyage/voyage-3-large": { - "id": "voyage/voyage-3-large", - "name": "voyage-3-large", - "family": "voyage", + "grok-3-fast": { + "id": "grok-3-fast", + "name": "Grok 3 Fast", + "family": "grok", "attachment": false, "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2024-09", - "last_updated": "2024-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.18, "output": 0 }, - "limit": { "context": 8192, "output": 1536 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 1.25 + } }, - "voyage/voyage-3.5": { - "id": "voyage/voyage-3.5", - "name": "voyage-3.5", - "family": "voyage", - "attachment": false, + "grok-2-vision-latest": { + "id": "grok-2-vision-latest", + "name": "Grok 2 Vision Latest", + "family": "grok", + "attachment": true, "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2025-05-20", - "last_updated": "2025-05-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-08", + "release_date": "2024-08-20", + "last_updated": "2024-12-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.06, "output": 0 }, - "limit": { "context": 8192, "output": 1536 } + "limit": { + "context": 8192, + "output": 4096 + }, + "cost": { + "input": 2, + "output": 10, + "cache_read": 2 + } }, - "voyage/voyage-4-large": { - "id": "voyage/voyage-4-large", - "name": "voyage-4-large", - "family": "voyage", - "attachment": false, - "reasoning": false, - "tool_call": false, + "grok-4.20-0309-reasoning": { + "id": "grok-4.20-0309-reasoning", + "name": "Grok 4.20 (Reasoning)", + "family": "grok", + "attachment": true, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2026-03-06", - "last_updated": "2026-03-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-09", + "last_updated": "2026-03-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 32000, "output": 0 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.2, + "tiers": [ + { + "input": 4, + "output": 12, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 12, + "cache_read": 0.4 + } + } }, - "arcee-ai/trinity-mini": { - "id": "arcee-ai/trinity-mini", - "name": "Trinity Mini", - "family": "trinity", - "attachment": false, + "grok-4-1-fast-non-reasoning": { + "id": "grok-4-1-fast-non-reasoning", + "name": "Grok 4.1 Fast (Non-Reasoning)", + "family": "grok", + "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-12", - "last_updated": "2025-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-11-19", + "last_updated": "2025-11-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.05, "output": 0.15 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "arcee-ai/trinity-large-preview": { - "id": "arcee-ai/trinity-large-preview", - "name": "Trinity Large Preview", - "family": "trinity", + "grok-3-mini-fast-latest": { + "id": "grok-3-mini-fast-latest", + "name": "Grok 3 Mini Fast Latest", + "family": "grok", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-01", - "last_updated": "2025-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 1 }, - "limit": { "context": 131000, "output": 131000 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.6, + "output": 4, + "reasoning": 4, + "cache_read": 0.15 + } }, - "bytedance/seed-1.6": { - "id": "bytedance/seed-1.6", - "name": "Seed 1.6", - "family": "seed", - "attachment": false, + "grok-4-fast": { + "id": "grok-4-fast", + "name": "Grok 4 Fast", + "family": "grok", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-09", - "last_updated": "2025-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-09-19", + "last_updated": "2025-09-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.05 }, - "limit": { "context": 256000, "output": 32000 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "bytedance/seed-1.8": { - "id": "bytedance/seed-1.8", - "name": "Seed 1.8", - "family": "seed", + "grok-3-latest": { + "id": "grok-3-latest", + "name": "Grok 3 Latest", + "family": "grok", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-10", - "last_updated": "2025-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.05 }, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75 + } }, - "minimax/minimax-m2": { - "id": "minimax/minimax-m2", - "name": "MiniMax M2", - "family": "minimax", + "grok-2": { + "id": "grok-2", + "name": "Grok 2", + "family": "grok", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-10-27", - "last_updated": "2025-10-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.27, "output": 1.15, "cache_read": 0.03, "cache_write": 0.38 }, - "limit": { "context": 262114, "output": 262114 } + "knowledge": "2024-08", + "release_date": "2024-08-20", + "last_updated": "2024-08-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 2, + "output": 10, + "cache_read": 2 + } }, - "minimax/minimax-m2.1": { - "id": "minimax/minimax-m2.1", - "name": "MiniMax M2.1", - "family": "minimax", + "grok-code-fast-1": { + "id": "grok-code-fast-1", + "name": "Grok Code Fast 1", + "family": "grok", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-10-27", - "last_updated": "2025-10-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-10", + "release_date": "2025-08-28", + "last_updated": "2025-08-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.03, "cache_write": 0.38 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 256000, + "output": 10000 + }, + "cost": { + "input": 0.2, + "output": 1.5, + "cache_read": 0.02 + } }, - "minimax/minimax-m2.7": { - "id": "minimax/minimax-m2.7", - "name": "Minimax M2.7", - "family": "minimax", + "grok-4.20-0309-non-reasoning": { + "id": "grok-4.20-0309-non-reasoning", + "name": "Grok 4.20 (Non-Reasoning)", + "family": "grok", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.06, "cache_write": 0.375 }, - "limit": { "context": 204800, "output": 131000 } + "release_date": "2026-03-09", + "last_updated": "2026-03-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.2, + "tiers": [ + { + "input": 4, + "output": 12, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 12, + "cache_read": 0.4 + } + } }, - "minimax/minimax-m2.1-lightning": { - "id": "minimax/minimax-m2.1-lightning", - "name": "MiniMax M2.1 Lightning", - "family": "minimax", + "grok-3-fast-latest": { + "id": "grok-3-fast-latest", + "name": "Grok 3 Fast Latest", + "family": "grok", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-10-27", - "last_updated": "2025-10-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 2.4, "cache_read": 0.03, "cache_write": 0.38 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 1.25 + } }, - "minimax/minimax-m2.7-highspeed": { - "id": "minimax/minimax-m2.7-highspeed", - "name": "MiniMax M2.7 High Speed", - "family": "minimax", + "grok-4.20-multi-agent-0309": { + "id": "grok-4.20-multi-agent-0309", + "name": "Grok 4.20 Multi-Agent", + "family": "grok", "attachment": true, "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.4, "cache_read": 0.06, "cache_write": 0.375 }, - "limit": { "context": 204800, "output": 131100 } - }, - "minimax/minimax-m2.5": { - "id": "minimax/minimax-m2.5", - "name": "MiniMax M2.5", - "family": "minimax", - "attachment": false, - "reasoning": true, - "tool_call": true, + "tool_call": false, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-09", + "last_updated": "2026-03-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.03, "cache_write": 0.375 }, - "limit": { "context": 204800, "output": 131000 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.2, + "tiers": [ + { + "input": 4, + "output": 12, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 12, + "cache_read": 0.4 + } + } }, - "minimax/minimax-m2.5-highspeed": { - "id": "minimax/minimax-m2.5-highspeed", - "name": "MiniMax M2.5 High Speed", - "family": "minimax", + "grok-4": { + "id": "grok-4", + "name": "Grok 4", + "family": "grok", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-03-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 2.4, "cache_read": 0.03, "cache_write": 0.375 }, - "limit": { "context": 0, "output": 0 } + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "reasoning": 15, + "cache_read": 0.75 + } }, - "xiaomi/mimo-v2-pro": { - "id": "xiaomi/mimo-v2-pro", - "name": "MiMo V2 Pro", - "family": "mimo", + "grok-2-latest": { + "id": "grok-2-latest", + "name": "Grok 2 Latest", + "family": "grok", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2026-03-18", - "last_updated": "2026-03-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-08", + "release_date": "2024-08-20", + "last_updated": "2024-12-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 3, "cache_read": 0.19999999999999998 }, - "limit": { "context": 1000000, "output": 128000 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 2, + "output": 10, + "cache_read": 2 + } }, - "xiaomi/mimo-v2-flash": { - "id": "xiaomi/mimo-v2-flash", - "name": "MiMo V2 Flash", - "family": "mimo", + "grok-beta": { + "id": "grok-beta", + "name": "Grok Beta", + "family": "grok-beta", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-08", + "release_date": "2024-11-01", + "last_updated": "2024-11-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.29 }, - "limit": { "context": 262144, "output": 32000 } + "limit": { + "context": 131072, + "output": 4096 + }, + "cost": { + "input": 5, + "output": 15, + "cache_read": 5 + } }, - "vercel/v0-1.5-md": { - "id": "vercel/v0-1.5-md", - "name": "v0-1.5-md", - "family": "v0", + "grok-2-vision": { + "id": "grok-2-vision", + "name": "Grok 2 Vision", + "family": "grok", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2025-06-09", - "last_updated": "2025-06-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-08", + "release_date": "2024-08-20", + "last_updated": "2024-08-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 128000, "output": 32000 } + "limit": { + "context": 8192, + "output": 4096 + }, + "cost": { + "input": 2, + "output": 10, + "cache_read": 2 + } }, - "vercel/v0-1.0-md": { - "id": "vercel/v0-1.0-md", - "name": "v0-1.0-md", - "family": "v0", + "grok-4-1-fast": { + "id": "grok-4-1-fast", + "name": "Grok 4.1 Fast", + "family": "grok", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-11-19", + "last_updated": "2025-11-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 128000, "output": 32000 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "moonshotai/kimi-k2-thinking": { - "id": "moonshotai/kimi-k2-thinking", - "name": "Kimi K2 Thinking", - "family": "kimi-thinking", + "grok-3-mini": { + "id": "grok-3-mini", + "name": "Grok 3 Mini", + "family": "grok", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": true, "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-11-06", - "last_updated": "2025-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.47, "output": 2, "cache_read": 0.14 }, - "limit": { "context": 216144, "output": 216144 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 0.5, + "reasoning": 0.5, + "cache_read": 0.075 + } }, - "moonshotai/kimi-k2.5": { - "id": "moonshotai/kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi", + "grok-2-vision-1212": { + "id": "grok-2-vision-1212", + "name": "Grok 2 Vision (1212)", + "family": "grok", "attachment": true, - "reasoning": true, - "tool_call": true, - "interleaved": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-01-26", - "last_updated": "2026-01-26", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 1.2 }, - "limit": { "context": 262144, "output": 262144 } - }, - "moonshotai/kimi-k2-turbo": { - "id": "moonshotai/kimi-k2-turbo", - "name": "Kimi K2 Turbo", - "family": "kimi", - "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, "knowledge": "2024-08", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-08-20", + "last_updated": "2024-12-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.4, "output": 10 }, - "limit": { "context": 256000, "output": 16384 } + "limit": { + "context": 8192, + "output": 4096 + }, + "cost": { + "input": 2, + "output": 10, + "cache_read": 2 + } }, - "moonshotai/kimi-k2-0905": { - "id": "moonshotai/kimi-k2-0905", - "name": "Kimi K2 0905", - "family": "kimi", + "grok-3": { + "id": "grok-3", + "name": "Grok 3", + "family": "grok", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 2.5 }, - "limit": { "context": 131072, "output": 16384 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75 + } }, - "moonshotai/kimi-k2-thinking-turbo": { - "id": "moonshotai/kimi-k2-thinking-turbo", - "name": "Kimi K2 Thinking Turbo", - "family": "kimi-thinking", - "attachment": false, - "reasoning": true, + "grok-4-fast-non-reasoning": { + "id": "grok-4-fast-non-reasoning", + "name": "Grok 4 Fast (Non-Reasoning)", + "family": "grok", + "attachment": true, + "reasoning": false, "tool_call": true, - "interleaved": true, "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-11-06", - "last_updated": "2025-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-09-19", + "last_updated": "2025-09-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.15, "output": 8, "cache_read": 0.15 }, - "limit": { "context": 262114, "output": 262114 } - }, - "moonshotai/kimi-k2": { - "id": "moonshotai/kimi-k2", - "name": "Kimi K2 Instruct", - "family": "kimi", + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } + } + } + }, + "meganova": { + "id": "meganova", + "env": ["MEGANOVA_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.meganova.ai/v1", + "name": "Meganova", + "doc": "https://docs.meganova.ai", + "models": { + "Qwen/Qwen3-235B-A22B-Instruct-2507": { + "id": "Qwen/Qwen3-235B-A22B-Instruct-2507", + "name": "Qwen3 235B A22B Instruct 2507", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-07-14", - "last_updated": "2025-07-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1, "output": 3 }, - "limit": { "context": 131072, "output": 16384 }, - "status": "deprecated" + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.09, + "output": 0.6 + } }, - "alibaba/qwen-3-32b": { - "id": "alibaba/qwen-3-32b", - "name": "Qwen 3.32B", + "Qwen/Qwen3.5-Plus": { + "id": "Qwen/Qwen3.5-Plus", + "name": "Qwen3.5 Plus", "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 40960, "output": 16384 } - }, - "alibaba/qwen3-embedding-8b": { - "id": "alibaba/qwen3-embedding-8b", - "name": "Qwen3 Embedding 8B", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2025-06-05", - "last_updated": "2025-06-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02", + "last_updated": "2026-02", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.05, "output": 0 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.4, + "output": 2.4, + "reasoning": 2.4 + } }, - "alibaba/qwen3.5-plus": { - "id": "alibaba/qwen3.5-plus", - "name": "Qwen 3.5 Plus", + "Qwen/Qwen2.5-VL-32B-Instruct": { + "id": "Qwen/Qwen2.5-VL-32B-Instruct", + "name": "Qwen2.5 VL 32B Instruct", "family": "qwen", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-02-16", - "last_updated": "2026-02-19", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 2.4, "cache_read": 0.04, "cache_write": 0.5 }, - "limit": { "context": 1000000, "output": 64000 } + "release_date": "2025-03-24", + "last_updated": "2025-03-24", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 16384, + "output": 16384 + }, + "cost": { + "input": 0.2, + "output": 0.6 + } }, - "alibaba/qwen3-coder-next": { - "id": "alibaba/qwen3-coder-next", - "name": "Qwen3 Coder Next", - "family": "qwen", + "zai-org/GLM-4.7": { + "id": "zai-org/GLM-4.7", + "name": "GLM-4.7", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2025-07-22", - "last_updated": "2026-02-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 1.2 }, - "limit": { "context": 256000, "output": 256000 } - }, - "alibaba/qwen3-max-preview": { - "id": "alibaba/qwen3-max-preview", - "name": "Qwen3 Max Preview", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, "knowledge": "2025-04", - "release_date": "2025-09-23", - "last_updated": "2025-09-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.2, "output": 6, "cache_read": 0.24 }, - "limit": { "context": 262144, "output": 32768 } + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 131072 + }, + "cost": { + "input": 0.2, + "output": 0.8 + } }, - "alibaba/qwen3-coder": { - "id": "alibaba/qwen3-coder", - "name": "Qwen3 Coder 480B A35B Instruct", - "family": "qwen", + "zai-org/GLM-5": { + "id": "zai-org/GLM-5", + "name": "GLM-5", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.38, "output": 1.53 }, - "limit": { "context": 262144, "output": 66536 } - }, - "alibaba/qwen3-embedding-0.6b": { - "id": "alibaba/qwen3-embedding-0.6b", - "name": "Qwen3 Embedding 0.6B", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.01, "output": 0 }, - "limit": { "context": 32768, "output": 32768 } + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 131072 + }, + "cost": { + "input": 0.8, + "output": 2.56 + } }, - "alibaba/qwen-3-235b": { - "id": "alibaba/qwen-3-235b", - "name": "Qwen3 235B A22B Instruct 2507", - "family": "qwen", + "zai-org/GLM-4.6": { + "id": "zai-org/GLM-4.6", + "name": "GLM-4.6", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.13, "output": 0.6 }, - "limit": { "context": 40960, "output": 16384 } + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 131072 + }, + "cost": { + "input": 0.45, + "output": 1.9 + } }, - "alibaba/qwen3-vl-instruct": { - "id": "alibaba/qwen3-vl-instruct", - "name": "Qwen3 VL Instruct", - "family": "qwen", + "mistralai/Mistral-Small-3.2-24B-Instruct-2506": { + "id": "mistralai/Mistral-Small-3.2-24B-Instruct-2506", + "name": "Mistral Small 3.2 24B Instruct", + "family": "mistral-small", "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-24", - "last_updated": "2025-09-24", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-06-20", + "last_updated": "2025-06-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.7, "output": 2.8 }, - "limit": { "context": 131072, "output": 129024 } + "limit": { + "context": 32768, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "alibaba/qwen3-next-80b-a3b-thinking": { - "id": "alibaba/qwen3-next-80b-a3b-thinking", - "name": "Qwen3 Next 80B A3B Thinking", - "family": "qwen", + "mistralai/Mistral-Nemo-Instruct-2407": { + "id": "mistralai/Mistral-Nemo-Instruct-2407", + "name": "Mistral Nemo Instruct 2407", + "family": "mistral", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-09-12", - "last_updated": "2025-09-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 1.5 }, - "limit": { "context": 131072, "output": 65536 } - }, - "alibaba/qwen3-235b-a22b-thinking": { - "id": "alibaba/qwen3-235b-a22b-thinking", - "name": "Qwen3 235B A22B Thinking 2507", - "family": "qwen", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.9 }, - "limit": { "context": 262114, "output": 262114 } + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.02, + "output": 0.04 + } }, - "alibaba/qwen3-max-thinking": { - "id": "alibaba/qwen3-max-thinking", - "name": "Qwen 3 Max Thinking", - "family": "qwen", + "XiaomiMiMo/MiMo-V2-Flash": { + "id": "XiaomiMiMo/MiMo-V2-Flash", + "name": "MiMo V2 Flash", + "family": "mimo", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-01", - "last_updated": "2025-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12-01", + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1.2, "output": 6, "cache_read": 0.24 }, - "limit": { "context": 256000, "output": 65536 } + "limit": { + "context": 262144, + "output": 32000 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } }, - "alibaba/qwen3-next-80b-a3b-instruct": { - "id": "alibaba/qwen3-next-80b-a3b-instruct", - "name": "Qwen3 Next 80B A3B Instruct", - "family": "qwen", + "meta-llama/Llama-3.3-70B-Instruct": { + "id": "meta-llama/Llama-3.3-70B-Instruct", + "name": "Llama 3.3 70B Instruct", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-12", - "last_updated": "2025-09-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.09, "output": 1.1 }, - "limit": { "context": 262144, "output": 32768 } + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } }, - "alibaba/qwen3-embedding-4b": { - "id": "alibaba/qwen3-embedding-4b", - "name": "Qwen3 Embedding 4B", - "family": "qwen", + "deepseek-ai/DeepSeek-V3.1": { + "id": "deepseek-ai/DeepSeek-V3.1", + "name": "DeepSeek V3.1", + "family": "deepseek", "attachment": false, "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2025-06-05", - "last_updated": "2025-06-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.02, "output": 0 }, - "limit": { "context": 32768, "output": 32768 } - }, - "alibaba/qwen-3-30b": { - "id": "alibaba/qwen-3-30b", - "name": "Qwen3-30B-A3B", - "family": "qwen", - "attachment": false, - "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.08, "output": 0.29 }, - "limit": { "context": 40960, "output": 16384 } + "release_date": "2025-08-25", + "last_updated": "2025-08-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 164000, + "output": 164000 + }, + "cost": { + "input": 0.27, + "output": 1 + } }, - "alibaba/qwen3-coder-plus": { - "id": "alibaba/qwen3-coder-plus", - "name": "Qwen3 Coder Plus", - "family": "qwen", + "deepseek-ai/DeepSeek-V3-0324": { + "id": "deepseek-ai/DeepSeek-V3-0324", + "name": "DeepSeek V3 0324", + "family": "deepseek", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-03-24", + "last_updated": "2025-03-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1, "output": 5 }, - "limit": { "context": 1000000, "output": 1000000 } + "limit": { + "context": 163840, + "output": 163840 + }, + "cost": { + "input": 0.25, + "output": 0.88 + } }, - "alibaba/qwen3-max": { - "id": "alibaba/qwen3-max", - "name": "Qwen3 Max", - "family": "qwen", + "deepseek-ai/DeepSeek-V3.2-Exp": { + "id": "deepseek-ai/DeepSeek-V3.2-Exp", + "name": "DeepSeek V3.2 Exp", + "family": "deepseek", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-23", - "last_updated": "2025-09-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.2, "output": 6 }, - "limit": { "context": 262144, "output": 32768 } + "release_date": "2025-10-10", + "last_updated": "2025-10-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 164000, + "output": 164000 + }, + "cost": { + "input": 0.27, + "output": 0.4 + } }, - "alibaba/qwen3-coder-30b-a3b": { - "id": "alibaba/qwen3-coder-30b-a3b", - "name": "Qwen 3 Coder 30B A3B Instruct", - "family": "qwen", + "deepseek-ai/DeepSeek-R1-0528": { + "id": "deepseek-ai/DeepSeek-R1-0528", + "name": "DeepSeek R1 0528", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, + "tool_call": false, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-07", + "release_date": "2025-05-28", + "last_updated": "2025-05-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 64000 + }, + "cost": { + "input": 0.5, + "output": 2.15 + } + }, + "deepseek-ai/DeepSeek-V3.2": { + "id": "deepseek-ai/DeepSeek-V3.2", + "name": "DeepSeek V3.2", + "family": "deepseek", + "attachment": false, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.07, "output": 0.27 }, - "limit": { "context": 160000, "output": 32768 } + "release_date": "2025-12-03", + "last_updated": "2025-12-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 164000, + "output": 164000 + }, + "cost": { + "input": 0.26, + "output": 0.38 + } }, - "alibaba/qwen3.5-flash": { - "id": "alibaba/qwen3.5-flash", - "name": "Qwen 3.5 Flash", - "family": "qwen", - "attachment": true, + "moonshotai/Kimi-K2-Thinking": { + "id": "moonshotai/Kimi-K2-Thinking", + "name": "Kimi K2 Thinking", + "family": "kimi-thinking", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2026-02-24", - "last_updated": "2026-02-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.001, "cache_write": 0.125 }, - "limit": { "context": 1000000, "output": 64000 } + "knowledge": "2024-08", + "release_date": "2025-11-06", + "last_updated": "2025-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 2.6 + } }, - "alibaba/qwen-3-14b": { - "id": "alibaba/qwen-3-14b", - "name": "Qwen3-14B", - "family": "qwen", + "moonshotai/Kimi-K2.5": { + "id": "moonshotai/Kimi-K2.5", + "name": "Kimi K2.5", + "family": "kimi", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.06, "output": 0.24 }, - "limit": { "context": 40960, "output": 16384 } + "knowledge": "2026-01", + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.45, + "output": 2.8 + } }, - "alibaba/qwen3-vl-thinking": { - "id": "alibaba/qwen3-vl-thinking", - "name": "Qwen3 VL Thinking", - "family": "qwen", - "attachment": true, + "MiniMaxAI/MiniMax-M2.5": { + "id": "MiniMaxAI/MiniMax-M2.5", + "name": "MiniMax M2.5", + "family": "minimax", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-09-24", - "last_updated": "2025-09-24", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.7, "output": 8.4 }, - "limit": { "context": 131072, "output": 129024 } - }, - "mistral/mistral-embed": { - "id": "mistral/mistral-embed", - "name": "Mistral Embed", - "family": "mistral-embed", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2023-12-11", - "last_updated": "2023-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0 }, - "limit": { "context": 8192, "output": 1536 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "mistral/devstral-small": { - "id": "mistral/devstral-small", - "name": "Devstral Small 1.1", - "family": "devstral", + "MiniMaxAI/MiniMax-M2.1": { + "id": "MiniMaxAI/MiniMax-M2.1", + "name": "MiniMax M2.1", + "family": "minimax", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-05-07", - "last_updated": "2025-05-07", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 128000, "output": 64000 } - }, - "mistral/mistral-large-3": { - "id": "mistral/mistral-large-3", - "name": "Mistral Large 3", - "family": "mistral-large", + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 196608, + "output": 131072 + }, + "cost": { + "input": 0.28, + "output": 1.2 + } + } + } + }, + "google-vertex-anthropic": { + "id": "google-vertex-anthropic", + "env": ["GOOGLE_VERTEX_PROJECT", "GOOGLE_VERTEX_LOCATION", "GOOGLE_APPLICATION_CREDENTIALS"], + "npm": "@ai-sdk/google-vertex/anthropic", + "name": "Vertex (Anthropic)", + "doc": "https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/claude", + "models": { + "claude-haiku-4-5@20251001": { + "id": "claude-haiku-4-5@20251001", + "name": "Claude Haiku 4.5", + "family": "claude-haiku", "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-12-02", - "last_updated": "2025-12-02", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 1.5 }, - "limit": { "context": 256000, "output": 256000 } - }, - "mistral/codestral-embed": { - "id": "mistral/codestral-embed", - "name": "Codestral Embed", - "family": "codestral-embed", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2025-05-28", - "last_updated": "2025-05-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0 }, - "limit": { "context": 8192, "output": 1536 } - }, - "mistral/mistral-nemo": { - "id": "mistral/mistral-nemo", - "name": "Mistral Nemo", - "family": "mistral-nemo", - "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-07-01", - "last_updated": "2024-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.04, "output": 0.17 }, - "limit": { "context": 60288, "output": 16000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } }, - "mistral/mistral-medium": { - "id": "mistral/mistral-medium", - "name": "Mistral Medium 3.1", - "family": "mistral-medium", + "claude-sonnet-4-6@default": { + "id": "claude-sonnet-4-6@default", + "name": "Claude Sonnet 4.6", + "family": "claude-sonnet", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-05-07", - "last_updated": "2025-05-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 128000, "output": 64000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75, + "context_over_200k": { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5 + }, + "tiers": [ + { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ] + } }, - "mistral/devstral-2": { - "id": "mistral/devstral-2", - "name": "Devstral 2", - "family": "devstral", - "attachment": false, + "claude-3-5-haiku@20241022": { + "id": "claude-3-5-haiku@20241022", + "name": "Claude Haiku 3.5", + "family": "claude-haiku", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-12-09", - "last_updated": "2025-12-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-07-31", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 256000, "output": 256000 } + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 4, + "cache_read": 0.08, + "cache_write": 1 + } }, - "mistral/devstral-small-2": { - "id": "mistral/devstral-small-2", - "name": "Devstral Small 2", - "family": "devstral", - "attachment": false, + "claude-3-5-sonnet@20241022": { + "id": "claude-3-5-sonnet@20241022", + "name": "Claude Sonnet 3.5 v2", + "family": "claude-sonnet", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-05-07", - "last_updated": "2025-05-07", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04-30", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 256000, "output": 256000 } + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "mistral/ministral-14b": { - "id": "mistral/ministral-14b", - "name": "Ministral 14B", - "family": "ministral", + "claude-opus-4-1@20250805": { + "id": "claude-opus-4-1@20250805", + "name": "Claude Opus 4.1", + "family": "claude-opus", "attachment": true, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 256000, "output": 256000 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "mistral/pixtral-12b": { - "id": "mistral/pixtral-12b", - "name": "Pixtral 12B", - "family": "pixtral", + "claude-sonnet-4@20250514": { + "id": "claude-sonnet-4@20250514", + "name": "Claude Sonnet 4", + "family": "claude-sonnet", "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-09", - "release_date": "2024-09-01", - "last_updated": "2024-09-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.15 }, - "limit": { "context": 128000, "output": 128000 } - }, - "mistral/codestral": { - "id": "mistral/codestral", - "name": "Codestral (latest)", - "family": "codestral", - "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-05-29", - "last_updated": "2025-01-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 0.9 }, - "limit": { "context": 256000, "output": 4096 } + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "mistral/ministral-8b": { - "id": "mistral/ministral-8b", - "name": "Ministral 8B (latest)", - "family": "ministral", - "attachment": false, - "reasoning": false, + "claude-3-7-sonnet@20250219": { + "id": "claude-3-7-sonnet@20250219", + "name": "Claude Sonnet 3.7", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-10-01", - "last_updated": "2024-10-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.1 }, - "limit": { "context": 128000, "output": 128000 } + "knowledge": "2024-10-31", + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "mistral/mistral-small": { - "id": "mistral/mistral-small", - "name": "Mistral Small (latest)", - "family": "mistral-small", + "claude-opus-4@20250514": { + "id": "claude-opus-4@20250514", + "name": "Claude Opus 4", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2026-03-16", - "last_updated": "2026-03-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 256000, "output": 256000 } + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "mistral/ministral-3b": { - "id": "mistral/ministral-3b", - "name": "Ministral 3B (latest)", - "family": "ministral", - "attachment": false, - "reasoning": false, + "claude-opus-4-5@20251101": { + "id": "claude-opus-4-5@20251101", + "name": "Claude Opus 4.5", + "family": "claude-opus", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-10-01", - "last_updated": "2024-10-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.04, "output": 0.04 }, - "limit": { "context": 128000, "output": 128000 } + "knowledge": "2025-03-31", + "release_date": "2025-11-01", + "last_updated": "2025-11-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "mistral/pixtral-large": { - "id": "mistral/pixtral-large", - "name": "Pixtral Large (latest)", - "family": "pixtral", + "claude-sonnet-4-5@20250929": { + "id": "claude-sonnet-4-5@20250929", + "name": "Claude Sonnet 4.5", + "family": "claude-sonnet", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-11", - "release_date": "2024-11-01", - "last_updated": "2024-11-04", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2, "output": 6 }, - "limit": { "context": 128000, "output": 128000 } + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "mistral/magistral-small": { - "id": "mistral/magistral-small", - "name": "Magistral Small", - "family": "magistral-small", - "attachment": false, + "claude-opus-4-6@default": { + "id": "claude-opus-4-6@default", + "name": "Claude Opus 4.6", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-03-17", - "last_updated": "2025-03-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.5, "output": 1.5 }, - "limit": { "context": 128000, "output": 128000 } + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25, + "context_over_200k": { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5 + }, + "tiers": [ + { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ] + } }, - "mistral/magistral-medium": { - "id": "mistral/magistral-medium", - "name": "Magistral Medium (latest)", - "family": "magistral-medium", - "attachment": false, + "claude-opus-4-7@default": { + "id": "claude-opus-4-7@default", + "name": "Claude Opus 4.7", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-03-17", - "last_updated": "2025-03-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2, "output": 5 }, - "limit": { "context": 128000, "output": 16384 } - }, - "mistral/mixtral-8x22b-instruct": { - "id": "mistral/mixtral-8x22b-instruct", - "name": "Mixtral 8x22B", - "family": "mixtral", + "temperature": false, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25, + "context_over_200k": { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5 + }, + "tiers": [ + { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ] + } + } + } + }, + "evroc": { + "id": "evroc", + "env": ["EVROC_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://models.think.evroc.com/v1", + "name": "evroc", + "doc": "https://docs.evroc.com/products/think/overview.html", + "models": { + "Qwen/Qwen3-VL-30B-A3B-Instruct": { + "id": "Qwen/Qwen3-VL-30B-A3B-Instruct", + "name": "Qwen3 VL 30B", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-04-17", - "last_updated": "2024-04-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-07-30", + "last_updated": "2025-07-30", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 2, "output": 6 }, - "limit": { "context": 64000, "output": 64000 } + "limit": { + "context": 100000, + "output": 100000 + }, + "cost": { + "input": 0.24, + "output": 0.94 + } }, - "meta/llama-3.2-90b": { - "id": "meta/llama-3.2-90b", - "name": "Llama 3.2 90B Vision Instruct", - "family": "llama", - "attachment": true, + "Qwen/Qwen3-Embedding-8B": { + "id": "Qwen/Qwen3-Embedding-8B", + "name": "Qwen3 Embedding 8B", + "family": "text-embedding", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.72, "output": 0.72 }, - "limit": { "context": 128000, "output": 8192 } + "tool_call": false, + "release_date": "2025-07-30", + "last_updated": "2025-07-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 40960, + "output": 40960 + }, + "cost": { + "input": 0.12, + "output": 0.12 + } }, - "meta/llama-3.2-11b": { - "id": "meta/llama-3.2-11b", - "name": "Llama 3.2 11B Vision Instruct", - "family": "llama", - "attachment": true, + "Qwen/Qwen3-30B-A3B-Instruct-2507-FP8": { + "id": "Qwen/Qwen3-30B-A3B-Instruct-2507-FP8", + "name": "Qwen3 30B 2507", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.16, "output": 0.16 }, - "limit": { "context": 128000, "output": 8192 } + "release_date": "2025-07-30", + "last_updated": "2025-07-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 64000, + "output": 64000 + }, + "cost": { + "input": 0.35, + "output": 1.42 + } }, - "meta/llama-3.1-70b": { - "id": "meta/llama-3.1-70b", - "name": "Llama 3.1 70B Instruct", - "family": "llama", + "mistralai/devstral-small-2-24b-instruct-2512": { + "id": "mistralai/devstral-small-2-24b-instruct-2512", + "name": "Devstral Small 2 24B Instruct 2512", + "family": "devstral", "attachment": false, "reasoning": false, "tool_call": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 0.4 }, - "limit": { "context": 131072, "output": 16384 } + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.12, + "output": 0.47 + } }, - "meta/llama-3.2-3b": { - "id": "meta/llama-3.2-3b", - "name": "Llama 3.2 3B Instruct", - "family": "llama", + "mistralai/Voxtral-Small-24B-2507": { + "id": "mistralai/Voxtral-Small-24B-2507", + "name": "Voxtral Small 24B", + "family": "voxtral", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-09-18", - "last_updated": "2024-09-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.15 }, - "limit": { "context": 128000, "output": 8192 } + "release_date": "2025-03-01", + "last_updated": "2025-03-01", + "modalities": { + "input": ["audio", "text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32000, + "output": 32000 + }, + "cost": { + "input": 0.00236, + "output": 0.00236, + "output_audio": 2.36 + } }, - "meta/llama-3.1-8b": { - "id": "meta/llama-3.1-8b", - "name": "Llama 3.1 8B Instruct", - "family": "llama", + "mistralai/Magistral-Small-2509": { + "id": "mistralai/Magistral-Small-2509", + "name": "Magistral Small 1.2 24B", + "family": "magistral-small", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.03, "output": 0.05 }, - "limit": { "context": 131072, "output": 16384 } + "tool_call": false, + "release_date": "2025-06-01", + "last_updated": "2025-06-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.59, + "output": 2.36 + } }, - "meta/llama-3.2-1b": { - "id": "meta/llama-3.2-1b", - "name": "Llama 3.2 1B Instruct", - "family": "llama", + "microsoft/Phi-4-multimodal-instruct": { + "id": "microsoft/Phi-4-multimodal-instruct", + "name": "Phi-4 15B", + "family": "phi", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-09-18", - "last_updated": "2024-09-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.1 }, - "limit": { "context": 128000, "output": 8192 } + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32000, + "output": 32000 + }, + "cost": { + "input": 0.24, + "output": 0.47 + } }, - "meta/llama-3.3-70b": { - "id": "meta/llama-3.3-70b", - "name": "Llama-3.3-70B-Instruct", - "family": "llama", - "attachment": true, + "KBLab/kb-whisper-large": { + "id": "KBLab/kb-whisper-large", + "name": "KB Whisper", + "family": "whisper", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "release_date": "2024-10-01", + "last_updated": "2024-10-01", + "modalities": { + "input": ["audio"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 448, + "output": 448 + }, + "cost": { + "input": 0.00236, + "output": 0.00236, + "output_audio": 2.36 + } }, - "meta/llama-4-maverick": { - "id": "meta/llama-4-maverick", - "name": "Llama-4-Maverick-17B-128E-Instruct-FP8", + "nvidia/Llama-3.3-70B-Instruct-FP8": { + "id": "nvidia/Llama-3.3-70B-Instruct-FP8", + "name": "Llama 3.3 70B", "family": "llama", - "attachment": true, + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 1.18, + "output": 1.18 + } }, - "meta/llama-4-scout": { - "id": "meta/llama-4-scout", - "name": "Llama-4-Scout-17B-16E-Instruct-FP8", - "family": "llama", - "attachment": true, + "openai/whisper-large-v3": { + "id": "openai/whisper-large-v3", + "name": "Whisper 3 Large", + "family": "whisper", + "attachment": false, "reasoning": false, + "tool_call": false, + "release_date": "2024-10-01", + "last_updated": "2024-10-01", + "modalities": { + "input": ["audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 448, + "output": 4096 + }, + "cost": { + "input": 0.00236, + "output": 0.00236, + "output_audio": 2.36 + } + }, + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "name": "GPT OSS 120B", + "family": "gpt-oss", + "attachment": false, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 65536, + "output": 65536 + }, + "cost": { + "input": 0.24, + "output": 0.94 + } }, - "inception/mercury-2": { - "id": "inception/mercury-2", - "name": "Mercury 2", - "family": "mercury", + "moonshotai/Kimi-K2.5": { + "id": "moonshotai/Kimi-K2.5", + "name": "Kimi K2.5", + "family": "kimi", "attachment": false, "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2026-02-24", - "last_updated": "2026-03-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 0.75, "cache_read": 0.024999999999999998 }, - "limit": { "context": 128000, "output": 128000 } + "interleaved": { + "field": "reasoning_content" + }, + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 1.47, + "output": 5.9 + } }, - "inception/mercury-coder-small": { - "id": "inception/mercury-coder-small", - "name": "Mercury Coder Small Beta", - "family": "mercury", + "intfloat/multilingual-e5-large-instruct": { + "id": "intfloat/multilingual-e5-large-instruct", + "name": "E5 Multi-Lingual Large Embeddings 0.6B", + "family": "text-embedding", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-02-26", - "last_updated": "2025-02-26", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 1 }, - "limit": { "context": 32000, "output": 16384 } + "tool_call": false, + "release_date": "2024-06-01", + "last_updated": "2024-06-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 512, + "output": 512 + }, + "cost": { + "input": 0.12, + "output": 0.12 + } } } }, @@ -41977,54 +79686,30 @@ "name": "Synthetic", "doc": "https://synthetic.new/pricing", "models": { - "hf:moonshotai/Kimi-K2.5": { - "id": "hf:moonshotai/Kimi-K2.5", - "name": "Kimi K2.5", - "family": "kimi", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.55, "output": 2.19 }, - "limit": { "context": 262144, "output": 65536 } - }, - "hf:moonshotai/Kimi-K2-Instruct-0905": { - "id": "hf:moonshotai/Kimi-K2-Instruct-0905", - "name": "Kimi K2 0905", - "family": "kimi", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1.2, "output": 1.2 }, - "limit": { "context": 262144, "output": 32768 } - }, - "hf:moonshotai/Kimi-K2-Thinking": { - "id": "hf:moonshotai/Kimi-K2-Thinking", - "name": "Kimi K2 Thinking", - "family": "kimi-thinking", + "hf:meta-llama/Llama-3.1-405B-Instruct": { + "id": "hf:meta-llama/Llama-3.1-405B-Instruct", + "name": "Llama-3.1-405B-Instruct", + "family": "llama", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-11", - "release_date": "2025-11-07", - "last_updated": "2025-11-07", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.55, "output": 2.19 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 3, + "output": 3 + } }, "hf:meta-llama/Llama-4-Scout-17B-16E-Instruct": { "id": "hf:meta-llama/Llama-4-Scout-17B-16E-Instruct", @@ -42037,26 +79722,19 @@ "knowledge": "2024-08", "release_date": "2025-04-05", "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 328000, "output": 4096 } - }, - "hf:meta-llama/Llama-3.1-8B-Instruct": { - "id": "hf:meta-llama/Llama-3.1-8B-Instruct", - "name": "Llama-3.1-8B-Instruct", - "family": "llama", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 328000, + "output": 4096 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, "hf:meta-llama/Llama-3.3-70B-Instruct": { "id": "hf:meta-llama/Llama-3.3-70B-Instruct", @@ -42069,14 +79747,23 @@ "knowledge": "2023-12", "release_date": "2024-12-06", "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.9, "output": 0.9 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.9, + "output": 0.9 + } }, - "hf:meta-llama/Llama-3.1-405B-Instruct": { - "id": "hf:meta-llama/Llama-3.1-405B-Instruct", - "name": "Llama-3.1-405B-Instruct", + "hf:meta-llama/Llama-3.1-8B-Instruct": { + "id": "hf:meta-llama/Llama-3.1-8B-Instruct", + "name": "Llama-3.1-8B-Instruct", "family": "llama", "attachment": false, "reasoning": true, @@ -42085,10 +79772,19 @@ "knowledge": "2023-12", "release_date": "2024-07-23", "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 3, "output": 3 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.2, + "output": 0.2 + } }, "hf:meta-llama/Llama-3.1-70B-Instruct": { "id": "hf:meta-llama/Llama-3.1-70B-Instruct", @@ -42101,10 +79797,19 @@ "knowledge": "2023-12", "release_date": "2024-07-23", "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.9, "output": 0.9 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.9, + "output": 0.9 + } }, "hf:meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8": { "id": "hf:meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8", @@ -42117,201 +79822,43 @@ "knowledge": "2024-08", "release_date": "2025-04-05", "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.22, "output": 0.88 }, - "limit": { "context": 524000, "output": 4096 } - }, - "hf:zai-org/GLM-4.7": { - "id": "hf:zai-org/GLM-4.7", - "name": "GLM 4.7", - "family": "glm", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.55, "output": 2.19 }, - "limit": { "context": 200000, "output": 64000 } - }, - "hf:zai-org/GLM-4.7-Flash": { - "id": "hf:zai-org/GLM-4.7-Flash", - "name": "GLM-4.7-Flash", - "family": "glm", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, - "temperature": true, - "release_date": "2026-01-18", - "last_updated": "2026-01-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.06, "output": 0.4, "cache_read": 0.06 }, - "limit": { "context": 196608, "output": 65536 } - }, - "hf:zai-org/GLM-4.6": { - "id": "hf:zai-org/GLM-4.6", - "name": "GLM 4.6", - "family": "glm", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.55, "output": 2.19 }, - "limit": { "context": 200000, "output": 64000 } - }, - "hf:deepseek-ai/DeepSeek-V3.1": { - "id": "hf:deepseek-ai/DeepSeek-V3.1", - "name": "DeepSeek V3.1", - "family": "deepseek", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-08-21", - "last_updated": "2025-08-21", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.56, "output": 1.68 }, - "limit": { "context": 128000, "output": 128000 } - }, - "hf:deepseek-ai/DeepSeek-V3-0324": { - "id": "hf:deepseek-ai/DeepSeek-V3-0324", - "name": "DeepSeek V3 (0324)", - "family": "deepseek", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2025-08-01", - "last_updated": "2025-08-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.2, "output": 1.2 }, - "limit": { "context": 128000, "output": 128000 } - }, - "hf:deepseek-ai/DeepSeek-R1": { - "id": "hf:deepseek-ai/DeepSeek-R1", - "name": "DeepSeek R1", - "family": "deepseek-thinking", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.55, "output": 2.19 }, - "limit": { "context": 128000, "output": 128000 } - }, - "hf:deepseek-ai/DeepSeek-V3.1-Terminus": { - "id": "hf:deepseek-ai/DeepSeek-V3.1-Terminus", - "name": "DeepSeek V3.1 Terminus", - "family": "deepseek", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-09-22", - "last_updated": "2025-09-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.2, "output": 1.2 }, - "limit": { "context": 128000, "output": 128000 } - }, - "hf:deepseek-ai/DeepSeek-R1-0528": { - "id": "hf:deepseek-ai/DeepSeek-R1-0528", - "name": "DeepSeek R1 (0528)", - "family": "deepseek-thinking", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-08-01", - "last_updated": "2025-08-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 8 }, - "limit": { "context": 128000, "output": 128000 } - }, - "hf:deepseek-ai/DeepSeek-V3.2": { - "id": "hf:deepseek-ai/DeepSeek-V3.2", - "name": "DeepSeek V3.2", - "family": "deepseek", - "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.27, "output": 0.4, "cache_read": 0.27, "cache_write": 0 }, - "limit": { "context": 162816, "input": 162816, "output": 8000 } - }, - "hf:deepseek-ai/DeepSeek-V3": { - "id": "hf:deepseek-ai/DeepSeek-V3", - "name": "DeepSeek V3", - "family": "deepseek", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-01-20", - "last_updated": "2025-05-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1.25, "output": 1.25 }, - "limit": { "context": 128000, "output": 128000 } - }, - "hf:nvidia/Kimi-K2.5-NVFP4": { - "id": "hf:nvidia/Kimi-K2.5-NVFP4", - "name": "Kimi K2.5 (NVFP4)", - "family": "kimi", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.55, "output": 2.19 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 524000, + "output": 4096 + }, + "cost": { + "input": 0.22, + "output": 0.88 + } }, - "hf:MiniMaxAI/MiniMax-M2.1": { - "id": "hf:MiniMaxAI/MiniMax-M2.1", - "name": "MiniMax-M2.1", + "hf:MiniMaxAI/MiniMax-M2": { + "id": "hf:MiniMaxAI/MiniMax-M2", + "name": "MiniMax-M2", "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-10-27", + "last_updated": "2025-10-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.55, "output": 2.19 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 196608, + "output": 131000 + }, + "cost": { + "input": 0.55, + "output": 2.19 + } }, "hf:MiniMaxAI/MiniMax-M2.5": { "id": "hf:MiniMaxAI/MiniMax-M2.5", @@ -42320,45 +79867,84 @@ "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, "release_date": "2026-02-07", "last_updated": "2026-02-07", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 3, "cache_read": 0.6 }, - "limit": { "context": 191488, "output": 65536 } + "limit": { + "context": 191488, + "output": 65536 + }, + "cost": { + "input": 0.6, + "output": 3, + "cache_read": 0.6 + } }, - "hf:MiniMaxAI/MiniMax-M2": { - "id": "hf:MiniMaxAI/MiniMax-M2", - "name": "MiniMax-M2", + "hf:MiniMaxAI/MiniMax-M2.1": { + "id": "hf:MiniMaxAI/MiniMax-M2.1", + "name": "MiniMax-M2.1", "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-10-27", - "last_updated": "2025-10-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.55, "output": 2.19 }, - "limit": { "context": 196608, "output": 131000 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.55, + "output": 2.19 + } }, - "hf:openai/gpt-oss-120b": { - "id": "hf:openai/gpt-oss-120b", - "name": "GPT OSS 120B", - "family": "gpt-oss", + "hf:Qwen/Qwen3.5-397B-A17B": { + "id": "hf:Qwen/Qwen3.5-397B-A17B", + "name": "Qwen3.5-97B-A17B", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.1 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 262144, + "output": 65536 + }, + "status": "beta", + "cost": { + "input": 0.6, + "output": 3, + "cache_read": 0.6 + } }, "hf:Qwen/Qwen2.5-Coder-32B-Instruct": { "id": "hf:Qwen/Qwen2.5-Coder-32B-Instruct", @@ -42371,10 +79957,44 @@ "knowledge": "2024-10", "release_date": "2024-11-11", "last_updated": "2024-11-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.8, + "output": 0.8 + } + }, + "hf:Qwen/Qwen3-235B-A22B-Instruct-2507": { + "id": "hf:Qwen/Qwen3-235B-A22B-Instruct-2507", + "name": "Qwen 3 235B Instruct", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-04-28", + "last_updated": "2025-07-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.8, "output": 0.8 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 256000, + "output": 32000 + }, + "cost": { + "input": 0.2, + "output": 0.6 + } }, "hf:Qwen/Qwen3-235B-A22B-Thinking-2507": { "id": "hf:Qwen/Qwen3-235B-A22B-Thinking-2507", @@ -42387,10 +80007,19 @@ "knowledge": "2025-04", "release_date": "2025-07-25", "last_updated": "2025-07-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.65, "output": 3 }, - "limit": { "context": 256000, "output": 32000 } + "limit": { + "context": 256000, + "output": 32000 + }, + "cost": { + "input": 0.65, + "output": 3 + } }, "hf:Qwen/Qwen3-Coder-480B-A35B-Instruct": { "id": "hf:Qwen/Qwen3-Coder-480B-A35B-Instruct", @@ -42403,4543 +80032,6565 @@ "knowledge": "2025-04", "release_date": "2025-07-23", "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 2, "output": 2 }, - "limit": { "context": 256000, "output": 32000 } + "limit": { + "context": 256000, + "output": 32000 + }, + "cost": { + "input": 2, + "output": 2 + } }, - "hf:Qwen/Qwen3-235B-A22B-Instruct-2507": { - "id": "hf:Qwen/Qwen3-235B-A22B-Instruct-2507", - "name": "Qwen 3 235B Instruct", - "family": "qwen", + "hf:deepseek-ai/DeepSeek-V3.1": { + "id": "hf:deepseek-ai/DeepSeek-V3.1", + "name": "DeepSeek V3.1", + "family": "deepseek", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-08-21", + "last_updated": "2025-08-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.56, + "output": 1.68 + } + }, + "hf:deepseek-ai/DeepSeek-V3-0324": { + "id": "hf:deepseek-ai/DeepSeek-V3-0324", + "name": "DeepSeek V3 (0324)", + "family": "deepseek", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04-28", - "last_updated": "2025-07-21", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.6 }, - "limit": { "context": 256000, "output": 32000 } - } - } - }, - "llmgateway": { - "id": "llmgateway", - "env": ["LLMGATEWAY_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.llmgateway.io/v1", - "name": "LLM Gateway", - "doc": "https://llmgateway.io/docs", - "models": { - "gpt-5.2-codex": { - "id": "gpt-5.2-codex", - "name": "GPT-5.2 Codex", - "family": "gpt", - "attachment": true, + "release_date": "2025-08-01", + "last_updated": "2025-08-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 1.2, + "output": 1.2 + } + }, + "hf:deepseek-ai/DeepSeek-V3": { + "id": "hf:deepseek-ai/DeepSeek-V3", + "name": "DeepSeek V3", + "family": "deepseek", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-01-14", - "last_updated": "2026-01-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.18 }, - "limit": { "context": 400000, "output": 128000 } + "knowledge": "2024-07", + "release_date": "2025-01-20", + "last_updated": "2025-05-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 1.25 + } }, - "qwen3-coder-30b-a3b-instruct": { - "id": "qwen3-coder-30b-a3b-instruct", - "name": "Qwen3 Coder 30B A3B Instruct", - "family": "qwen", + "hf:deepseek-ai/DeepSeek-R1": { + "id": "hf:deepseek-ai/DeepSeek-R1", + "name": "DeepSeek R1", + "family": "deepseek-thinking", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-31", - "last_updated": "2025-07-31", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 262000, "output": 8192 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.55, + "output": 2.19 + } }, - "gemini-2.5-flash-lite": { - "id": "gemini-2.5-flash-lite", - "name": "Gemini 2.5 Flash Lite", - "family": "gemini", - "attachment": true, - "reasoning": false, + "hf:deepseek-ai/DeepSeek-R1-0528": { + "id": "hf:deepseek-ai/DeepSeek-R1-0528", + "name": "DeepSeek R1 (0528)", + "family": "deepseek-thinking", + "attachment": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-22", - "last_updated": "2025-07-22", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-01", + "last_updated": "2025-08-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.01 }, - "limit": { "context": 1048576, "output": 65535 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 3, + "output": 8 + } }, - "grok-4-20-multi-agent-beta-0309": { - "id": "grok-4-20-multi-agent-beta-0309", - "name": "Grok 4.20 Multi-Agent Beta (0309)", - "family": "grok", - "attachment": true, + "hf:deepseek-ai/DeepSeek-V3.2": { + "id": "hf:deepseek-ai/DeepSeek-V3.2", + "name": "DeepSeek V3.2", + "family": "deepseek", + "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2026-03-09", - "last_updated": "2026-03-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 6, "cache_read": 0.2 }, - "limit": { "context": 2000000, "output": 30000 } + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 162816, + "input": 162816, + "output": 8000 + }, + "cost": { + "input": 0.27, + "output": 0.4, + "cache_read": 0.27, + "cache_write": 0 + } }, - "grok-imagine-image": { - "id": "grok-imagine-image", - "name": "Grok Imagine Image", - "family": "grok", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, + "hf:deepseek-ai/DeepSeek-V3.1-Terminus": { + "id": "hf:deepseek-ai/DeepSeek-V3.1-Terminus", + "name": "DeepSeek V3.1 Terminus", + "family": "deepseek", + "attachment": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2026-03-02", - "last_updated": "2026-03-02", - "modalities": { "input": ["text", "image"], "output": ["text", "image"] }, + "release_date": "2025-09-22", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 2000, "output": 4096 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 1.2, + "output": 1.2 + } }, - "gpt-5.1-codex-mini": { - "id": "gpt-5.1-codex-mini", - "name": "GPT-5.1 Codex mini", - "family": "gpt", - "attachment": true, + "hf:openai/gpt-oss-120b": { + "id": "hf:openai/gpt-oss-120b", + "name": "GPT OSS 120B", + "family": "gpt-oss", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-11-12", - "last_updated": "2025-11-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.03 }, - "limit": { "context": 400000, "output": 128000 } + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "qwen3-235b-a22b-instruct-2507": { - "id": "qwen3-235b-a22b-instruct-2507", - "name": "Qwen3 235B A22B Instruct 2507", - "family": "qwen", + "hf:moonshotai/Kimi-K2-Thinking": { + "id": "hf:moonshotai/Kimi-K2-Thinking", + "name": "Kimi K2 Thinking", + "family": "kimi-thinking", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-11", + "release_date": "2025-11-07", + "last_updated": "2025-11-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.55, + "output": 2.19 + } + }, + "hf:moonshotai/Kimi-K2-Instruct-0905": { + "id": "hf:moonshotai/Kimi-K2-Instruct-0905", + "name": "Kimi K2 0905", + "family": "kimi", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-21", - "last_updated": "2025-07-21", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 0.6 }, - "limit": { "context": 262000, "output": 8192 }, - "status": "beta" + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 1.2, + "output": 1.2 + } }, - "gpt-5.4-pro": { - "id": "gpt-5.4-pro", - "name": "GPT-5.4 Pro", - "family": "gpt", - "attachment": true, + "hf:moonshotai/Kimi-K2.5": { + "id": "hf:moonshotai/Kimi-K2.5", + "name": "Kimi K2.5", + "family": "kimi", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2026-03-01", - "last_updated": "2026-03-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 30, "output": 180 }, - "limit": { "context": 1050000, "output": 128000 } + "knowledge": "2025-01", + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.55, + "output": 2.19 + } }, - "claude-opus-4-5-20251101": { - "id": "claude-opus-4-5-20251101", - "name": "Claude Opus 4.5", - "family": "claude", - "attachment": true, + "hf:nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4": { + "id": "hf:nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4", + "name": "Nemotron 3 Super 120B", + "family": "nemotron", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "release_date": "2025-11-24", - "last_updated": "2025-11-24", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5 }, - "limit": { "context": 200000, "output": 32000 } + "knowledge": "2024-04", + "release_date": "2026-03-11", + "last_updated": "2026-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 1, + "cache_read": 0.3 + } }, - "qwen-image": { - "id": "qwen-image", - "name": "Qwen Image", - "family": "qwen", + "hf:nvidia/Kimi-K2.5-NVFP4": { + "id": "hf:nvidia/Kimi-K2.5-NVFP4", + "name": "Kimi K2.5 (NVFP4)", + "family": "kimi", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-08-04", - "last_updated": "2025-08-04", - "modalities": { "input": ["text"], "output": ["text", "image"] }, + "knowledge": "2025-01", + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 2000, "output": 4096 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.55, + "output": 2.19 + } }, - "hermes-2-pro-llama-3-8b": { - "id": "hermes-2-pro-llama-3-8b", - "name": "Hermes 2 Pro Llama 3 8B", - "family": "nousresearch", + "hf:zai-org/GLM-4.7-Flash": { + "id": "hf:zai-org/GLM-4.7-Flash", + "name": "GLM-4.7-Flash", + "family": "glm", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2024-05-27", - "last_updated": "2024-05-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01-18", + "last_updated": "2026-01-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.14, "output": 0.14 }, - "limit": { "context": 8192, "output": 8192 }, - "status": "beta" + "limit": { + "context": 196608, + "output": 65536 + }, + "cost": { + "input": 0.06, + "output": 0.4, + "cache_read": 0.06 + } }, - "pixtral-large-latest": { - "id": "pixtral-large-latest", - "name": "Pixtral Large Latest", - "family": "mistral", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, + "hf:zai-org/GLM-4.7": { + "id": "hf:zai-org/GLM-4.7", + "name": "GLM 4.7", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2024-11-18", - "last_updated": "2024-11-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 4, "output": 12 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 0.55, + "output": 2.19 + } }, - "o3-mini": { - "id": "o3-mini", - "name": "o3 Mini", - "family": "gpt", + "hf:zai-org/GLM-5.1": { + "id": "hf:zai-org/GLM-5.1", + "name": "GLM 5.1", + "family": "glm", "attachment": false, "reasoning": true, - "tool_call": false, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "release_date": "2025-06-01", - "last_updated": "2025-06-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.55 }, - "limit": { "context": 200000, "output": 16384 } + "release_date": "2026-03-27", + "last_updated": "2026-04-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 196608, + "output": 65536 + }, + "cost": { + "input": 1, + "output": 3, + "cache_read": 1 + } }, - "gpt-5.4-mini": { - "id": "gpt-5.4-mini", - "name": "GPT-5.4 Mini", - "family": "gpt", - "attachment": true, + "hf:zai-org/GLM-5": { + "id": "hf:zai-org/GLM-5", + "name": "GLM-5", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "release_date": "2026-03-17", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.75, "output": 4.5, "cache_read": 0.08 }, - "limit": { "context": 400000, "output": 128000 } + "release_date": "2026-02-12", + "last_updated": "2026-04-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 196608, + "output": 65536 + }, + "cost": { + "input": 1, + "output": 3, + "cache_read": 1 + } }, - "gpt-5-pro": { - "id": "gpt-5-pro", - "name": "GPT-5 Pro", - "family": "gpt", - "attachment": true, + "hf:zai-org/GLM-4.6": { + "id": "hf:zai-org/GLM-4.6", + "name": "GLM 4.6", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-08-01", - "last_updated": "2025-08-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 120 }, - "limit": { "context": 400000, "output": 272000 } + "knowledge": "2025-04", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 0.55, + "output": 2.19 + } }, - "grok-4-fast": { - "id": "grok-4-fast", - "name": "Grok 4 Fast", - "family": "grok", + "hf:moonshotai/Kimi-K2.6": { + "id": "hf:moonshotai/Kimi-K2.6", + "name": "Kimi K2.6", + "family": "kimi-k2.6", "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } - }, - "qwen-turbo": { - "id": "qwen-turbo", - "name": "Qwen Turbo", - "family": "qwen", + "knowledge": "2025-01", + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.95, + "output": 4, + "cache_read": 0.95 + } + } + } + }, + "nvidia": { + "id": "nvidia", + "env": ["NVIDIA_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://integrate.api.nvidia.com/v1", + "name": "Nvidia", + "doc": "https://docs.api.nvidia.com/nim/", + "models": { + "upstage/solar-10_7b-instruct": { + "id": "upstage/solar-10_7b-instruct", + "name": "solar-10.7b-instruct", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": true, + "tool_call": true, "temperature": true, - "release_date": "2025-02-01", - "last_updated": "2025-02-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-06-05", + "last_updated": "2025-04-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.05, "output": 0.2 }, - "limit": { "context": 1000000, "output": 8192 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "llama-3.1-nemotron-ultra-253b": { - "id": "llama-3.1-nemotron-ultra-253b", - "name": "Llama 3.1 Nemotron Ultra 253B", - "family": "llama", + "black-forest-labs/flux_2-klein-4b": { + "id": "black-forest-labs/flux_2-klein-4b", + "name": "FLUX.2 Klein 4B", + "family": "flux", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": true, "temperature": true, - "release_date": "2025-04-07", - "last_updated": "2025-04-07", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-06", + "release_date": "2026-01-14", + "last_updated": "2026-01-31", + "modalities": { + "input": ["image", "text"], + "output": ["image"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 1.8 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 40960, + "output": 40960 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gemini-2.5-flash-lite-preview-09-2025": { - "id": "gemini-2.5-flash-lite-preview-09-2025", - "name": "Gemini 2.5 Flash Lite Preview (09-2025)", - "family": "gemini", - "attachment": true, + "black-forest-labs/flux.1-dev": { + "id": "black-forest-labs/flux.1-dev", + "name": "FLUX.1-dev", + "family": "flux", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, + "tool_call": false, "temperature": true, - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-08", + "release_date": "2024-08-01", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.01 }, - "limit": { "context": 1048576, "output": 65535 } - }, - "qwen-vl-max": { - "id": "qwen-vl-max", - "name": "Qwen VL Max", - "family": "qwen", - "attachment": true, + "limit": { + "context": 4096, + "output": 0 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "black-forest-labs/flux_1-schnell": { + "id": "black-forest-labs/flux_1-schnell", + "name": "FLUX.1-schnell", + "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": true, - "temperature": true, - "release_date": "2025-02-01", - "last_updated": "2025-02-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": false, + "temperature": false, + "knowledge": "2024-07", + "release_date": "2024-08-01", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["image"] + }, "open_weights": true, - "cost": { "input": 0.8, "output": 3.2 }, - "limit": { "context": 131072, "output": 32000 } + "limit": { + "context": 77, + "input": 77, + "output": 0 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "qwen3-vl-235b-a22b-instruct": { - "id": "qwen3-vl-235b-a22b-instruct", - "name": "Qwen3 VL 235B A22B Instruct", - "family": "qwen", + "black-forest-labs/flux_1-kontext-dev": { + "id": "black-forest-labs/flux_1-kontext-dev", + "name": "FLUX.1-Kontext-dev", "attachment": true, "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2025-08-12", + "last_updated": "2025-08-12", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, + "open_weights": true, + "limit": { + "context": 40960, + "output": 40960 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "stepfun-ai/step-3.5-flash": { + "id": "stepfun-ai/step-3.5-flash", + "name": "Step 3.5 Flash", + "attachment": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-09-23", - "last_updated": "2025-09-23", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-02-02", + "last_updated": "2026-02-02", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.5, "output": 2 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 256000, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "claude-3-opus": { - "id": "claude-3-opus", - "name": "Claude 3 Opus", - "family": "claude", + "mistralai/mistral-large-3-675b-instruct-2512": { + "id": "mistralai/mistral-large-3-675b-instruct-2512", + "name": "Mistral Large 3 675B Instruct 2512", + "family": "mistral-large", "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": false, + "structured_output": true, "temperature": true, - "release_date": "2024-03-04", - "last_updated": "2024-03-04", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5 }, - "limit": { "context": 200000, "output": 4096 } + "knowledge": "2025-01", + "release_date": "2025-12-02", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "kimi-k2-thinking": { - "id": "kimi-k2-thinking", - "name": "Kimi K2 Thinking", - "family": "kimi", - "attachment": false, + "mistralai/devstral-2-123b-instruct-2512": { + "id": "mistralai/devstral-2-123b-instruct-2512", + "name": "Devstral-2-123B-Instruct-2512", + "family": "devstral", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-11-06", - "last_updated": "2025-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.6, "output": 2.5, "cache_read": 0.15 }, - "limit": { "context": 262144, "output": 262144 } + "knowledge": "2025-12", + "release_date": "2025-12-08", + "last_updated": "2025-12-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "qwen3-coder-next": { - "id": "qwen3-coder-next", - "name": "Qwen3 Coder Next", - "family": "qwen", + "mistralai/mistral-nemotron": { + "id": "mistralai/mistral-nemotron", + "name": "mistral-nemotron", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-01-01", - "last_updated": "2024-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-06-11", + "last_updated": "2025-06-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.11, "output": 0.68, "cache_read": 0.06 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "qwen-max-latest": { - "id": "qwen-max-latest", - "name": "Qwen Max Latest", - "family": "qwen", - "attachment": true, + "mistralai/mixtral-8x22b-instruct": { + "id": "mistralai/mixtral-8x22b-instruct", + "name": "Mistral: Mixtral 8x22B Instruct", + "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-01-25", - "last_updated": "2025-01-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2024-04-17", + "last_updated": "2024-04-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1.6, "output": 6.4 }, - "limit": { "context": 131072, "output": 32000 } - }, - "gpt-5.2-chat-latest": { - "id": "gpt-5.2-chat-latest", - "name": "GPT-5.2 Chat", - "family": "gpt", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.18 }, - "limit": { "context": 128000, "output": 16400 } + "limit": { + "context": 65536, + "output": 13108 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "qwen-vl-plus": { - "id": "qwen-vl-plus", - "name": "Qwen VL Plus", - "family": "qwen", + "mistralai/mistral-medium-3-instruct": { + "id": "mistralai/mistral-medium-3-instruct", + "name": "Mistral Medium 3", + "family": "mistral-medium", "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": true, - "temperature": true, - "release_date": "2025-02-05", - "last_updated": "2025-02-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.21, "output": 0.64 }, - "limit": { "context": 131072, "output": 32000 } + "structured_output": false, + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "input": 131072, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "qwen-max": { - "id": "qwen-max", - "name": "Qwen Max", - "family": "qwen", - "attachment": true, + "mistralai/mixtral-8x7b-instruct": { + "id": "mistralai/mixtral-8x7b-instruct", + "name": "Mistral: Mixtral 8x7B Instruct", + "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2023-12-10", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1.6, "output": 6.4 }, - "limit": { "context": 131072, "output": 32000 } + "limit": { + "context": 32768, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "glm-image": { - "id": "glm-image", - "name": "GLM-Image", - "family": "glm", + "mistralai/magistral-small-2506": { + "id": "mistralai/magistral-small-2506", + "name": "Magistral Small 2506", "attachment": false, "reasoning": false, "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-01-14", - "last_updated": "2025-01-14", - "modalities": { "input": ["text"], "output": ["text", "image"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 2000, "output": 4096 } - }, - "gpt-5": { - "id": "gpt-5", - "name": "GPT-5", - "family": "gpt", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-08-01", - "last_updated": "2025-08-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.13 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "llama-3.3-70b-instruct": { - "id": "llama-3.3-70b-instruct", - "name": "Llama 3.3 70B Instruct", - "family": "llama", + "mistralai/mistral-7b-instruct-v03": { + "id": "mistralai/mistral-7b-instruct-v03", + "name": "Mistral-7B-Instruct-v0.3", "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-04-01", + "last_updated": "2025-04-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.13, "output": 0.4 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 65536, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gemma-3-1b-it": { - "id": "gemma-3-1b-it", - "name": "Gemma 3 1B IT", - "family": "gemma", + "mistralai/mistral-small-4-119b-2603": { + "id": "mistralai/mistral-small-4-119b-2603", + "name": "mistral-small-4-119b-2603", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, + "tool_call": true, "temperature": true, - "release_date": "2025-03-12", - "last_updated": "2025-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-16", + "last_updated": "2026-03-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.08, "output": 0.3 }, - "limit": { "context": 1000000, "output": 16384 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "claude-opus-4-20250514": { - "id": "claude-opus-4-20250514", - "name": "Claude Opus 4 (2025-05-14)", - "family": "claude", + "sarvamai/sarvam-m": { + "id": "sarvamai/sarvam-m", + "name": "sarvam-m", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": false, "temperature": true, - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5 }, - "limit": { "context": 200000, "output": 16384 } + "release_date": "2025-07-25", + "last_updated": "2025-07-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gemini-3.1-pro-preview": { - "id": "gemini-3.1-pro-preview", - "name": "Gemini 3.1 Pro (Preview)", - "family": "gemini", + "microsoft/phi-4-mini-instruct": { + "id": "microsoft/phi-4-mini-instruct", + "name": "Phi-4-Mini", + "family": "phi", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-02-19", - "last_updated": "2026-02-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2024-12-01", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 12, "cache_read": 0.2 }, - "limit": { "context": 1048576, "output": 65536 } - }, - "devstral-2512": { - "id": "devstral-2512", - "name": "Devstral 2", - "family": "mistral", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": true, - "temperature": true, - "release_date": "2025-12-09", - "last_updated": "2025-12-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 262144, "output": 16384 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "seedream-4-0": { - "id": "seedream-4-0", - "name": "Seedream 4.0", - "family": "seed", + "microsoft/phi-4-multimodal-instruct": { + "id": "microsoft/phi-4-multimodal-instruct", + "name": "Phi 4 Multimodal", "attachment": false, "reasoning": false, "tool_call": false, "structured_output": false, - "temperature": true, - "release_date": "2025-09-16", - "last_updated": "2025-09-16", - "modalities": { "input": ["text"], "output": ["text", "image"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 2000, "output": 4096 } - }, - "claude-3-7-sonnet-20250219": { - "id": "claude-3-7-sonnet-20250219", - "name": "Claude 3.7 Sonnet (2025-02-19)", - "family": "claude", - "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-07-26", + "last_updated": "2025-07-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3 }, - "limit": { "context": 200000, "output": 8192 } + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "kimi-k2.5": { - "id": "kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi", + "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning": { + "id": "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning", + "name": "Nemotron 3 Nano Omni", + "family": "nemotron", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2026-01-26", - "last_updated": "2026-01-26", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.6, "output": 3, "cache_read": 0.1 }, - "limit": { "context": 262144, "output": 32768 } + "release_date": "2026-04-28", + "last_updated": "2026-04-28", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gemma-3n-e2b-it": { - "id": "gemma-3n-e2b-it", - "name": "Gemma 3n E2B IT", - "family": "gemma", + "nvidia/usdvalidate": { + "id": "nvidia/usdvalidate", + "name": "usdvalidate", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2025-06-26", - "last_updated": "2025-06-26", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2024-07-24", + "last_updated": "2025-01-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.08, "output": 0.3 }, - "limit": { "context": 1000000, "output": 16384 } + "limit": { + "context": 0, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "llama-guard-4-12b": { - "id": "llama-guard-4-12b", - "name": "Llama Guard 4 12B", - "family": "llama", - "attachment": false, + "nvidia/synthetic-video-detector": { + "id": "nvidia/synthetic-video-detector", + "name": "synthetic-video-detector", + "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": false, "temperature": true, - "release_date": "2025-04-30", - "last_updated": "2025-04-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 131072, "output": 16384 } + "limit": { + "context": 0, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gpt-4-turbo": { - "id": "gpt-4-turbo", - "name": "GPT-4 Turbo", - "family": "gpt", + "nvidia/cosmos-transfer1-7b": { + "id": "nvidia/cosmos-transfer1-7b", + "name": "cosmos-transfer1-7b", "attachment": true, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2023-11-06", - "last_updated": "2023-11-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 10, "output": 30 }, - "limit": { "context": 128000, "output": 16384 } - }, - "qwen-image-max": { - "id": "qwen-image-max", - "name": "Qwen Image Max", - "family": "qwen", - "attachment": false, - "reasoning": false, "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2025-08-04", - "last_updated": "2025-08-04", - "modalities": { "input": ["text"], "output": ["text", "image"] }, + "temperature": false, + "release_date": "2025-06-13", + "last_updated": "2025-06-30", + "modalities": { + "input": ["text", "image", "video"], + "output": ["video"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 2000, "output": 4096 } + "limit": { + "context": 0, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "mistral-large-latest": { - "id": "mistral-large-latest", - "name": "Mistral Large Latest", - "family": "mistral", + "nvidia/rerank-qa-mistral-4b": { + "id": "nvidia/rerank-qa-mistral-4b", + "name": "rerank-qa-mistral-4b", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2025-12-02", - "last_updated": "2025-12-02", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2024-03-17", + "last_updated": "2025-01-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 4, "output": 12 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "qwq-plus": { - "id": "qwq-plus", - "name": "QwQ Plus", - "family": "qwen", + "nvidia/nv-embedcode-7b-v1": { + "id": "nvidia/nv-embedcode-7b-v1", + "name": "nv-embedcode-7b-v1", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2025-03-06", - "last_updated": "2025-03-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-03-17", + "last_updated": "2025-05-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.8, "output": 2.4 }, - "limit": { "context": 131072, "output": 8192 } - }, - "gpt-4o": { - "id": "gpt-4o", - "name": "GPT-4o", - "family": "gpt", - "attachment": true, - "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2024-05-13", - "last_updated": "2024-05-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 10, "cache_read": 1.25 }, - "limit": { "context": 128000, "output": 16384 } - }, - "grok-4-fast-non-reasoning": { - "id": "grok-4-fast-non-reasoning", - "name": "Grok 4 Fast Non-Reasoning", - "family": "grok", - "attachment": true, - "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-10-10", - "last_updated": "2025-10-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } + "limit": { + "context": 32768, + "output": 2048 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "glm-4.7-flash": { - "id": "glm-4.7-flash", - "name": "GLM-4.7 Flash", - "family": "glm", + "nvidia/nemotron-3-super-120b-a12b": { + "id": "nvidia/nemotron-3-super-120b-a12b", + "name": "Nemotron 3 Super", + "family": "nemotron", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 200000, "output": 128000 } - }, - "grok-4": { - "id": "grok-4", - "name": "Grok 4", - "family": "grok", - "attachment": true, - "reasoning": false, - "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 256000, "output": 256000 } + "knowledge": "2024-04", + "release_date": "2026-03-11", + "last_updated": "2026-03-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.2, + "output": 0.8 + } }, - "grok-code-fast-1": { - "id": "grok-code-fast-1", - "name": "Grok Code Fast 1", - "family": "grok", + "nvidia/riva-translate-4b-instruct-v1_1": { + "id": "nvidia/riva-translate-4b-instruct-v1_1", + "name": "riva-translate-4b-instruct-v1_1", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-08-28", - "last_updated": "2025-08-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 1.5 }, - "limit": { "context": 256000, "output": 10000 } + "tool_call": false, + "temperature": false, + "release_date": "2025-12-12", + "last_updated": "2025-12-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "grok-4-20-beta-0309-non-reasoning": { - "id": "grok-4-20-beta-0309-non-reasoning", - "name": "Grok 4.20 Beta Non-Reasoning (0309)", - "family": "grok", + "nvidia/nemotron-voicechat": { + "id": "nvidia/nemotron-voicechat", + "name": "nemotron-voicechat", "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-03-09", - "last_updated": "2026-03-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 6, "cache_read": 0.2 }, - "limit": { "context": 2000000, "output": 30000 } + "release_date": "2026-03-16", + "last_updated": "2026-03-16", + "modalities": { + "input": ["text", "audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gpt-5.3-codex": { - "id": "gpt-5.3-codex", - "name": "GPT-5.3 Codex", - "family": "gpt", - "attachment": true, + "nvidia/llama-3_3-nemotron-super-49b-v1_5": { + "id": "nvidia/llama-3_3-nemotron-super-49b-v1_5", + "name": "Llama 3.3 Nemotron Super 49B v1.5", + "family": "nemotron", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-02-24", - "last_updated": "2026-02-24", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2025-07-25", + "last_updated": "2025-07-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "nvidia/usdcode": { + "id": "nvidia/usdcode", + "name": "usdcode", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2026-01-01", + "last_updated": "2026-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.18 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "qwen-plus-latest": { - "id": "qwen-plus-latest", - "name": "Qwen Plus Latest", - "family": "qwen", + "nvidia/llama-3_2-nemoretriever-300m-embed-v1": { + "id": "nvidia/llama-3_2-nemoretriever-300m-embed-v1", + "name": "llama-3_2-nemoretriever-300m-embed-v1", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2024-09-09", - "last_updated": "2024-09-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "temperature": false, + "release_date": "2025-07-24", + "last_updated": "2025-07-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.4, "output": 1.2, "cache_read": 0.08 }, - "limit": { "context": 1000000, "output": 32000 } + "limit": { + "context": 32768, + "output": 2048 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gemma-3-4b-it": { - "id": "gemma-3-4b-it", - "name": "Gemma 3 4B IT", - "family": "gemma", + "nvidia/llama-3_1-nemotron-safety-guard-8b-v3": { + "id": "nvidia/llama-3_1-nemotron-safety-guard-8b-v3", + "name": "llama-3.1-nemotron-safety-guard-8b-v3", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2025-03-10", - "last_updated": "2025-03-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-10-28", + "last_updated": "2025-10-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.08, "output": 0.3 }, - "limit": { "context": 1000000, "output": 16384 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "ministral-8b-2512": { - "id": "ministral-8b-2512", - "name": "Ministral 8B", - "family": "mistral", - "attachment": true, + "nvidia/nv-embed-v1": { + "id": "nvidia/nv-embed-v1", + "name": "nv-embed-v1", + "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": true, - "temperature": true, - "release_date": "2025-12-02", - "last_updated": "2025-12-02", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "release_date": "2024-06-07", + "last_updated": "2025-07-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.15 }, - "limit": { "context": 262144, "output": 16384 } + "limit": { + "context": 32768, + "output": 2048 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "kimi-k2-thinking-turbo": { - "id": "kimi-k2-thinking-turbo", - "name": "Kimi K2 Thinking Turbo", - "family": "kimi", + "nvidia/nemotron-3-nano-30b-a3b": { + "id": "nvidia/nemotron-3-nano-30b-a3b", + "name": "nemotron-3-nano-30b-a3b", + "family": "nemotron", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-11-06", - "last_updated": "2025-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.15, "output": 8, "cache_read": 0.15 }, - "limit": { "context": 262144, "output": 262144 } + "knowledge": "2024-09", + "release_date": "2024-12", + "last_updated": "2024-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "ministral-14b-2512": { - "id": "ministral-14b-2512", - "name": "Ministral 14B", - "family": "mistral", - "attachment": true, + "nvidia/gliner-pii": { + "id": "nvidia/gliner-pii", + "name": "gliner-pii", + "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": true, "temperature": true, - "release_date": "2025-12-02", - "last_updated": "2025-12-02", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-03-03", + "last_updated": "2026-03-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 262144, "output": 16384 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gpt-5-mini": { - "id": "gpt-5-mini", - "name": "GPT-5 Mini", - "family": "gpt", + "nvidia/bevformer": { + "id": "nvidia/bevformer", + "name": "bevformer", "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "release_date": "2025-08-01", - "last_updated": "2025-08-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.03 }, - "limit": { "context": 400000, "output": 128000 } + "release_date": "2025-03-18", + "last_updated": "2025-07-20", + "modalities": { + "input": ["video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "seed-1-6-250615": { - "id": "seed-1-6-250615", - "name": "Seed 1.6 (250615)", - "family": "seed", - "attachment": true, - "reasoning": true, + "nvidia/nemotron-mini-4b-instruct": { + "id": "nvidia/nemotron-mini-4b-instruct", + "name": "nemotron-mini-4b-instruct", + "attachment": false, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-06-25", - "last_updated": "2025-06-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.05 }, - "limit": { "context": 256000, "output": 16384 } + "release_date": "2024-08-21", + "last_updated": "2024-08-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "qwen3-30b-a3b-thinking-2507": { - "id": "qwen3-30b-a3b-thinking-2507", - "name": "Qwen3 30B A3B Thinking 2507", - "family": "qwen", + "nvidia/cosmos-predict1-5b": { + "id": "nvidia/cosmos-predict1-5b", + "name": "cosmos-predict1-5b", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2025-03-18", + "last_updated": "2025-03-18", + "modalities": { + "input": ["text", "image", "video"], + "output": ["video"] + }, + "open_weights": true, + "limit": { + "context": 0, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "nvidia/llama-3_3-nemotron-super-49b-v1": { + "id": "nvidia/llama-3_3-nemotron-super-49b-v1", + "name": "Llama 3.3 Nemotron Super 49B v1", + "family": "nemotron", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-30", - "last_updated": "2025-07-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2025-04-07", + "last_updated": "2025-04-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 262000, "output": 8192 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "kimi-k2": { - "id": "kimi-k2", - "name": "Kimi K2", - "family": "kimi", + "nvidia/nvidia-nemotron-nano-9b-v2": { + "id": "nvidia/nvidia-nemotron-nano-9b-v2", + "name": "nvidia-nemotron-nano-9b-v2", + "family": "nemotron", "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-07-11", - "last_updated": "2025-07-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1, "output": 3, "cache_read": 0.5 }, - "limit": { "context": 131072, "output": 16384 } - }, - "claude-opus-4-6": { - "id": "claude-opus-4-6", - "name": "Claude Opus 4.6", - "family": "claude", - "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5 }, - "limit": { "context": 1000000, "output": 128000 } + "knowledge": "2024-09", + "release_date": "2025-08-18", + "last_updated": "2025-08-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "qwen-image-edit-plus": { - "id": "qwen-image-edit-plus", - "name": "Qwen Image Edit Plus", - "family": "qwen", + "nvidia/cosmos-transfer2_5-2b": { + "id": "nvidia/cosmos-transfer2_5-2b", + "name": "cosmos-transfer2.5-2b", "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2025-08-19", - "last_updated": "2025-08-19", - "modalities": { "input": ["text", "image"], "output": ["text", "image"] }, + "temperature": false, + "release_date": "2026-02-26", + "last_updated": "2026-02-26", + "modalities": { + "input": ["text", "image", "video"], + "output": ["video"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 2000, "output": 4096 } + "limit": { + "context": 0, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "claude-sonnet-4-5-20250929": { - "id": "claude-sonnet-4-5-20250929", - "name": "Claude Sonnet 4.5 (2025-09-29)", - "family": "claude", + "nvidia/nemotron-content-safety-reasoning-4b": { + "id": "nvidia/nemotron-content-safety-reasoning-4b", + "name": "nemotron-content-safety-reasoning-4b", "attachment": false, "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3 }, - "limit": { "context": 200000, "output": 64000 } + "tool_call": false, + "temperature": false, + "release_date": "2026-01-22", + "last_updated": "2026-01-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gpt-4o-mini": { - "id": "gpt-4o-mini", - "name": "GPT-4o Mini", - "family": "gpt", - "attachment": false, + "nvidia/active-speaker-detection": { + "id": "nvidia/active-speaker-detection", + "name": "Active Speaker Detection", + "attachment": true, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.6, "cache_read": 0.08 }, - "limit": { "context": 128000, "output": 16384 } + "tool_call": false, + "temperature": false, + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 0, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "mistral-small-2506": { - "id": "mistral-small-2506", - "name": "Mistral Small 3.2", - "family": "mistral", + "nvidia/magpie-tts-zeroshot": { + "id": "nvidia/magpie-tts-zeroshot", + "name": "magpie-tts-zeroshot", "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": true, - "temperature": true, - "release_date": "2025-06-20", - "last_updated": "2025-06-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-05-22", + "last_updated": "2025-06-12", + "modalities": { + "input": ["text", "audio"], + "output": ["audio"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 0, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "qwen35-397b-a17b": { - "id": "qwen35-397b-a17b", - "name": "Qwen3.5 397B A17B", - "family": "qwen", + "nvidia/streampetr": { + "id": "nvidia/streampetr", + "name": "streampetr", "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "release_date": "2026-02-16", - "last_updated": "2026-02-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 3.6 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gemma-3n-e4b-it": { - "id": "gemma-3n-e4b-it", - "name": "Gemma 3n E4B IT", - "family": "gemma", - "attachment": false, + "nvidia/sparsedrive": { + "id": "nvidia/sparsedrive", + "name": "sparsedrive", + "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": false, "temperature": true, - "release_date": "2025-06-26", - "last_updated": "2025-06-26", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-03-18", + "last_updated": "2025-07-20", + "modalities": { + "input": ["video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.08, "output": 0.3 }, - "limit": { "context": 1000000, "output": 16384 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "qwen-plus": { - "id": "qwen-plus", - "name": "Qwen Plus", - "family": "qwen", + "nvidia/nemotron-3-content-safety": { + "id": "nvidia/nemotron-3-content-safety", + "name": "nemotron-3-content-safety", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-01-25", - "last_updated": "2025-01-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "temperature": false, + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.4, "output": 1.2, "cache_read": 0.08 }, - "limit": { "context": 131072, "output": 32000 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "codestral-2508": { - "id": "codestral-2508", - "name": "Codestral", - "family": "mistral", + "nvidia/llama-nemotron-embed-vl-1b-v2": { + "id": "nvidia/llama-nemotron-embed-vl-1b-v2", + "name": "llama-nemotron-embed-vl-1b-v2", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2026-02-10", + "last_updated": "2026-02-10", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 2048 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "nvidia/studiovoice": { + "id": "nvidia/studiovoice", + "name": "studiovoice", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": true, "temperature": true, - "release_date": "2025-07-30", - "last_updated": "2025-07-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-10-03", + "last_updated": "2025-06-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 0.9 }, - "limit": { "context": 256000, "output": 16384 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "claude-sonnet-4-6": { - "id": "claude-sonnet-4-6", - "name": "Claude Sonnet 4.6", - "family": "claude", + "nvidia/llama-nemotron-rerank-vl-1b-v2": { + "id": "nvidia/llama-nemotron-rerank-vl-1b-v2", + "name": "llama-nemotron-rerank-vl-1b-v2", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2026-03-31", + "last_updated": "2026-03-31", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "deepseek-ai/deepseek-v3.2": { + "id": "deepseek-ai/deepseek-v3.2", + "name": "DeepSeek V3.2", + "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-02-17", - "last_updated": "2026-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-07", + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 163840, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gpt-4.1": { - "id": "gpt-4.1", - "name": "GPT-4.1", - "family": "gpt", - "attachment": true, - "reasoning": false, + "deepseek-ai/deepseek-v3.1-terminus": { + "id": "deepseek-ai/deepseek-v3.1-terminus", + "name": "DeepSeek V3.1 Terminus", + "family": "deepseek", + "attachment": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-09-22", + "last_updated": "2025-09-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 1000000, "output": 16384 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "llama-3.1-8b-instruct": { - "id": "llama-3.1-8b-instruct", - "name": "Llama 3.1 8B Instruct", - "family": "llama", + "openai/whisper-large-v3": { + "id": "openai/whisper-large-v3", + "name": "Whisper Large v3", + "family": "whisper", "attachment": false, "reasoning": false, + "tool_call": false, + "temperature": false, + "knowledge": "2023-09", + "release_date": "2023-09-01", + "last_updated": "2025-09-05", + "modalities": { + "input": ["audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 0, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "openai/gpt-oss-20b": { + "id": "openai/gpt-oss-20b", + "name": "GPT OSS 20B", + "family": "gpt-oss", + "attachment": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.22, "output": 0.22 }, - "limit": { "context": 128000, "output": 2048 }, - "status": "beta" + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "qwen-image-plus": { - "id": "qwen-image-plus", - "name": "Qwen Image Plus", - "family": "qwen", - "attachment": false, - "reasoning": false, + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "name": "GPT-OSS-120B", + "family": "gpt-oss", + "attachment": true, + "reasoning": true, "tool_call": false, - "structured_output": false, "temperature": true, + "knowledge": "2025-08", "release_date": "2025-08-04", - "last_updated": "2025-08-04", - "modalities": { "input": ["text"], "output": ["text", "image"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 2000, "output": 4096 } + "last_updated": "2025-08-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "glm-4.5-airx": { - "id": "glm-4.5-airx", - "name": "GLM-4.5 AirX", - "family": "glm", + "minimaxai/minimax-m2.7": { + "id": "minimaxai/minimax-m2.7", + "name": "MiniMax-M2.7", + "family": "minimax", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.1, "output": 4.5, "cache_read": 0.22 }, - "limit": { "context": 128000, "output": 16384 } + "release_date": "2026-03-18", + "last_updated": "2026-04-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "minimax-m2": { - "id": "minimax-m2", - "name": "MiniMax M2", + "minimaxai/minimax-m2.5": { + "id": "minimaxai/minimax-m2.5", + "name": "MiniMax-M2.5", "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-27", - "last_updated": "2025-10-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-08", + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 1, "cache_read": 0.03 }, - "limit": { "context": 196608, "output": 131072 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "seed-1-6-flash-250715": { - "id": "seed-1-6-flash-250715", - "name": "Seed 1.6 Flash (250715)", - "family": "seed", - "attachment": true, + "z-ai/glm4.7": { + "id": "z-ai/glm4.7", + "name": "GLM-4.7", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-07-26", - "last_updated": "2025-07-26", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.07, "output": 0.3, "cache_read": 0.02 }, - "limit": { "context": 256000, "output": 16384 } + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gpt-3.5-turbo": { - "id": "gpt-3.5-turbo", - "name": "GPT-3.5 Turbo", - "family": "gpt", + "z-ai/glm-5.1": { + "id": "z-ai/glm-5.1", + "name": "GLM-5.1", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "release_date": "2022-11-30", - "last_updated": "2022-11-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 1.5 }, - "limit": { "context": 16385, "output": 16384 } + "release_date": "2026-03-27", + "last_updated": "2026-03-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "qwen3-vl-flash": { - "id": "qwen3-vl-flash", - "name": "Qwen3 VL Flash", - "family": "qwen", - "attachment": true, + "meta/esm2-650m": { + "id": "meta/esm2-650m", + "name": "esm2-650m", + "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, + "tool_call": false, "temperature": true, - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2024-08-29", + "last_updated": "2025-03-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.05, "output": 0.4, "cache_read": 0.01 }, - "limit": { "context": 262144, "output": 32768 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "sonar": { - "id": "sonar", - "name": "Sonar", - "family": "sonar", + "meta/llama-3.3-70b-instruct": { + "id": "meta/llama-3.3-70b-instruct", + "name": "Llama 3.3 70b Instruct", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1, "output": 1 }, - "limit": { "context": 130000, "output": 16384 } + "release_date": "2024-11-26", + "last_updated": "2024-11-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "qwen3-30b-a3b-fp8": { - "id": "qwen3-30b-a3b-fp8", - "name": "Qwen3 30B A3B FP8", - "family": "qwen", + "meta/esmfold": { + "id": "meta/esmfold", + "name": "esmfold", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, "temperature": true, - "release_date": "2025-04-28", - "last_updated": "2025-04-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-03-15", + "last_updated": "2025-06-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "meta/llama-3.2-90b-vision-instruct": { + "id": "meta/llama-3.2-90b-vision-instruct", + "name": "Llama-3.2-90B-Vision-Instruct", + "family": "llama", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2024-09-25", + "last_updated": "2024-09-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.09, "output": 0.45 }, - "limit": { "context": 40960, "output": 20000 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gpt-oss-120b": { - "id": "gpt-oss-120b", - "name": "GPT OSS 120B", - "family": "gpt-oss", - "attachment": false, - "reasoning": true, + "meta/llama-3.2-11b-vision-instruct": { + "id": "meta/llama-3.2-11b-vision-instruct", + "name": "Llama 3.2 11b Vision Instruct", + "attachment": true, + "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.75 }, - "limit": { "context": 131072, "output": 32766 } + "knowledge": "2023-12", + "release_date": "2024-09-18", + "last_updated": "2024-09-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "minimax-text-01": { - "id": "minimax-text-01", - "name": "MiniMax Text 01", - "family": "minimax", - "attachment": false, + "meta/llama-guard-4-12b": { + "id": "meta/llama-guard-4-12b", + "name": "Llama Guard 4 12B", + "family": "llama", + "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": false, "temperature": true, - "release_date": "2025-01-15", - "last_updated": "2025-01-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-04-05", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 1.1 }, - "limit": { "context": 1000000, "output": 131072 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "llama-4-scout": { - "id": "llama-4-scout", - "name": "Llama 4 Scout", - "family": "llama", + "meta/llama-3.1-70b-instruct": { + "id": "meta/llama-3.1-70b-instruct", + "name": "Llama 3.1 70b Instruct", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": false, + "structured_output": true, "temperature": true, - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-07-16", + "last_updated": "2024-07-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.18, "output": 0.59 }, - "limit": { "context": 32768, "output": 16384 }, - "status": "beta" + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gemini-2.0-flash-lite": { - "id": "gemini-2.0-flash-lite", - "name": "Gemini 2.0 Flash Lite", - "family": "gemini", + "meta/llama-3.2-1b-instruct": { + "id": "meta/llama-3.2-1b-instruct", + "name": "Llama 3.2 1b Instruct", "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-02-25", - "last_updated": "2025-02-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.08, "output": 0.3 }, - "limit": { "context": 1048576, "output": 8192 }, - "status": "deprecated" + "knowledge": "2023-12", + "release_date": "2024-09-18", + "last_updated": "2024-09-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gpt-5.4": { - "id": "gpt-5.4", - "name": "GPT-5.4", - "family": "gpt", + "meta/llama-4-maverick-17b-128e-instruct": { + "id": "meta/llama-4-maverick-17b-128e-instruct", + "name": "Llama 4 Maverick 17b 128e Instruct", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2026-03-06", - "last_updated": "2026-03-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 15, "cache_read": 0.25 }, - "limit": { "context": 1050000, "output": 128000 } + "knowledge": "2024-02", + "release_date": "2025-04-01", + "last_updated": "2025-04-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "o1": { - "id": "o1", - "name": "o1", - "family": "gpt", - "attachment": true, - "reasoning": true, + "meta/llama-3.2-3b-instruct": { + "id": "meta/llama-3.2-3b-instruct", + "name": "Llama 3.2 3B Instruct", + "family": "llama", + "attachment": false, + "reasoning": false, "tool_call": false, "structured_output": true, "temperature": true, - "release_date": "2024-09-12", - "last_updated": "2024-09-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 60, "cache_read": 7.5 }, - "limit": { "context": 200000, "output": 16384 } + "release_date": "2024-09-18", + "last_updated": "2024-09-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 32000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "o3": { - "id": "o3", - "name": "o3", - "family": "gpt", + "meta/llama-3.1-8b-instruct": { + "id": "meta/llama-3.1-8b-instruct", + "name": "Llama 3.1 8B Instruct", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 16000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "qwen/qwen-image-edit": { + "id": "qwen/qwen-image-edit", + "name": "Qwen Image Edit", + "family": "qwen", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": false, - "structured_output": true, + "structured_output": false, "temperature": true, - "release_date": "2025-06-01", - "last_updated": "2025-06-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-19", + "last_updated": "2025-08-19", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 200000, "output": 16384 } + "limit": { + "context": 0, + "output": 0 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "qwen2-5-vl-32b-instruct": { - "id": "qwen2-5-vl-32b-instruct", - "name": "Qwen2.5 VL 32B Instruct", + "qwen/qwen3.5-122b-a10b": { + "id": "qwen/qwen3.5-122b-a10b", + "name": "Qwen3.5 122B-A10B", "family": "qwen", "attachment": true, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-02-23", + "last_updated": "2026-02-23", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1.4, "output": 4.2 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "llama-3-8b-instruct": { - "id": "llama-3-8b-instruct", - "name": "Llama 3 8B Instruct", - "family": "llama", + "qwen/qwen2.5-coder-32b-instruct": { + "id": "qwen/qwen2.5-coder-32b-instruct", + "name": "Qwen2.5 Coder 32b Instruct", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-11-06", + "last_updated": "2024-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.04, "output": 0.04 }, - "limit": { "context": 8192, "output": 8192 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "grok-4-1-fast-non-reasoning": { - "id": "grok-4-1-fast-non-reasoning", - "name": "Grok 4.1 Fast Non-Reasoning", - "family": "grok", + "qwen/qwen3.5-397b-a17b": { + "id": "qwen/qwen3.5-397b-a17b", + "name": "Qwen3.5-397B-A17B", + "family": "qwen", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-11-19", - "last_updated": "2025-11-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } + "knowledge": "2026-01", + "release_date": "2026-02-16", + "last_updated": "2026-02-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "llama-3-70b-instruct": { - "id": "llama-3-70b-instruct", - "name": "Llama 3 70B Instruct", - "family": "llama", + "qwen/qwen3-next-80b-a3b-thinking": { + "id": "qwen/qwen3-next-80b-a3b-thinking", + "name": "Qwen3-Next-80B-A3B-Thinking", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": true, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2024-04-18", - "last_updated": "2024-04-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2024-12-01", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.51, "output": 0.74 }, - "limit": { "context": 8192, "output": 8000 } + "limit": { + "context": 262144, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gpt-5.3-chat-latest": { - "id": "gpt-5.3-chat-latest", - "name": "GPT-5.3 Chat", - "family": "gpt", - "attachment": true, - "reasoning": true, + "qwen/qwen3-next-80b-a3b-instruct": { + "id": "qwen/qwen3-next-80b-a3b-instruct", + "name": "Qwen3-Next-80B-A3B-Instruct", + "family": "qwen", + "attachment": false, + "reasoning": false, "tool_call": true, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2024-12-01", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "qwen/qwen-image": { + "id": "qwen/qwen-image", + "name": "Qwen Image", + "family": "qwen", + "attachment": true, + "reasoning": false, + "tool_call": false, "structured_output": false, "temperature": true, - "release_date": "2026-03-03", - "last_updated": "2026-03-03", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.18 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 0, + "output": 0 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "glm-4.5": { - "id": "glm-4.5", - "name": "GLM-4.5", - "family": "glm", + "qwen/qwen3-coder-480b-a35b-instruct": { + "id": "qwen/qwen3-coder-480b-a35b-instruct", + "name": "Qwen3 Coder 480B A35B Instruct", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 2.2, "cache_read": 0.11 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 262144, + "output": 66536 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "minimax-m2.1": { - "id": "minimax-m2.1", - "name": "MiniMax M2.1", - "family": "minimax", + "google/gemma-2-2b-it": { + "id": "google/gemma-2-2b-it", + "name": "Gemma 2 2b It", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-07-16", + "last_updated": "2024-07-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.27, "output": 1.1 }, - "limit": { "context": 196608, "output": 131072 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "glm-4.7-flashx": { - "id": "glm-4.7-flashx", - "name": "GLM-4.7 FlashX", - "family": "glm", - "attachment": false, - "reasoning": true, + "google/gemma-3n-e4b-it": { + "id": "google/gemma-3n-e4b-it", + "name": "Gemma 3n E4b It", + "attachment": true, + "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.07, "output": 0.4, "cache_read": 0.01 }, - "limit": { "context": 200000, "output": 128000 } - }, - "sonar-reasoning-pro": { - "id": "sonar-reasoning-pro", - "name": "Sonar Reasoning Pro", - "family": "sonar", - "attachment": false, - "reasoning": true, - "tool_call": false, - "structured_output": true, - "temperature": true, - "release_date": "2025-03-07", - "last_updated": "2025-03-07", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 8 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2024-06", + "release_date": "2025-06-03", + "last_updated": "2025-06-03", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gemini-pro-latest": { - "id": "gemini-pro-latest", - "name": "Gemini Pro Latest", - "family": "gemini", + "google/gemma-4-31b-it": { + "id": "google/gemma-4-31b-it", + "name": "Gemma-4-31B-IT", + "family": "gemma", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-02-27", - "last_updated": "2026-02-27", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 12, "cache_read": 0.2 }, - "limit": { "context": 1048576, "output": 65536 } + "knowledge": "2025-01", + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "veo-3.1-fast-generate-preview": { - "id": "veo-3.1-fast-generate-preview", - "name": "Veo 3.1 Fast", - "family": "gemini", - "attachment": false, + "google/gemma-3n-e2b-it": { + "id": "google/gemma-3n-e2b-it", + "name": "Gemma 3n E2b It", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-03-14", - "last_updated": "2026-03-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 32768, "output": 1 }, - "status": "beta" + "knowledge": "2024-06", + "release_date": "2025-06-12", + "last_updated": "2025-06-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "claude-3-haiku-20240307": { - "id": "claude-3-haiku-20240307", - "name": "Claude 3 Haiku (2024-03-07)", - "family": "claude", + "google/gemma-3-27b-it": { + "id": "google/gemma-3-27b-it", + "name": "Gemma-3-27B-IT", + "family": "gemma", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": false, "temperature": true, - "release_date": "2024-03-04", - "last_updated": "2024-03-04", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2024-12-01", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 1.25, "cache_read": 0.03 }, - "limit": { "context": 200000, "output": 4096 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gpt-4o-search-preview": { - "id": "gpt-4o-search-preview", - "name": "GPT-4o Search Preview", - "family": "gpt", + "google/google-paligemma": { + "id": "google/google-paligemma", + "name": "paligemma", "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": false, "temperature": true, - "release_date": "2024-10-01", - "last_updated": "2024-10-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2024-05-14", + "last_updated": "2024-08-26", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "moonshotai/kimi-k2-instruct": { + "id": "moonshotai/kimi-k2-instruct", + "name": "Kimi K2 Instruct", + "family": "kimi", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-01", + "release_date": "2025-01-01", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "llama-4-scout-17b-instruct": { - "id": "llama-4-scout-17b-instruct", - "name": "Llama 4 Scout 17B Instruct", - "family": "llama", + "moonshotai/kimi-k2.6": { + "id": "moonshotai/kimi-k2.6", + "name": "Kimi K2.6", + "family": "kimi-k2.6", "attachment": true, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.17, "output": 0.66 }, - "limit": { "context": 8192, "output": 2048 }, - "status": "beta" + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "qwen3-235b-a22b-fp8": { - "id": "qwen3-235b-a22b-fp8", - "name": "Qwen3 235B A22B FP8", - "family": "qwen", + "moonshotai/kimi-k2-instruct-0905": { + "id": "moonshotai/kimi-k2-instruct-0905", + "name": "Kimi K2 0905", + "family": "kimi", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": true, + "tool_call": true, "temperature": true, - "release_date": "2025-04-28", - "last_updated": "2025-04-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 40960, "output": 20000 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "claude-sonnet-4-20250514": { - "id": "claude-sonnet-4-20250514", - "name": "Claude Sonnet 4 (2025-05-14)", - "family": "claude", + "moonshotai/kimi-k2-thinking": { + "id": "moonshotai/kimi-k2-thinking", + "name": "Kimi K2 Thinking", + "family": "kimi-thinking", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": false, + "interleaved": true, + "structured_output": true, "temperature": true, - "release_date": "2025-05-14", - "last_updated": "2025-05-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3 }, - "limit": { "context": 200000, "output": 16384 } + "knowledge": "2025-07", + "release_date": "2025-11", + "last_updated": "2025-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "grok-4-0709": { - "id": "grok-4-0709", - "name": "Grok 4 (0709)", - "family": "grok", + "abacusai/dracarys-llama-3_1-70b-instruct": { + "id": "abacusai/dracarys-llama-3_1-70b-instruct", + "name": "dracarys-llama-3.1-70b-instruct", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 256000, "output": 256000 } + "release_date": "2024-09-11", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "glm-4.6v-flashx": { - "id": "glm-4.6v-flashx", - "name": "GLM-4.6V FlashX", - "family": "glm", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-12-08", - "last_updated": "2025-12-08", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.04, "output": 0.4, "cache_read": 0 }, - "limit": { "context": 128000, "output": 16000 } + "baai/bge-m3": { + "id": "baai/bge-m3", + "name": "BGE M3", + "family": "bge", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2024-01-30", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 1024 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "glm-4.6": { - "id": "glm-4.6", - "name": "GLM-4.6", - "family": "glm", + "bytedance/seed-oss-36b-instruct": { + "id": "bytedance/seed-oss-36b-instruct", + "name": "ByteDance-Seed/Seed-OSS-36B-Instruct", + "family": "seed", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-09-04", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 2.2, "cache_read": 0.11 }, - "limit": { "context": 200000, "output": 16384 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gemini-3-flash-preview": { - "id": "gemini-3-flash-preview", - "name": "Gemini 3 Flash (Preview)", - "family": "gemini", - "attachment": true, + "deepseek-ai/deepseek-v4-flash": { + "id": "deepseek-ai/deepseek-v4-flash", + "name": "DeepSeek V4 Flash", + "family": "deepseek-flash", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 3, "cache_read": 0.05 }, - "limit": { "context": 1048576, "output": 65535 } + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 393216 + }, + "cost": { + "input": 0.14, + "output": 0.28, + "cache_read": 0.028 + } }, - "gpt-5.4-nano": { - "id": "gpt-5.4-nano", - "name": "GPT-5.4 Nano", - "family": "gpt", - "attachment": true, + "deepseek-ai/deepseek-v4-pro": { + "id": "deepseek-ai/deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "release_date": "2026-03-17", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 1.25, "cache_read": 0.02 }, - "limit": { "context": 400000, "output": 128000 } - }, - "claude-3-5-sonnet": { - "id": "claude-3-5-sonnet", - "name": "Claude 3.5 Sonnet", - "family": "claude", + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 393216 + }, + "cost": { + "input": 1.74, + "output": 3.48, + "cache_read": 0.145 + } + } + } + }, + "inference": { + "id": "inference", + "env": ["INFERENCE_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://inference.net/v1", + "name": "Inference", + "doc": "https://inference.net/models", + "models": { + "mistral/mistral-nemo-12b-instruct": { + "id": "mistral/mistral-nemo-12b-instruct", + "name": "Mistral Nemo 12B Instruct", + "family": "mistral-nemo", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2024-06-20", - "last_updated": "2024-06-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3 }, - "limit": { "context": 200000, "output": 16384 } - }, - "glm-4.6v": { - "id": "glm-4.6v", - "name": "GLM-4.6V", - "family": "glm", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-12-08", - "last_updated": "2025-12-08", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 0.9, "cache_read": 0.05 }, - "limit": { "context": 128000, "output": 16000 } + "knowledge": "2024-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 16000, + "output": 4096 + }, + "cost": { + "input": 0.038, + "output": 0.1 + } }, - "qwen3-vl-8b-instruct": { - "id": "qwen3-vl-8b-instruct", - "name": "Qwen3 VL 8B Instruct", - "family": "qwen", + "meta/llama-3.2-11b-vision-instruct": { + "id": "meta/llama-3.2-11b-vision-instruct", + "name": "Llama 3.2 11B Vision Instruct", + "family": "llama", "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": true, + "tool_call": true, "temperature": true, - "release_date": "2025-10-14", - "last_updated": "2025-10-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.08, "output": 0.5 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 16000, + "output": 4096 + }, + "cost": { + "input": 0.055, + "output": 0.055 + } }, - "gpt-4": { - "id": "gpt-4", - "name": "GPT-4", - "family": "gpt", + "meta/llama-3.2-1b-instruct": { + "id": "meta/llama-3.2-1b-instruct", + "name": "Llama 3.2 1B Instruct", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": false, "temperature": true, - "release_date": "2023-03-14", - "last_updated": "2023-03-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 30, "output": 60 }, - "limit": { "context": 8192, "output": 8192 } + "knowledge": "2023-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 16000, + "output": 4096 + }, + "cost": { + "input": 0.01, + "output": 0.01 + } }, - "minimax-m2.7": { - "id": "minimax-m2.7", - "name": "MiniMax M2.7", - "family": "minimax", + "meta/llama-3.2-3b-instruct": { + "id": "meta/llama-3.2-3b-instruct", + "name": "Llama 3.2 3B Instruct", + "family": "llama", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-01-01", - "last_updated": "2024-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.06 }, - "limit": { "context": 204800, "output": 131100 } + "limit": { + "context": 16000, + "output": 4096 + }, + "cost": { + "input": 0.02, + "output": 0.02 + } }, - "qwen3-coder-480b-a35b-instruct": { - "id": "qwen3-coder-480b-a35b-instruct", - "name": "Qwen3 Coder 480B A35B Instruct", - "family": "qwen", + "meta/llama-3.1-8b-instruct": { + "id": "meta/llama-3.1-8b-instruct", + "name": "Llama 3.1 8B Instruct", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-01-31", - "last_updated": "2025-01-31", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.4, "output": 1.8 }, - "limit": { "context": 262000, "output": 8192 } + "limit": { + "context": 16000, + "output": 4096 + }, + "cost": { + "input": 0.025, + "output": 0.025 + } }, - "auto": { - "id": "auto", - "name": "Auto Route", - "family": "auto", + "qwen/qwen-2.5-7b-vision-instruct": { + "id": "qwen/qwen-2.5-7b-vision-instruct", + "name": "Qwen 2.5 7B Vision Instruct", + "family": "qwen", "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-01-01", - "last_updated": "2024-01-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2024-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 125000, + "output": 4096 + }, + "cost": { + "input": 0.2, + "output": 0.2 + } }, - "qwen3-next-80b-a3b-thinking": { - "id": "qwen3-next-80b-a3b-thinking", - "name": "Qwen3 Next 80B A3B Thinking", + "qwen/qwen3-embedding-4b": { + "id": "qwen/qwen3-embedding-4b", + "name": "Qwen 3 Embedding 4B", "family": "qwen", "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2025-09-10", - "last_updated": "2025-09-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "temperature": false, + "knowledge": "2024-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.5, "output": 6 }, - "limit": { "context": 131072, "output": 32768 }, - "status": "beta" + "limit": { + "context": 32000, + "output": 2048 + }, + "cost": { + "input": 0.01, + "output": 0 + } }, - "qwen3-vl-30b-a3b-instruct": { - "id": "qwen3-vl-30b-a3b-instruct", - "name": "Qwen3 VL 30B A3B Instruct", - "family": "qwen", + "google/gemma-3": { + "id": "google/gemma-3", + "name": "Google Gemma 3", + "family": "gemma", "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": false, "temperature": true, - "release_date": "2025-10-05", - "last_updated": "2025-10-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 0.7 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 125000, + "output": 4096 + }, + "cost": { + "input": 0.15, + "output": 0.3 + } }, - "gemini-2.5-pro": { - "id": "gemini-2.5-pro", - "name": "Gemini 2.5 Pro", - "family": "gemini", - "attachment": true, - "reasoning": true, + "osmosis/osmosis-structure-0.6b": { + "id": "osmosis/osmosis-structure-0.6b", + "name": "Osmosis Structure 0.6B", + "family": "osmosis", + "attachment": false, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-03-25", - "last_updated": "2025-03-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 4000, + "output": 2048 + }, + "cost": { + "input": 0.1, + "output": 0.5 + } + } + } + }, + "inception": { + "id": "inception", + "env": ["INCEPTION_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.inceptionlabs.ai/v1/", + "name": "Inception", + "doc": "https://platform.inceptionlabs.ai/docs", + "models": { + "mercury-edit-2": { + "id": "mercury-edit-2", + "name": "Mercury Edit 2", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2026-03-30", + "last_updated": "2026-03-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.13 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.25, + "output": 0.75, + "cache_read": 0.025 + } }, - "claude-opus-4-1-20250805": { - "id": "claude-opus-4-1-20250805", - "name": "Claude Opus 4.1", - "family": "claude", - "attachment": true, + "mercury-2": { + "id": "mercury-2", + "name": "Mercury 2", + "family": "mercury", + "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-01-01", + "release_date": "2026-02-24", + "last_updated": "2026-02-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5 }, - "limit": { "context": 200000, "output": 32000 } - }, - "gemini-2.5-flash": { - "id": "gemini-2.5-flash", - "name": "Gemini 2.5 Flash", - "family": "gemini", + "limit": { + "context": 128000, + "output": 50000 + }, + "cost": { + "input": 0.25, + "output": 0.75, + "cache_read": 0.025 + } + } + } + }, + "openai": { + "id": "openai", + "env": ["OPENAI_API_KEY"], + "npm": "@ai-sdk/openai", + "name": "OpenAI", + "doc": "https://platform.openai.com/docs/models", + "models": { + "gpt-5.1-codex-max": { + "id": "gpt-5.1-codex-max", + "name": "GPT-5.1 Codex Max", + "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-08-26", - "last_updated": "2025-08-26", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 2.5, "cache_read": 0.03 }, - "limit": { "context": 1048576, "output": 65535 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "sonar-pro": { - "id": "sonar-pro", - "name": "Sonar Pro", - "family": "sonar", - "attachment": false, + "gpt-4o-2024-05-13": { + "id": "gpt-4o-2024-05-13", + "name": "GPT-4o (2024-05-13)", + "family": "gpt", + "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-03-07", - "last_updated": "2025-03-07", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-09", + "release_date": "2024-05-13", + "last_updated": "2024-05-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 200000, "output": 16384 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 5, + "output": 15 + } }, - "qwen3-235b-a22b-thinking-2507": { - "id": "qwen3-235b-a22b-thinking-2507", - "name": "Qwen3 235B A22B Thinking 2507", - "family": "qwen", + "o1-mini": { + "id": "o1-mini", + "name": "o1-mini", + "family": "o-mini", "attachment": false, "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-07-25", - "last_updated": "2025-07-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.6 }, - "limit": { "context": 262000, "output": 8192 }, - "status": "beta" - }, - "qwen-image-edit-max": { - "id": "qwen-image-edit-max", - "name": "Qwen Image Edit Max", - "family": "qwen", - "attachment": true, - "reasoning": false, "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2026-01-16", - "last_updated": "2026-01-16", - "modalities": { "input": ["text", "image"], "output": ["text", "image"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 2000, "output": 4096 } - }, - "glm-4-32b-0414-128k": { - "id": "glm-4-32b-0414-128k", - "name": "GLM-4 32B (0414-128k)", - "family": "glm", - "attachment": false, - "reasoning": false, - "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2023-09", + "release_date": "2024-09-12", + "last_updated": "2024-09-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.1 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "output": 65536 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.55 + } }, - "gpt-5.2": { - "id": "gpt-5.2", - "name": "GPT-5.2", - "family": "gpt", + "gpt-5.2-pro": { + "id": "gpt-5.2-pro", + "name": "GPT-5.2 Pro", + "family": "gpt-pro", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, + "structured_output": false, + "temperature": false, + "knowledge": "2025-08-31", "release_date": "2025-12-11", "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.18 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 21, + "output": 168 + } }, - "qwen3-next-80b-a3b-instruct": { - "id": "qwen3-next-80b-a3b-instruct", - "name": "Qwen3 Next 80B A3B Instruct", - "family": "qwen", + "text-embedding-3-large": { + "id": "text-embedding-3-large", + "name": "text-embedding-3-large", + "family": "text-embedding", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-09-10", - "last_updated": "2025-09-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.5, "output": 2 }, - "limit": { "context": 129024, "output": 32768 } + "tool_call": false, + "temperature": false, + "knowledge": "2024-01", + "release_date": "2024-01-25", + "last_updated": "2024-01-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8191, + "output": 3072 + }, + "cost": { + "input": 0.13, + "output": 0 + } }, - "grok-4-1-fast-reasoning": { - "id": "grok-4-1-fast-reasoning", - "name": "Grok 4.1 Fast Reasoning", - "family": "grok", + "gpt-5.3-chat-latest": { + "id": "gpt-5.3-chat-latest", + "name": "GPT-5.3 Chat (latest)", + "family": "gpt", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-11-19", - "last_updated": "2025-11-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-03-03", + "last_updated": "2026-03-03", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "gpt-5.1": { - "id": "gpt-5.1", - "name": "GPT-5.1", + "gpt-5.5": { + "id": "gpt-5.5", + "name": "GPT-5.5", "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-11-01", - "last_updated": "2025-11-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.13 }, - "limit": { "context": 400000, "output": 128000 } - }, - "gpt-4o-mini-search-preview": { - "id": "gpt-4o-mini-search-preview", - "name": "GPT-4o Mini Search Preview", - "family": "gpt", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2024-10-01", - "last_updated": "2024-10-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-12-01", + "release_date": "2026-04-23", + "last_updated": "2026-04-23", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 128000, "output": 16384 } - }, - "qwen3-vl-plus": { - "id": "qwen3-vl-plus", - "name": "Qwen3 VL Plus", - "family": "qwen", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": true, - "temperature": true, - "release_date": "2025-09-23", - "last_updated": "2025-09-23", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 1.6, "cache_read": 0.04 }, - "limit": { "context": 262144, "output": 32768 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "experimental": { + "modes": { + "fast": { + "cost": { + "input": 12.5, + "output": 75, + "cache_read": 1.25 + }, + "provider": { + "body": { + "service_tier": "priority" + } + } + } + } + }, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5, + "tiers": [ + { + "input": 10, + "output": 45, + "cache_read": 1, + "tier": { + "type": "context", + "size": 272000 + } + } + ], + "context_over_200k": { + "input": 10, + "output": 45, + "cache_read": 1 + } + } }, - "deepseek-v3.1": { - "id": "deepseek-v3.1", - "name": "DeepSeek V3.1", - "family": "deepseek", + "gpt-5-mini": { + "id": "gpt-5-mini", + "name": "GPT-5 Mini", + "family": "gpt-mini", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-08-21", - "last_updated": "2025-08-21", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.56, "output": 1.68, "cache_read": 0.11 }, - "limit": { "context": 128000, "output": 32768 } - }, - "gemini-2.0-flash": { - "id": "gemini-2.0-flash", - "name": "Gemini 2.0 Flash", - "family": "gemini", - "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-02-05", - "last_updated": "2025-02-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.03 }, - "limit": { "context": 1048576, "output": 8192 }, - "status": "deprecated" + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.025 + } }, - "custom": { - "id": "custom", - "name": "Custom Model", - "family": "auto", + "gpt-5-nano": { + "id": "gpt-5-nano", + "name": "GPT-5 Nano", + "family": "gpt-nano", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2024-01-01", - "last_updated": "2024-01-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.05, + "output": 0.4, + "cache_read": 0.005 + } }, - "glm-4.5-flash": { - "id": "glm-4.5-flash", - "name": "GLM-4.5 Flash", - "family": "glm", - "attachment": false, - "reasoning": false, + "gpt-5.3-codex": { + "id": "gpt-5.3-codex", + "name": "GPT-5.3 Codex", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-08-13", - "last_updated": "2025-08-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } - }, - "llama-3.2-3b-instruct": { - "id": "llama-3.2-3b-instruct", - "name": "Llama 3.2 3B Instruct", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": true, - "temperature": true, - "release_date": "2024-09-18", - "last_updated": "2024-09-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.03, "output": 0.05 }, - "limit": { "context": 32768, "output": 32000 }, - "status": "beta" + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "gpt-4.1-mini": { - "id": "gpt-4.1-mini", - "name": "GPT-4.1 Mini", + "gpt-4-turbo": { + "id": "gpt-4-turbo", + "name": "GPT-4 Turbo", "family": "gpt", "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 1.6, "cache_read": 0.1 }, - "limit": { "context": 1000000, "output": 16384 } - }, - "qwen-image-max-2025-12-30": { - "id": "qwen-image-max-2025-12-30", - "name": "Qwen Image Max 2025-12-30", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2025-12-31", - "last_updated": "2025-12-31", - "modalities": { "input": ["text"], "output": ["text", "image"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 2000, "output": 4096 } - }, - "cogview-4": { - "id": "cogview-4", - "name": "CogView-4", - "family": "glm", - "attachment": false, - "reasoning": false, - "tool_call": false, "structured_output": false, "temperature": true, - "release_date": "2025-03-04", - "last_updated": "2025-03-04", - "modalities": { "input": ["text"], "output": ["text", "image"] }, + "knowledge": "2023-12", + "release_date": "2023-11-06", + "last_updated": "2024-04-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 2000, "output": 4096 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 10, + "output": 30 + } }, - "llama-3.2-11b-instruct": { - "id": "llama-3.2-11b-instruct", - "name": "Llama 3.2 11B Instruct", - "family": "llama", + "text-embedding-ada-002": { + "id": "text-embedding-ada-002", + "name": "text-embedding-ada-002", + "family": "text-embedding", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": true, - "temperature": true, - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.07, "output": 0.33 }, - "limit": { "context": 128000, "output": 16384 }, - "status": "beta" + "temperature": false, + "knowledge": "2022-12", + "release_date": "2022-12-15", + "last_updated": "2022-12-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8192, + "output": 1536 + }, + "cost": { + "input": 0.1, + "output": 0 + } }, - "glm-5": { - "id": "glm-5", - "name": "GLM-5", - "family": "glm", - "attachment": false, + "gpt-5.2": { + "id": "gpt-5.2", + "name": "GPT-5.2", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2026-02-15", - "last_updated": "2026-02-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1, "output": 3.2, "cache_read": 0.2 }, - "limit": { "context": 202800, "output": 131100 } - }, - "veo-3.1-generate-preview": { - "id": "veo-3.1-generate-preview", - "name": "Veo 3.1", - "family": "gemini", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2026-03-14", - "last_updated": "2026-03-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 32768, "output": 1 }, - "status": "beta" + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "qwen3-vl-30b-a3b-thinking": { - "id": "qwen3-vl-30b-a3b-thinking", - "name": "Qwen3 VL 30B A3B Thinking", - "family": "qwen", + "o3-pro": { + "id": "o3-pro", + "name": "o3-pro", + "family": "o-pro", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-10-11", - "last_updated": "2025-10-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 1 }, - "limit": { "context": 131072, "output": 32768 } - }, - "llama-3.1-70b-instruct": { - "id": "llama-3.1-70b-instruct", - "name": "Llama 3.1 70B Instruct", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.72, "output": 0.72 }, - "limit": { "context": 128000, "output": 2048 }, - "status": "beta" - }, - "qwen-omni-turbo": { - "id": "qwen-omni-turbo", - "name": "Qwen Omni Turbo", - "family": "qwen", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": true, - "temperature": true, - "release_date": "2025-03-26", - "last_updated": "2025-03-26", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 32768, "output": 8192 } + "temperature": false, + "knowledge": "2024-05", + "release_date": "2025-06-10", + "last_updated": "2025-06-10", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 20, + "output": 80 + } }, - "gpt-5-chat-latest": { - "id": "gpt-5-chat-latest", - "name": "GPT-5 Chat Latest", - "family": "gpt", + "gpt-4o-mini": { + "id": "gpt-4o-mini", + "name": "GPT-4o mini", + "family": "gpt-mini", "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-08-01", - "last_updated": "2025-08-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2023-09", + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.13 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.08 + } }, - "minimax-m2.1-lightning": { - "id": "minimax-m2.1-lightning", - "name": "MiniMax M2.1 Lightning", - "family": "minimax", - "attachment": false, + "o4-mini-deep-research": { + "id": "o4-mini-deep-research", + "name": "o4-mini-deep-research", + "family": "o-mini", + "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.12, "output": 0.48 }, - "limit": { "context": 196608, "output": 131072 } + "tool_call": true, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2024-06-26", + "last_updated": "2024-06-26", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } }, - "grok-4-fast-reasoning": { - "id": "grok-4-fast-reasoning", - "name": "Grok 4 Fast Reasoning", - "family": "grok", + "gpt-5.4-mini": { + "id": "gpt-5.4-mini", + "name": "GPT-5.4 mini", + "family": "gpt-mini", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "experimental": { + "modes": { + "fast": { + "cost": { + "input": 1.5, + "output": 9, + "cache_read": 0.15 + }, + "provider": { + "body": { + "service_tier": "priority" + } + } + } + } + }, + "cost": { + "input": 0.75, + "output": 4.5, + "cache_read": 0.075 + } }, - "gpt-5-nano": { - "id": "gpt-5-nano", - "name": "GPT-5 Nano", - "family": "gpt", - "attachment": false, + "o4-mini": { + "id": "o4-mini", + "name": "o4-mini", + "family": "o-mini", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-08-01", - "last_updated": "2025-08-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.05, "output": 0.4, "cache_read": 0.01 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.28 + } }, - "seed-1-6-250915": { - "id": "seed-1-6-250915", - "name": "Seed 1.6 (250915)", - "family": "seed", + "gpt-5.4-nano": { + "id": "gpt-5.4-nano", + "name": "GPT-5.4 nano", + "family": "gpt-nano", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.05 }, - "limit": { "context": 256000, "output": 16384 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.2, + "output": 1.25, + "cache_read": 0.02 + } }, - "qwen3-32b-fp8": { - "id": "qwen3-32b-fp8", - "name": "Qwen3 32B FP8", - "family": "qwen", - "attachment": false, + "gpt-image-1": { + "id": "gpt-image-1", + "name": "gpt-image-1", + "family": "gpt-image", + "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2025-04-28", - "last_updated": "2025-04-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.45 }, - "limit": { "context": 40960, "output": 20000 } - }, - "claude-3-5-haiku": { - "id": "claude-3-5-haiku", - "name": "Claude 3.5 Haiku", - "family": "claude", - "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-04-24", + "last_updated": "2025-04-24", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 0.8, "output": 4, "cache_read": 0.08 }, - "limit": { "context": 200000, "output": 8192 }, - "status": "deprecated" - }, - "mixtral-8x7b-instruct-together": { - "id": "mixtral-8x7b-instruct-together", - "name": "Mixtral 8x7B Instruct", - "family": "mistral", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": true, - "temperature": true, - "release_date": "2023-12-10", - "last_updated": "2023-12-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.06, "output": 0.06 }, - "limit": { "context": 32768, "output": 16384 } + "limit": { + "context": 0, + "input": 0, + "output": 0 + } }, - "qwen-coder-plus": { - "id": "qwen-coder-plus", - "name": "Qwen Coder Plus", - "family": "qwen", - "attachment": false, - "reasoning": false, + "gpt-5.2-codex": { + "id": "gpt-5.2-codex", + "name": "GPT-5.2 Codex", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2024-09-18", - "last_updated": "2024-09-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1, "output": 5 }, - "limit": { "context": 131072, "output": 8192 } + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "glm-4.5-air": { - "id": "glm-4.5-air", - "name": "GLM-4.5 Air", - "family": "glm", - "attachment": false, - "reasoning": false, + "gpt-5.2-chat-latest": { + "id": "gpt-5.2-chat-latest", + "name": "GPT-5.2 Chat", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-07-25", - "last_updated": "2025-07-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 1.1, "cache_read": 0.03 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "gemini-3.1-flash-lite-preview": { - "id": "gemini-3.1-flash-lite-preview", - "name": "Gemini 3.1 Flash Lite (Preview)", - "family": "gemini", + "gpt-5.1-codex-mini": { + "id": "gpt-5.1-codex-mini", + "name": "GPT-5.1 Codex mini", + "family": "gpt-codex", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2026-03-03", - "last_updated": "2026-03-03", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 1.5, "cache_read": 0.03 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.025 + } }, - "grok-3": { - "id": "grok-3", - "name": "Grok-3", - "family": "grok", + "o1-preview": { + "id": "o1-preview", + "name": "o1-preview", + "family": "o", "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "knowledge": "2023-09", + "release_date": "2024-09-12", + "last_updated": "2024-09-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 15, + "output": 60, + "cache_read": 7.5 + } + }, + "gpt-4o-2024-08-06": { + "id": "gpt-4o-2024-08-06", + "name": "GPT-4o (2024-08-06)", + "family": "gpt", + "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-09", + "release_date": "2024-08-06", + "last_updated": "2024-08-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 131072, "output": 16384 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10, + "cache_read": 1.25 + } }, - "gemini-3.1-flash-image-preview": { - "id": "gemini-3.1-flash-image-preview", - "name": "Gemini 3.1 Flash Image (Preview)", - "family": "gemini", + "gpt-5.1": { + "id": "gpt-5.1", + "name": "GPT-5.1", + "family": "gpt", "attachment": true, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2026-02-26", - "last_updated": "2026-02-26", - "modalities": { "input": ["text", "image"], "output": ["text", "image"] }, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 1.5 }, - "limit": { "context": 65536, "output": 65536 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.13 + } }, - "gemini-2.5-flash-image": { - "id": "gemini-2.5-flash-image", - "name": "Gemini 2.5 Flash Image", - "family": "gemini", + "gpt-image-1-mini": { + "id": "gpt-image-1-mini", + "name": "gpt-image-1-mini", + "family": "gpt-image", "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": true, - "temperature": true, - "release_date": "2025-10-02", - "last_updated": "2025-10-02", - "modalities": { "input": ["text", "image"], "output": ["text", "image"] }, + "temperature": false, + "release_date": "2025-09-26", + "last_updated": "2025-09-26", + "modalities": { + "input": ["text", "image"], + "output": ["text", "image"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 30, "cache_read": 0.03 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 0, + "input": 0, + "output": 0 + } }, - "glm-4.5v": { - "id": "glm-4.5v", - "name": "GLM-4.5V", - "family": "glm", + "o1": { + "id": "o1", + "name": "o1", + "family": "o", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-08-11", - "last_updated": "2025-08-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2023-09", + "release_date": "2024-12-05", + "last_updated": "2024-12-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 1.8, "cache_read": 0.11 }, - "limit": { "context": 128000, "output": 16000 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 15, + "output": 60, + "cache_read": 7.5 + } }, - "minimax-m2.7-highspeed": { - "id": "minimax-m2.7-highspeed", - "name": "MiniMax M2.7 Highspeed", - "family": "minimax", - "attachment": false, + "gpt-5.4-pro": { + "id": "gpt-5.4-pro", + "name": "GPT-5.4 Pro", + "family": "gpt-pro", + "attachment": true, "reasoning": true, - "tool_call": false, + "tool_call": true, "structured_output": false, - "temperature": true, - "release_date": "2024-01-01", - "last_updated": "2024-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.4, "cache_read": 0.06 }, - "limit": { "context": 204800, "output": 131100 } + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 30, + "output": 180, + "tiers": [ + { + "input": 60, + "output": 270, + "tier": { + "type": "context", + "size": 272000 + } + } + ], + "context_over_200k": { + "input": 60, + "output": 270 + } + } }, - "seedream-4-5": { - "id": "seedream-4-5", - "name": "Seedream 4.5", - "family": "seed", + "gpt-3.5-turbo": { + "id": "gpt-3.5-turbo", + "name": "GPT-3.5-turbo", + "family": "gpt", "attachment": false, "reasoning": false, "tool_call": false, "structured_output": false, "temperature": true, - "release_date": "2025-12-03", - "last_updated": "2025-12-03", - "modalities": { "input": ["text"], "output": ["text", "image"] }, + "knowledge": "2021-09-01", + "release_date": "2023-03-01", + "last_updated": "2023-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 2000, "output": 4096 } + "limit": { + "context": 16385, + "output": 4096 + }, + "cost": { + "input": 0.5, + "output": 1.5, + "cache_read": 1.25 + } }, - "qwen3-coder-plus": { - "id": "qwen3-coder-plus", - "name": "Qwen3 Coder Plus", - "family": "qwen", - "attachment": false, - "reasoning": false, + "o3-deep-research": { + "id": "o3-deep-research", + "name": "o3-deep-research", + "family": "o", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-09-23", - "last_updated": "2025-09-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 6, "output": 60 }, - "limit": { "context": 1000000, "output": 66000 } + "temperature": false, + "knowledge": "2024-05", + "release_date": "2024-06-26", + "last_updated": "2024-06-26", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 10, + "output": 40, + "cache_read": 2.5 + } }, - "claude-haiku-4-5-20251001": { - "id": "claude-haiku-4-5-20251001", - "name": "Claude Haiku 4.5 (2025-10-01)", - "family": "claude", + "o3-mini": { + "id": "o3-mini", + "name": "o3-mini", + "family": "o-mini", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2024-12-20", + "last_updated": "2025-01-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.55 + } }, - "glm-4.7": { - "id": "glm-4.7", - "name": "GLM-4.7", - "family": "glm", + "text-embedding-3-small": { + "id": "text-embedding-3-small", + "name": "text-embedding-3-small", + "family": "text-embedding", "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "temperature": false, + "knowledge": "2024-01", + "release_date": "2024-01-25", + "last_updated": "2024-01-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 2.2, "cache_read": 0.11 }, - "limit": { "context": 200000, "output": 128000 } + "limit": { + "context": 8191, + "output": 1536 + }, + "cost": { + "input": 0.02, + "output": 0 + } }, - "qwen3-max-2026-01-23": { - "id": "qwen3-max-2026-01-23", - "name": "Qwen3 Max 2026-01-23", - "family": "qwen", + "o1-pro": { + "id": "o1-pro", + "name": "o1-pro", + "family": "o-pro", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2026-01-23", - "last_updated": "2026-01-23", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1.2, "output": 6, "cache_read": 0.24 }, - "limit": { "context": 262144, "output": 65536 } + "temperature": false, + "knowledge": "2023-09", + "release_date": "2025-03-19", + "last_updated": "2025-03-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 150, + "output": 600 + } }, - "grok-imagine-image-pro": { - "id": "grok-imagine-image-pro", - "name": "Grok Imagine Image Pro", - "family": "grok", + "gpt-4": { + "id": "gpt-4", + "name": "GPT-4", + "family": "gpt", "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, "structured_output": false, "temperature": true, - "release_date": "2026-03-02", - "last_updated": "2026-03-02", - "modalities": { "input": ["text", "image"], "output": ["text", "image"] }, + "knowledge": "2023-11", + "release_date": "2023-11-06", + "last_updated": "2024-04-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 2000, "output": 4096 } + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 30, + "output": 60 + } }, - "qwen-flash": { - "id": "qwen-flash", - "name": "Qwen Flash", - "family": "qwen", + "gpt-5-codex": { + "id": "gpt-5-codex", + "name": "GPT-5-Codex", + "family": "gpt-codex", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2024-09-09", - "last_updated": "2024-09-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.05, "output": 0.4, "cache_read": 0.01 }, - "limit": { "context": 1000000, "output": 32000 } - }, - "deepseek-r1-0528": { - "id": "deepseek-r1-0528", - "name": "DeepSeek R1 (0528)", - "family": "deepseek", - "attachment": false, - "reasoning": true, - "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2025-05-28", - "last_updated": "2025-05-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.8, "output": 2.4 }, - "limit": { "context": 64000, "output": 16384 }, - "status": "beta" + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-09-15", + "last_updated": "2025-09-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "qwen3-max": { - "id": "qwen3-max", - "name": "Qwen3 Max", - "family": "qwen", + "gpt-5.4": { + "id": "gpt-5.4", + "name": "GPT-5.4", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-09-24", - "last_updated": "2025-09-24", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 3, "output": 15, "cache_read": 0.6 }, - "limit": { "context": 256000, "output": 32800 } + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "experimental": { + "modes": { + "fast": { + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5 + }, + "provider": { + "body": { + "service_tier": "priority" + } + } + } + } + }, + "cost": { + "input": 2.5, + "output": 15, + "cache_read": 0.25, + "tiers": [ + { + "input": 5, + "output": 22.5, + "cache_read": 0.5, + "tier": { + "type": "context", + "size": 272000 + } + } + ], + "context_over_200k": { + "input": 5, + "output": 22.5, + "cache_read": 0.5 + } + } }, - "gpt-oss-20b": { - "id": "gpt-oss-20b", - "name": "GPT OSS 20B", - "family": "gpt-oss", - "attachment": false, + "gpt-5.1-chat-latest": { + "id": "gpt-5.1-chat-latest", + "name": "GPT-5.1 Chat", + "family": "gpt-codex", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.5 }, - "limit": { "context": 131072, "output": 32766 } - }, - "qwen2-5-vl-72b-instruct": { - "id": "qwen2-5-vl-72b-instruct", - "name": "Qwen2.5 VL 72B Instruct", - "family": "qwen", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": true, - "temperature": true, - "release_date": "2025-01-26", - "last_updated": "2025-01-26", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.13, "output": 0.4 }, - "limit": { "context": 32768, "output": 8192 } - }, - "gemma-3-27b": { - "id": "gemma-3-27b", - "name": "Gemma 3 27B", - "family": "gemma", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2025-03-12", - "last_updated": "2025-03-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.27, "output": 0.27 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "grok-4-20-beta-0309-reasoning": { - "id": "grok-4-20-beta-0309-reasoning", - "name": "Grok 4.20 Beta Reasoning (0309)", - "family": "grok", + "gpt-5.3-codex-spark": { + "id": "gpt-5.3-codex-spark", + "name": "GPT-5.3 Codex Spark", + "family": "gpt-codex-spark", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2026-03-09", - "last_updated": "2026-03-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 6, "cache_read": 0.2 }, - "limit": { "context": 2000000, "output": 30000 } + "limit": { + "context": 128000, + "input": 100000, + "output": 32000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "qwen3-32b": { - "id": "qwen3-32b", - "name": "Qwen3 32B", - "family": "qwen", - "attachment": false, + "chatgpt-image-latest": { + "id": "chatgpt-image-latest", + "name": "chatgpt-image-latest", + "family": "gpt-image", + "attachment": true, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-04-28", - "last_updated": "2025-04-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 32768, "output": 8192 } + "tool_call": false, + "temperature": false, + "release_date": "2025-12-16", + "last_updated": "2025-12-16", + "modalities": { + "input": ["text", "image"], + "output": ["text", "image"] + }, + "open_weights": false, + "limit": { + "context": 0, + "input": 0, + "output": 0 + } }, "gpt-4.1-nano": { "id": "gpt-4.1-nano", - "name": "GPT-4.1 Nano", - "family": "gpt", + "name": "GPT-4.1 nano", + "family": "gpt-nano", "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, + "knowledge": "2024-04", "release_date": "2025-04-14", "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.03 }, - "limit": { "context": 1000000, "output": 16384 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.03 + } }, - "gemma-3-12b-it": { - "id": "gemma-3-12b-it", - "name": "Gemma 3 12B IT", - "family": "gemma", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2025-03-10", - "last_updated": "2025-03-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.08, "output": 0.3 }, - "limit": { "context": 1000000, "output": 16384 } + "o3": { + "id": "o3", + "name": "o3", + "family": "o", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } }, - "claude-sonnet-4-5": { - "id": "claude-sonnet-4-5", - "name": "Claude Sonnet 4.5", - "family": "claude", - "attachment": false, + "gpt-5-pro": { + "id": "gpt-5-pro", + "name": "GPT-5 Pro", + "family": "gpt-pro", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-10-06", + "last_updated": "2025-10-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3 }, - "limit": { "context": 200000, "output": 64000 } - }, - "devstral-small-2507": { - "id": "devstral-small-2507", - "name": "Devstral Small 1.1", - "family": "mistral", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": true, - "temperature": true, - "release_date": "2025-07-21", - "last_updated": "2025-07-21", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 131072, "output": 16384 } + "limit": { + "context": 400000, + "input": 272000, + "output": 272000 + }, + "cost": { + "input": 15, + "output": 120 + } }, - "ministral-3b-2512": { - "id": "ministral-3b-2512", - "name": "Ministral 3B", - "family": "mistral", + "gpt-4o": { + "id": "gpt-4o", + "name": "GPT-4o", + "family": "gpt", "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-12-02", - "last_updated": "2025-12-02", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.1 }, - "limit": { "context": 131072, "output": 16384 } + "knowledge": "2023-09", + "release_date": "2024-05-13", + "last_updated": "2024-08-06", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10, + "cache_read": 1.25 + } }, - "deepseek-v3.2": { - "id": "deepseek-v3.2", - "name": "DeepSeek V3.2", - "family": "deepseek", + "gpt-5": { + "id": "gpt-5", + "name": "GPT-5", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.28, "output": 0.42, "cache_read": 0.03 }, - "limit": { "context": 163840, "output": 16384 } + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "gemma-2-27b-it-together": { - "id": "gemma-2-27b-it-together", - "name": "Gemma 2 27B IT", - "family": "gemma", - "attachment": false, - "reasoning": false, + "gpt-5-chat-latest": { + "id": "gpt-5-chat-latest", + "name": "GPT-5 Chat (latest)", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, "tool_call": false, - "structured_output": false, + "structured_output": true, "temperature": true, - "release_date": "2024-06-27", - "last_updated": "2024-06-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.08, "output": 0.08 }, - "limit": { "context": 8192, "output": 16384 } + "knowledge": "2024-09-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, - "qwen3-4b-fp8": { - "id": "qwen3-4b-fp8", - "name": "Qwen3 4B FP8", - "family": "qwen", - "attachment": false, + "gpt-image-1.5": { + "id": "gpt-image-1.5", + "name": "gpt-image-1.5", + "family": "gpt-image", + "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2025-04-28", - "last_updated": "2025-04-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.03, "output": 0.03 }, - "limit": { "context": 128000, "output": 20000 } + "temperature": false, + "release_date": "2025-11-25", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text", "image"] + }, + "open_weights": false, + "limit": { + "context": 0, + "input": 0, + "output": 0 + } }, - "qwen3-vl-235b-a22b-thinking": { - "id": "qwen3-vl-235b-a22b-thinking", - "name": "Qwen3 VL 235B A22B Thinking", - "family": "qwen", + "gpt-5.5-pro": { + "id": "gpt-5.5-pro", + "name": "GPT-5.5 Pro", + "family": "gpt-pro", "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2025-09-23", - "last_updated": "2025-09-23", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.5, "output": 2 }, - "limit": { "context": 131072, "output": 32768 } + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-12-01", + "release_date": "2026-04-23", + "last_updated": "2026-04-23", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 30, + "output": 180, + "tiers": [ + { + "input": 60, + "output": 270, + "tier": { + "type": "context", + "size": 272000 + } + } + ], + "context_over_200k": { + "input": 60, + "output": 270 + } + } }, - "llama-4-maverick-17b-instruct": { - "id": "llama-4-maverick-17b-instruct", - "name": "Llama 4 Maverick 17B Instruct", - "family": "llama", + "gpt-4.1": { + "id": "gpt-4.1", + "name": "GPT-4.1", + "family": "gpt", "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.24, "output": 0.97 }, - "limit": { "context": 8192, "output": 2048 }, - "status": "beta" + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } }, - "qwen3-coder-flash": { - "id": "qwen3-coder-flash", - "name": "Qwen3 Coder Flash", - "family": "qwen", - "attachment": false, + "gpt-4.1-mini": { + "id": "gpt-4.1-mini", + "name": "GPT-4.1 mini", + "family": "gpt-mini", + "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-07-22", - "last_updated": "2025-07-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.5, "cache_read": 0.06 }, - "limit": { "context": 1000000, "output": 65536 } + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 1.6, + "cache_read": 0.1 + } }, - "claude-3-haiku": { - "id": "claude-3-haiku", - "name": "Claude 3 Haiku", - "family": "claude", + "gpt-5.1-codex": { + "id": "gpt-5.1-codex", + "name": "GPT-5.1 Codex", + "family": "gpt-codex", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2024-03-04", - "last_updated": "2024-03-04", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 1.25, "cache_read": 0.03 }, - "limit": { "context": 200000, "output": 4096 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "gemini-2.5-flash-image-preview": { - "id": "gemini-2.5-flash-image-preview", - "name": "Gemini 2.5 Flash Image (Preview)", - "family": "gemini", + "gpt-4o-2024-11-20": { + "id": "gpt-4o-2024-11-20", + "name": "GPT-4o (2024-11-20)", + "family": "gpt", "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-10-02", - "last_updated": "2025-10-02", - "modalities": { "input": ["text", "image"], "output": ["text", "image"] }, + "knowledge": "2023-09", + "release_date": "2024-11-20", + "last_updated": "2024-11-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10, + "cache_read": 1.25 + } + } + } + }, + "requesty": { + "id": "requesty", + "env": ["REQUESTY_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://router.requesty.ai/v1", + "name": "Requesty", + "doc": "https://requesty.ai/solution/llm-routing/models", + "models": { + "xai/grok-4-fast": { + "id": "xai/grok-4-fast", + "name": "Grok 4 Fast", + "family": "grok", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-09-19", + "last_updated": "2025-09-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 2.5 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 2000000, + "output": 64000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05, + "cache_write": 0.2 + } }, - "o4-mini": { - "id": "o4-mini", - "name": "o4 Mini", - "family": "gpt", + "xai/grok-4": { + "id": "xai/grok-4", + "name": "Grok 4", + "family": "grok", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-09-09", + "last_updated": "2025-09-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.28 }, - "limit": { "context": 200000, "output": 16384 } + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75, + "cache_write": 3 + } }, - "glm-4.5-x": { - "id": "glm-4.5-x", - "name": "GLM-4.5 X", - "family": "glm", - "attachment": false, + "openai/gpt-5.1-codex-max": { + "id": "openai/gpt-5.1-codex-max", + "name": "GPT-5.1-Codex-Max", + "family": "gpt-codex", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.2, "output": 8.9, "cache_read": 0.45 }, - "limit": { "context": 128000, "output": 16384 }, - "status": "beta" + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.1, + "output": 9, + "cache_read": 0.11 + } }, - "gemini-3-pro-image-preview": { - "id": "gemini-3-pro-image-preview", - "name": "Gemini 3 Pro Image (Preview)", - "family": "gemini", + "openai/gpt-5-chat": { + "id": "openai/gpt-5-chat", + "name": "GPT-5 Chat (latest)", + "family": "gpt-codex", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": false, "structured_output": true, "temperature": true, - "release_date": "2025-11-20", - "last_updated": "2025-11-20", - "modalities": { "input": ["text", "image"], "output": ["text", "image"] }, + "knowledge": "2024-09-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 12, "cache_read": 0.2 }, - "limit": { "context": 65536, "output": 32768 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, - "claude-haiku-4-5": { - "id": "claude-haiku-4-5", - "name": "Claude Haiku 4.5", - "family": "claude", - "attachment": false, - "reasoning": false, + "openai/gpt-5.2-pro": { + "id": "openai/gpt-5.2-pro", + "name": "GPT-5.2 Pro", + "family": "gpt-pro", + "attachment": true, + "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 21, + "output": 168 + } }, - "qwen25-coder-7b": { - "id": "qwen25-coder-7b", - "name": "Qwen2.5 Coder 7B", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": true, - "temperature": true, - "release_date": "2024-09-19", - "last_updated": "2024-09-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.01, "output": 0.03 }, - "limit": { "context": 32768, "output": 8192 } + "openai/gpt-5-mini": { + "id": "openai/gpt-5-mini", + "name": "GPT-5 Mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 32000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.03 + } }, - "seed-1-8-251228": { - "id": "seed-1-8-251228", - "name": "Seed 1.8 (251228)", - "family": "seed", + "openai/gpt-5-nano": { + "id": "openai/gpt-5-nano", + "name": "GPT-5 Nano", + "family": "gpt-nano", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-12-18", - "last_updated": "2025-12-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.05 }, - "limit": { "context": 256000, "output": 16384 } + "limit": { + "context": 16000, + "output": 4000 + }, + "cost": { + "input": 0.05, + "output": 0.4, + "cache_read": 0.01 + } }, - "gpt-5.1-codex": { - "id": "gpt-5.1-codex", - "name": "GPT-5.1 Codex", - "family": "gpt", + "openai/gpt-5.3-codex": { + "id": "openai/gpt-5.3-codex", + "name": "GPT-5.3-Codex", + "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-02-24", + "last_updated": "2026-02-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 400000, "output": 272000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "claude-3-5-sonnet-20241022": { - "id": "claude-3-5-sonnet-20241022", - "name": "Claude 3.5 Sonnet (2024-10-22)", - "family": "claude", - "attachment": false, + "openai/gpt-4o-mini": { + "id": "openai/gpt-4o-mini", + "name": "GPT-4o Mini", + "family": "gpt-mini", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": false, "temperature": true, - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3 }, - "limit": { "context": 200000, "output": 8192 }, - "status": "deprecated" + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.08 + } }, - "minimax-m2.5": { - "id": "minimax-m2.5", - "name": "MiniMax M2.5", - "family": "minimax", - "attachment": false, + "openai/gpt-5.1-chat": { + "id": "openai/gpt-5.1-chat", + "name": "GPT-5.1 Chat", + "family": "gpt-codex", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2026-02-15", - "last_updated": "2026-02-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.03 }, - "limit": { "context": 204800, "output": 131100 } + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "mistral-large-2512": { - "id": "mistral-large-2512", - "name": "Mistral Large 3", - "family": "mistral", + "openai/o4-mini": { + "id": "openai/o4-mini", + "name": "o4 Mini", + "family": "o-mini", "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": true, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-12-02", - "last_updated": "2025-12-02", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.5, "output": 1.5 }, - "limit": { "context": 262144, "output": 16384 } + "knowledge": "2024-06", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.28 + } }, - "minimax-m2.5-highspeed": { - "id": "minimax-m2.5-highspeed", - "name": "MiniMax M2.5 Highspeed", - "family": "minimax", - "attachment": false, + "openai/gpt-5.2-codex": { + "id": "openai/gpt-5.2-codex", + "name": "GPT-5.2-Codex", + "family": "gpt-codex", + "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2024-01-01", - "last_updated": "2024-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.4, "cache_read": 0.03 }, - "limit": { "context": 204800, "output": 131100 } + "knowledge": "2025-08-31", + "release_date": "2026-01-14", + "last_updated": "2026-01-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "glm-4.6v-flash": { - "id": "glm-4.6v-flash", - "name": "GLM-4.6V Flash", - "family": "glm", + "openai/gpt-5.1-codex-mini": { + "id": "openai/gpt-5.1-codex-mini", + "name": "GPT-5.1-Codex-Mini", + "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-12-08", - "last_updated": "2025-12-08", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16000 } - }, - "qwen3-30b-a3b-instruct-2507": { - "id": "qwen3-30b-a3b-instruct-2507", - "name": "Qwen3 30B A3B Instruct 2507", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-07-30", - "last_updated": "2025-07-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 262000, "output": 8192 } + "limit": { + "context": 400000, + "output": 100000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.025 + } }, - "grok-4-1-fast": { - "id": "grok-4-1-fast", - "name": "Grok 4.1 Fast", - "family": "grok", + "openai/gpt-5-image": { + "id": "openai/gpt-5-image", + "name": "GPT-5 Image", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-11-19", - "last_updated": "2025-11-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-10-01", + "release_date": "2025-10-14", + "last_updated": "2025-10-14", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text", "image"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 10, + "cache_read": 1.25 + } }, - "gpt-5.2-pro": { - "id": "gpt-5.2-pro", - "name": "GPT-5.2 Pro", + "openai/gpt-5.1": { + "id": "openai/gpt-5.1", + "name": "GPT-5.1", "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 21, "output": 168 }, - "limit": { "context": 400000, "output": 272000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "claude-3-7-sonnet": { - "id": "claude-3-7-sonnet", - "name": "Claude 3.7 Sonnet", - "family": "claude", - "attachment": false, + "openai/gpt-5.4-pro": { + "id": "openai/gpt-5.4-pro", + "name": "GPT-5.4 Pro", + "family": "gpt-pro", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": false, - "temperature": true, - "release_date": "2025-02-24", - "last_updated": "2025-02-24", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3 }, - "limit": { "context": 200000, "output": 8192 } - } - } - }, - "groq": { - "id": "groq", - "env": ["GROQ_API_KEY"], - "npm": "@ai-sdk/groq", - "name": "Groq", - "doc": "https://console.groq.com/docs/models", - "models": { - "llama3-70b-8192": { - "id": "llama3-70b-8192", - "name": "Llama 3 70B", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-03", - "release_date": "2024-04-18", - "last_updated": "2024-04-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.59, "output": 0.79 }, - "limit": { "context": 8192, "output": 8192 }, - "status": "deprecated" - }, - "llama-3.3-70b-versatile": { - "id": "llama-3.3-70b-versatile", - "name": "Llama 3.3 70B Versatile", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.59, "output": 0.79 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 30, + "output": 180, + "cache_read": 30 + } }, - "deepseek-r1-distill-llama-70b": { - "id": "deepseek-r1-distill-llama-70b", - "name": "DeepSeek R1 Distill Llama 70B", - "family": "deepseek-thinking", - "attachment": false, + "openai/gpt-5-codex": { + "id": "openai/gpt-5-codex", + "name": "GPT-5 Codex", + "family": "gpt-codex", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.75, "output": 0.99 }, - "limit": { "context": 131072, "output": 8192 }, - "status": "deprecated" - }, - "gemma2-9b-it": { - "id": "gemma2-9b-it", - "name": "Gemma 2 9B", - "family": "gemma", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-06", - "release_date": "2024-06-27", - "last_updated": "2024-06-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 8192, "output": 8192 }, - "status": "deprecated" - }, - "llama-3.1-8b-instant": { - "id": "llama-3.1-8b-instant", - "name": "Llama 3.1 8B Instant", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.05, "output": 0.08 }, - "limit": { "context": 131072, "output": 131072 } + "knowledge": "2024-10-01", + "release_date": "2025-09-15", + "last_updated": "2025-09-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "qwen-qwq-32b": { - "id": "qwen-qwq-32b", - "name": "Qwen QwQ 32B", - "family": "qwen", - "attachment": false, + "openai/gpt-5": { + "id": "openai/gpt-5", + "name": "GPT-5", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-09", - "release_date": "2024-11-27", - "last_updated": "2024-11-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.29, "output": 0.39 }, - "limit": { "context": 131072, "output": 16384 }, - "status": "deprecated" - }, - "llama-guard-3-8b": { - "id": "llama-guard-3-8b", - "name": "Llama Guard 3 8B", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 8192, "output": 8192 }, - "status": "deprecated" - }, - "mistral-saba-24b": { - "id": "mistral-saba-24b", - "name": "Mistral Saba 24B", - "family": "mistral", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-02-06", - "last_updated": "2025-02-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "audio", "image", "video"], + "output": ["text", "audio", "image"] + }, "open_weights": false, - "cost": { "input": 0.79, "output": 0.79 }, - "limit": { "context": 32768, "output": 32768 }, - "status": "deprecated" - }, - "whisper-large-v3": { - "id": "whisper-large-v3", - "name": "Whisper Large V3", - "family": "whisper", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2023-09", - "release_date": "2023-09-01", - "last_updated": "2025-09-05", - "modalities": { "input": ["audio"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 448, "output": 448 } - }, - "whisper-large-v3-turbo": { - "id": "whisper-large-v3-turbo", - "name": "Whisper Large v3 Turbo", - "family": "whisper", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-10-01", - "last_updated": "2024-10-01", - "modalities": { "input": ["audio"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 448, "output": 448 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.13 + } }, - "llama3-8b-8192": { - "id": "llama3-8b-8192", - "name": "Llama 3 8B", - "family": "llama", - "attachment": false, + "openai/gpt-4.1-mini": { + "id": "openai/gpt-4.1-mini", + "name": "GPT-4.1 Mini", + "family": "gpt-mini", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-03", - "release_date": "2024-04-18", - "last_updated": "2024-04-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.05, "output": 0.08 }, - "limit": { "context": 8192, "output": 8192 }, - "status": "deprecated" - }, - "allam-2-7b": { - "id": "allam-2-7b", - "name": "ALLaM-2-7b", - "family": "allam", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-09", - "release_date": "2024-09", - "last_updated": "2024-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 4096, "output": 4096 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 1.6, + "cache_read": 0.1 + } }, - "openai/gpt-oss-120b": { - "id": "openai/gpt-oss-120b", - "name": "GPT OSS 120B", - "family": "gpt-oss", - "attachment": false, + "openai/gpt-5.1-codex": { + "id": "openai/gpt-5.1-codex", + "name": "GPT-5.1-Codex", + "family": "gpt-codex", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 131072, "output": 65536 } + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "openai/gpt-oss-safeguard-20b": { - "id": "openai/gpt-oss-safeguard-20b", - "name": "Safety GPT OSS 20B", - "family": "gpt-oss", - "attachment": false, + "google/gemini-3-flash-preview": { + "id": "google/gemini-3-flash-preview", + "name": "Gemini 3 Flash", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-03-05", - "last_updated": "2025-03-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.075, "output": 0.3, "cache_read": 0.037 }, - "limit": { "context": 131072, "output": 65536 } + "knowledge": "2025-01", + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.05, + "cache_write": 1 + } }, - "openai/gpt-oss-20b": { - "id": "openai/gpt-oss-20b", - "name": "GPT OSS 20B", - "family": "gpt-oss", - "attachment": false, + "google/gemini-3-pro-preview": { + "id": "google/gemini-3-pro-preview", + "name": "Gemini 3 Pro", + "family": "gemini-pro", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.075, "output": 0.3 }, - "limit": { "context": 131072, "output": 65536 } - }, - "canopylabs/orpheus-arabic-saudi": { - "id": "canopylabs/orpheus-arabic-saudi", - "name": "Orpheus Arabic Saudi", - "family": "canopylabs", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2025-12-16", - "release_date": "2025-12-16", - "last_updated": "2025-12-16", - "modalities": { "input": ["text"], "output": ["audio"] }, - "open_weights": false, - "cost": { "input": 40, "output": 0 }, - "limit": { "context": 4000, "output": 50000 } - }, - "canopylabs/orpheus-v1-english": { - "id": "canopylabs/orpheus-v1-english", - "name": "Orpheus V1 English", - "family": "canopylabs", - "attachment": false, - "reasoning": false, - "tool_call": false, "temperature": true, - "knowledge": "2025-12-19", - "release_date": "2025-12-19", - "last_updated": "2025-12-19", - "modalities": { "input": ["text"], "output": ["audio"] }, + "knowledge": "2025-01", + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 4000, "output": 50000 } - }, - "meta-llama/llama-guard-4-12b": { - "id": "meta-llama/llama-guard-4-12b", - "name": "Llama Guard 4 12B", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 131072, "output": 1024 }, - "status": "deprecated" + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2, + "cache_write": 4.5 + } }, - "meta-llama/llama-4-scout-17b-16e-instruct": { - "id": "meta-llama/llama-4-scout-17b-16e-instruct", - "name": "Llama 4 Scout 17B", - "family": "llama", - "attachment": false, - "reasoning": false, + "google/gemini-2.5-flash": { + "id": "google/gemini-2.5-flash", + "name": "Gemini 2.5 Flash", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.11, "output": 0.34 }, - "limit": { "context": 131072, "output": 8192 } - }, - "meta-llama/llama-prompt-guard-2-22m": { - "id": "meta-llama/llama-prompt-guard-2-22m", - "name": "Llama Prompt Guard 2 22M", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": false, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-10-01", - "last_updated": "2024-10-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.03, "output": 0.03 }, - "limit": { "context": 512, "output": 512 } + "knowledge": "2025-01", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.075, + "cache_write": 0.55 + } }, - "meta-llama/llama-4-maverick-17b-128e-instruct": { - "id": "meta-llama/llama-4-maverick-17b-128e-instruct", - "name": "Llama 4 Maverick 17B", - "family": "llama", - "attachment": false, - "reasoning": false, + "anthropic/claude-haiku-4-5": { + "id": "anthropic/claude-haiku-4-5", + "name": "Claude Haiku 4.5", + "family": "claude-haiku", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.6 }, - "limit": { "context": 131072, "output": 8192 }, - "status": "deprecated" - }, - "meta-llama/llama-prompt-guard-2-86m": { - "id": "meta-llama/llama-prompt-guard-2-86m", - "name": "Llama Prompt Guard 2 86M", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": false, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-10-01", - "last_updated": "2024-10-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.04, "output": 0.04 }, - "limit": { "context": 512, "output": 512 } + "knowledge": "2025-02-01", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 62000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } }, - "qwen/qwen3-32b": { - "id": "qwen/qwen3-32b", - "name": "Qwen3 32B", - "family": "qwen", - "attachment": false, + "anthropic/claude-sonnet-4-6": { + "id": "anthropic/claude-sonnet-4-6", + "name": "Claude Sonnet 4.6", + "family": "claude-sonnet", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-11-08", - "release_date": "2024-12-23", - "last_updated": "2024-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.29, "output": 0.59 }, - "limit": { "context": 131072, "output": 40960 } + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-02-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75, + "tiers": [ + { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5 + } + } }, - "groq/compound-mini": { - "id": "groq/compound-mini", - "name": "Compound Mini", - "family": "groq", - "attachment": false, + "anthropic/claude-3-7-sonnet": { + "id": "anthropic/claude-3-7-sonnet", + "name": "Claude Sonnet 3.7", + "family": "claude-sonnet", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-09-04", - "release_date": "2025-09-04", - "last_updated": "2025-09-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-01", + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "groq/compound": { - "id": "groq/compound", - "name": "Compound", - "family": "groq", - "attachment": false, + "anthropic/claude-opus-4-5": { + "id": "anthropic/claude-opus-4-5", + "name": "Claude Opus 4.5", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-09-04", - "release_date": "2025-09-04", - "last_updated": "2025-09-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-03-31", + "release_date": "2025-11-24", + "last_updated": "2025-11-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "moonshotai/kimi-k2-instruct-0905": { - "id": "moonshotai/kimi-k2-instruct-0905", - "name": "Kimi K2 Instruct 0905", - "family": "kimi", - "attachment": false, - "reasoning": false, + "anthropic/claude-opus-4": { + "id": "anthropic/claude-opus-4", + "name": "Claude Opus 4", + "family": "claude-opus", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1, "output": 3 }, - "limit": { "context": 262144, "output": 16384 } + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "moonshotai/kimi-k2-instruct": { - "id": "moonshotai/kimi-k2-instruct", - "name": "Kimi K2 Instruct", - "family": "kimi", - "attachment": false, - "reasoning": false, + "anthropic/claude-opus-4-6": { + "id": "anthropic/claude-opus-4-6", + "name": "Claude Opus 4.6", + "family": "claude-opus", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-07-14", - "last_updated": "2025-07-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1, "output": 3 }, - "limit": { "context": 131072, "output": 16384 }, - "status": "deprecated" - } - } - }, - "azure": { - "id": "azure", - "env": ["AZURE_RESOURCE_NAME", "AZURE_API_KEY"], - "npm": "@ai-sdk/azure", - "name": "Azure", - "doc": "https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models", - "models": { - "gpt-5.2-codex": { - "id": "gpt-5.2-codex", - "name": "GPT-5.2 Codex", + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25, + "tiers": [ + { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5 + } + } + }, + "openai/gpt-5.2-chat": { + "id": "openai/gpt-5.2-chat", + "name": "GPT-5.2 Chat", "family": "gpt-codex", - "attachment": false, + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, "knowledge": "2025-08-31", - "release_date": "2026-01-14", - "last_updated": "2026-01-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "output": 128000 } - }, - "text-embedding-3-large": { - "id": "text-embedding-3-large", - "name": "text-embedding-3-large", - "family": "text-embedding", - "attachment": false, - "reasoning": false, - "tool_call": false, - "release_date": "2024-01-25", - "last_updated": "2024-01-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.13, "output": 0 }, - "limit": { "context": 8191, "output": 3072 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "gpt-5.1-codex-mini": { - "id": "gpt-5.1-codex-mini", - "name": "GPT-5.1 Codex Mini", - "family": "gpt-codex", - "attachment": false, + "openai/gpt-5.2": { + "id": "openai/gpt-5.2", + "name": "GPT-5.2", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.025 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "phi-4-multimodal": { - "id": "phi-4-multimodal", - "name": "Phi-4-multimodal", - "family": "phi", + "openai/gpt-5.4": { + "id": "openai/gpt-5.4", + "name": "GPT-5.4", + "family": "gpt", "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text", "image", "audio"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.08, "output": 0.32, "input_audio": 4 }, - "limit": { "context": 128000, "output": 4096 } - }, - "cohere-embed-v3-multilingual": { - "id": "cohere-embed-v3-multilingual", - "name": "Embed v3 Multilingual", - "family": "cohere-embed", - "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": false, - "release_date": "2023-11-07", - "last_updated": "2023-11-07", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0 }, - "limit": { "context": 512, "output": 1024 } + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 2.5, + "output": 15, + "cache_read": 0.25, + "context_over_200k": { + "input": 5, + "output": 22.5, + "cache_read": 0.5 + }, + "tiers": [ + { + "input": 5, + "output": 22.5, + "cache_read": 0.5, + "tier": { + "type": "context", + "size": 272000 + } + } + ] + } }, - "gpt-5.4-pro": { - "id": "gpt-5.4-pro", - "name": "GPT-5.4 Pro", + "openai/gpt-5-pro": { + "id": "openai/gpt-5-pro", + "name": "GPT-5 Pro", "family": "gpt-pro", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": false, + "structured_output": true, "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-09-30", + "release_date": "2025-10-06", + "last_updated": "2025-10-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 30, "output": 180 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 400000, + "output": 272000 + }, + "cost": { + "input": 15, + "output": 120 + } }, - "cohere-embed-v3-english": { - "id": "cohere-embed-v3-english", - "name": "Embed v3 English", - "family": "cohere-embed", - "attachment": false, + "openai/gpt-4.1": { + "id": "openai/gpt-4.1", + "name": "GPT-4.1", + "family": "gpt", + "attachment": true, "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2023-11-07", - "last_updated": "2023-11-07", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0 }, - "limit": { "context": 512, "output": 1024 } + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } }, - "o3-mini": { - "id": "o3-mini", - "name": "o3-mini", - "family": "o-mini", - "attachment": false, + "google/gemini-2.5-pro": { + "id": "google/gemini-2.5-pro", + "name": "Gemini 2.5 Pro", + "family": "gemini-pro", + "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2024-12-20", - "last_updated": "2025-01-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.55 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.31, + "cache_write": 2.375, + "context_over_200k": { + "input": 2.5, + "output": 15, + "cache_read": 0.25 + }, + "tiers": [ + { + "input": 2.5, + "output": 15, + "cache_read": 0.25, + "tier": { + "type": "context", + "size": 200000 + } + } + ] + } }, - "gpt-5.4-mini": { - "id": "gpt-5.4-mini", - "name": "GPT-5.4 Mini", - "family": "gpt-mini", + "anthropic/claude-opus-4-1": { + "id": "anthropic/claude-opus-4-1", + "name": "Claude Opus 4.1", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-17", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.75, "output": 4.5, "cache_read": 0.075 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "gpt-5-pro": { - "id": "gpt-5-pro", - "name": "GPT-5 Pro", - "family": "gpt-pro", + "anthropic/claude-sonnet-4": { + "id": "anthropic/claude-sonnet-4", + "name": "Claude Sonnet 4", + "family": "claude-sonnet", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-10-06", - "last_updated": "2025-10-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 120 }, - "limit": { "context": 400000, "output": 272000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "gpt-3.5-turbo-0613": { - "id": "gpt-3.5-turbo-0613", - "name": "GPT-3.5 Turbo 0613", - "family": "gpt", - "attachment": false, + "anthropic/claude-sonnet-4-5": { + "id": "anthropic/claude-sonnet-4-5", + "name": "Claude Sonnet 4.5", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + } + } + }, + "digitalocean": { + "id": "digitalocean", + "env": ["DIGITALOCEAN_ACCESS_TOKEN"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://inference.do-ai.run/v1", + "name": "DigitalOcean", + "doc": "https://docs.digitalocean.com/products/gradient-ai-platform/details/models/", + "models": { + "openai-gpt-4o-mini": { + "id": "openai-gpt-4o-mini", + "name": "GPT-4o mini", + "family": "gpt-mini", + "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2021-08", - "release_date": "2023-06-13", - "last_updated": "2023-06-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-09", + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 4 }, - "limit": { "context": 16384, "output": 16384 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.075 + } }, - "deepseek-v3-0324": { - "id": "deepseek-v3-0324", - "name": "DeepSeek-V3-0324", - "family": "deepseek", + "multi-qa-mpnet-base-dot-v1": { + "id": "multi-qa-mpnet-base-dot-v1", + "name": "Multi-QA-mpnet-base-dot-v1", + "family": "text-embedding", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-03-24", - "last_updated": "2025-03-24", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "temperature": false, + "release_date": "2021-08-30", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1.14, "output": 4.56 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 512, + "output": 768 + }, + "cost": { + "input": 0.009, + "output": 0 + } }, - "meta-llama-3.1-8b-instruct": { - "id": "meta-llama-3.1-8b-instruct", - "name": "Meta-Llama-3.1-8B-Instruct", - "family": "llama", + "kimi-k2.5": { + "id": "kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2026-01", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 0.61 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0.5, + "output": 2.7 + } }, - "kimi-k2-thinking": { - "id": "kimi-k2-thinking", - "name": "Kimi K2 Thinking", - "family": "kimi-thinking", - "attachment": false, + "nemotron-3-nano-omni": { + "id": "nemotron-3-nano-omni", + "name": "Nemotron Nano 3 Omni", + "family": "nemotron", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-11-06", - "last_updated": "2025-12-02", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-28", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.5, "cache_read": 0.15 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 65536, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 0.9 + } }, - "deepseek-r1": { - "id": "deepseek-r1", - "name": "DeepSeek-R1", - "family": "deepseek-thinking", + "llama3-8b-instruct": { + "id": "llama3-8b-instruct", + "name": "Llama 3.1 Instruct (8B)", + "family": "llama", "attachment": false, - "reasoning": true, - "tool_call": false, + "reasoning": false, + "tool_call": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1.35, "output": 5.4 }, - "limit": { "context": 163840, "output": 163840 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.198, + "output": 0.198 + } }, - "gpt-5": { - "id": "gpt-5", - "name": "GPT-5", - "family": "gpt", + "anthropic-claude-opus-4.7": { + "id": "anthropic-claude-opus-4.7", + "name": "Claude Opus 4.7", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.13 }, - "limit": { "context": 272000, "output": 128000 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "llama-3.3-70b-instruct": { - "id": "llama-3.3-70b-instruct", - "name": "Llama-3.3-70B-Instruct", - "family": "llama", - "attachment": false, - "reasoning": false, + "anthropic-claude-sonnet-4": { + "id": "anthropic-claude-sonnet-4", + "name": "Claude Sonnet 4", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.71, "output": 0.71 }, - "limit": { "context": 128000, "output": 32768 } + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75, + "tiers": [ + { + "input": 6, + "output": 22.5, + "cache_read": 0.3, + "cache_write": 3.75, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 6, + "output": 22.5, + "cache_read": 0.3, + "cache_write": 3.75 + } + } }, - "phi-3-mini-128k-instruct": { - "id": "phi-3-mini-128k-instruct", - "name": "Phi-3-mini-instruct (128k)", - "family": "phi", + "wan2-2-t2v-a14b": { + "id": "wan2-2-t2v-a14b", + "name": "Wan2.2-T2V-A14B", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-04-23", - "last_updated": "2024-04-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-07-28", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text"], + "output": ["video"] + }, "open_weights": true, - "cost": { "input": 0.13, "output": 0.52 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 100, + "output": 1 + }, + "cost": { + "input": 0.6, + "output": 0 + } }, - "phi-3-small-8k-instruct": { - "id": "phi-3-small-8k-instruct", - "name": "Phi-3-small-instruct (8k)", - "family": "phi", + "qwen-2.5-14b-instruct": { + "id": "qwen-2.5-14b-instruct", + "name": "Qwen 2.5 14B Instruct", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-04-23", - "last_updated": "2024-04-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-09", + "release_date": "2024-09-19", + "last_updated": "2024-09-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 8192, "output": 2048 } + "limit": { + "context": 131072, + "output": 131072 + } }, - "claude-opus-4-5": { - "id": "claude-opus-4-5", - "name": "Claude Opus 4.5", - "family": "claude-opus", + "openai-gpt-5.4": { + "id": "openai-gpt-5.4", + "name": "GPT-5.4", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-11-24", - "last_updated": "2025-08-01", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 200000, "output": 64000 }, - "provider": { - "npm": "@ai-sdk/anthropic", - "api": "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 2.5, + "output": 15, + "cache_read": 0.25 } }, - "kimi-k2.5": { - "id": "kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi", + "qwen3.5-397b-a17b": { + "id": "qwen3.5-397b-a17b", + "name": "Qwen 3.5 397B A17B", + "family": "qwen3.5", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": true, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-02-06", - "last_updated": "2026-02-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-02-15", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 3 }, - "limit": { "context": 262144, "output": 262144 }, - "provider": { - "npm": "@ai-sdk/openai-compatible", - "api": "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/models", - "shape": "completions" + "limit": { + "context": 262144, + "output": 81920 + }, + "cost": { + "input": 0.55, + "output": 3.5 } }, - "gpt-4-turbo": { - "id": "gpt-4-turbo", - "name": "GPT-4 Turbo", - "family": "gpt", + "openai-o3": { + "id": "openai-o3", + "name": "o3", + "family": "o", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2023-11", - "release_date": "2023-11-06", - "last_updated": "2024-04-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 10, "output": 30 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } }, - "gpt-4o": { - "id": "gpt-4o", - "name": "GPT-4o", - "family": "gpt", - "attachment": true, + "e5-large-v2": { + "id": "e5-large-v2", + "name": "E5 Large v2", + "family": "text-embedding", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-09", - "release_date": "2024-05-13", - "last_updated": "2024-05-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 10, "cache_read": 1.25 }, - "limit": { "context": 128000, "output": 16384 } + "tool_call": false, + "temperature": false, + "release_date": "2023-05-19", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 512, + "output": 1024 + }, + "cost": { + "input": 0.02, + "output": 0 + } }, - "grok-4-fast-non-reasoning": { - "id": "grok-4-fast-non-reasoning", - "name": "Grok 4 Fast (Non-Reasoning)", - "family": "grok", + "openai-gpt-5.2-pro": { + "id": "openai-gpt-5.2-pro", + "name": "GPT-5.2 pro", + "family": "gpt-pro", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-09-19", - "last_updated": "2025-09-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 21, + "output": 168 + } }, - "grok-4": { - "id": "grok-4", - "name": "Grok 4", - "family": "grok", + "glm-5": { + "id": "glm-5", + "name": "GLM 5", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "reasoning": 15, "cache_read": 0.75 }, - "limit": { "context": 256000, "output": 64000 } + "interleaved": { + "field": "reasoning_content" + }, + "release_date": "2026-02-11", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 128000 + }, + "cost": { + "input": 1, + "output": 3.2 + } }, - "gpt-3.5-turbo-1106": { - "id": "gpt-3.5-turbo-1106", - "name": "GPT-3.5 Turbo 1106", - "family": "gpt", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2021-08", - "release_date": "2023-11-06", - "last_updated": "2023-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "openai-gpt-5.4-nano": { + "id": "openai-gpt-5.4-nano", + "name": "GPT-5.4 nano", + "family": "gpt-nano", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 2 }, - "limit": { "context": 16384, "output": 16384 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.2, + "output": 1.25, + "cache_read": 0.02 + } }, - "phi-4-mini": { - "id": "phi-4-mini", - "name": "Phi-4-mini", - "family": "phi", + "mistral-7b-instruct-v0.3": { + "id": "mistral-7b-instruct-v0.3", + "name": "Mistral 7B Instruct v0.3", + "family": "mistral", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.075, "output": 0.3 }, - "limit": { "context": 128000, "output": 4096 } - }, - "phi-4-reasoning": { - "id": "phi-4-reasoning", - "name": "Phi-4-reasoning", - "family": "phi", - "attachment": false, - "reasoning": true, - "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-05-22", + "last_updated": "2024-05-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.125, "output": 0.5 }, - "limit": { "context": 32000, "output": 4096 } + "limit": { + "context": 32768, + "output": 32768 + } }, - "grok-code-fast-1": { - "id": "grok-code-fast-1", - "name": "Grok Code Fast 1", - "family": "grok", + "llama3.3-70b-instruct": { + "id": "llama3.3-70b-instruct", + "name": "Llama 3.3 Instruct 70B", + "family": "llama", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2025-08-28", - "last_updated": "2025-08-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 1.5, "cache_read": 0.02 }, - "limit": { "context": 256000, "output": 10000 } + "knowledge": "2023-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.65, + "output": 0.65 + } }, - "codestral-2501": { - "id": "codestral-2501", - "name": "Codestral 25.01", - "family": "codestral", + "mistral-3-14B": { + "id": "mistral-3-14B", + "name": "Ministral 3 14B Instruct", + "family": "ministral", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-03", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 0.9 }, - "limit": { "context": 256000, "output": 256000 } + "release_date": "2025-12-15", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 128000 + }, + "cost": { + "input": 0.2, + "output": 0.2 + } }, - "gpt-5.3-codex": { - "id": "gpt-5.3-codex", - "name": "GPT-5.3 Codex", - "family": "gpt-codex", + "deepseek-r1-distill-llama-70b": { + "id": "deepseek-r1-distill-llama-70b", + "name": "DeepSeek R1 Distill Llama 70B", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-02-24", - "last_updated": "2026-02-24", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "output": 128000 } - }, - "gpt-5-mini": { - "id": "gpt-5-mini", - "name": "GPT-5 Mini", - "family": "gpt-mini", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.03 }, - "limit": { "context": 272000, "output": 128000 } - }, - "gpt-4-turbo-vision": { - "id": "gpt-4-turbo-vision", - "name": "GPT-4 Turbo Vision", - "family": "gpt", - "attachment": true, - "reasoning": false, - "tool_call": true, "temperature": true, - "knowledge": "2023-11", - "release_date": "2023-11-06", - "last_updated": "2024-04-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 10, "output": 30 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2025-01-30", + "last_updated": "2025-01-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.99, + "output": 0.99 + } }, - "mistral-large-2411": { - "id": "mistral-large-2411", - "name": "Mistral Large 24.11", - "family": "mistral-large", + "alibaba-qwen3-32b": { + "id": "alibaba-qwen3-32b", + "name": "Qwen3-32B", + "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-09", - "release_date": "2024-11-01", - "last_updated": "2024-11-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 6 }, - "limit": { "context": 128000, "output": 32768 } + "release_date": "2025-04-30", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131000, + "output": 40960 + }, + "cost": { + "input": 0.25, + "output": 0.55 + } }, - "claude-opus-4-6": { - "id": "claude-opus-4-6", - "name": "Claude Opus 4.6", + "anthropic-claude-opus-4.5": { + "id": "anthropic-claude-opus-4.5", + "name": "Claude Opus 4.5", "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-05", - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-03-31", + "release_date": "2025-11-24", + "last_updated": "2025-11-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, "cost": { "input": 5, "output": 25, "cache_read": 0.5, - "cache_write": 6.25, - "context_over_200k": { "input": 10, "output": 37.5, "cache_read": 1, "cache_write": 12.5 } - }, - "limit": { "context": 200000, "output": 128000 }, - "provider": { - "npm": "@ai-sdk/anthropic", - "api": "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + "cache_write": 6.25 } }, - "gpt-4o-mini": { - "id": "gpt-4o-mini", - "name": "GPT-4o mini", - "family": "gpt-mini", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-09", - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.6, "cache_read": 0.08 }, - "limit": { "context": 128000, "output": 16384 } - }, - "gpt-5.1-codex-max": { - "id": "gpt-5.1-codex-max", - "name": "GPT-5.1 Codex Max", - "family": "gpt-codex", + "openai-o1": { + "id": "openai-o1", + "name": "o1", + "family": "o", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2023-09", + "release_date": "2024-12-05", + "last_updated": "2024-12-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 15, + "output": 60, + "cache_read": 7.5 + } }, - "gpt-4.1": { - "id": "gpt-4.1", - "name": "GPT-4.1", - "family": "gpt", + "anthropic-claude-3-opus": { + "id": "anthropic-claude-3-opus", + "name": "Claude 3 Opus", + "family": "claude-opus", "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-05", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2023-08", + "release_date": "2024-02-29", + "last_updated": "2024-02-29", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 200000, + "output": 4096 + }, + "status": "deprecated", + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "codex-mini": { - "id": "codex-mini", - "name": "Codex Mini", - "family": "gpt-codex-mini", + "stable-diffusion-3.5-large": { + "id": "stable-diffusion-3.5-large", + "name": "Stable Diffusion 3.5 Large", + "family": "stable-diffusion", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2024-10-22", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text"], + "output": ["image"] + }, + "open_weights": true, + "limit": { + "context": 256, + "output": 1 + }, + "cost": { + "input": 0.08, + "output": 0 + } + }, + "openai-gpt-5-nano": { + "id": "openai-gpt-5-nano", + "name": "GPT-5 nano", + "family": "gpt-nano", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": false, - "knowledge": "2024-04", - "release_date": "2025-05-16", - "last_updated": "2025-05-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.5, "output": 6, "cache_read": 0.375 }, - "limit": { "context": 200000, "output": 100000 } - }, - "phi-4-mini-reasoning": { - "id": "phi-4-mini-reasoning", - "name": "Phi-4-mini-reasoning", - "family": "phi", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.075, "output": 0.3 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.05, + "output": 0.4, + "cache_read": 0.005 + } }, - "llama-4-scout-17b-16e-instruct": { - "id": "llama-4-scout-17b-16e-instruct", - "name": "Llama 4 Scout 17B 16E Instruct", + "llama-4-maverick": { + "id": "llama-4-maverick", + "name": "Llama 4 Maverick 17B 128E Instruct", "family": "llama", "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, "knowledge": "2024-08", "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "last_updated": "2026-04-30", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 0.78 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 1000000, + "output": 16384 + }, + "cost": { + "input": 0.25, + "output": 0.87 + } }, - "llama-3.2-11b-vision-instruct": { - "id": "llama-3.2-11b-vision-instruct", - "name": "Llama-3.2-11B-Vision-Instruct", - "family": "llama", + "anthropic-claude-4.5-sonnet": { + "id": "anthropic-claude-4.5-sonnet", + "name": "Claude Sonnet 4.5", + "family": "claude-sonnet", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.37, "output": 0.37 }, - "limit": { "context": 128000, "output": 8192 } + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75, + "tiers": [ + { + "input": 6, + "output": 22.5, + "cache_read": 0.3, + "cache_write": 3.75, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 6, + "output": 22.5, + "cache_read": 0.3, + "cache_write": 3.75 + } + } }, - "phi-3-mini-4k-instruct": { - "id": "phi-3-mini-4k-instruct", - "name": "Phi-3-mini-instruct (4k)", - "family": "phi", + "qwen3-embedding-0.6b": { + "id": "qwen3-embedding-0.6b", + "name": "Qwen3 Embedding 0.6B", + "family": "text-embedding", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-04-23", - "last_updated": "2024-04-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-06-03", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.13, "output": 0.52 }, - "limit": { "context": 4096, "output": 1024 } + "limit": { + "context": 8000, + "output": 1024 + }, + "status": "beta", + "cost": { + "input": 0.04, + "output": 0 + } }, - "phi-3-medium-4k-instruct": { - "id": "phi-3-medium-4k-instruct", - "name": "Phi-3-medium-instruct (4k)", - "family": "phi", + "anthropic-claude-4.5-haiku": { + "id": "anthropic-claude-4.5-haiku", + "name": "Claude Haiku 4.5", + "family": "claude-haiku", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 1, + "cache_write": 1.25 + } + }, + "gte-large-en-v1.5": { + "id": "gte-large-en-v1.5", + "name": "GTE Large (v1.5)", + "family": "text-embedding", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-04-23", - "last_updated": "2024-04-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2024-03-27", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.17, "output": 0.68 }, - "limit": { "context": 4096, "output": 1024 } + "limit": { + "context": 8192, + "output": 1024 + }, + "cost": { + "input": 0.09, + "output": 0 + } }, - "model-router": { - "id": "model-router", - "name": "Model Router", - "family": "model-router", + "openai-gpt-4.1": { + "id": "openai-gpt-4.1", + "name": "GPT-4.1", + "family": "gpt", "attachment": true, "reasoning": false, "tool_call": true, - "release_date": "2025-05-19", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.14, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } }, - "gpt-3.5-turbo-instruct": { - "id": "gpt-3.5-turbo-instruct", - "name": "GPT-3.5 Turbo Instruct", - "family": "gpt", + "anthropic-claude-3.5-haiku": { + "id": "anthropic-claude-3.5-haiku", + "name": "Claude 3.5 Haiku", + "family": "claude-haiku", "attachment": false, "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2021-08", - "release_date": "2023-09-21", - "last_updated": "2023-09-21", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.5, "output": 2 }, - "limit": { "context": 4096, "output": 4096 } - }, - "gpt-5.1-chat": { - "id": "gpt-5.1-chat", - "name": "GPT-5.1 Chat", - "family": "gpt-codex", - "attachment": true, - "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text", "image", "audio"], "output": ["text", "image", "audio"] }, + "temperature": true, + "knowledge": "2024-07", + "release_date": "2024-11-05", + "last_updated": "2024-11-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 200000, + "output": 8192 + }, + "status": "deprecated", + "cost": { + "input": 0.8, + "output": 4, + "cache_read": 0.08, + "cache_write": 1 + } }, - "gpt-5.4": { - "id": "gpt-5.4", - "name": "GPT-5.4", + "openai-gpt-5.2": { + "id": "openai-gpt-5.2", + "name": "GPT-5.2", "family": "gpt", "attachment": true, "reasoning": true, @@ -46947,455 +86598,802 @@ "structured_output": true, "temperature": false, "knowledge": "2025-08-31", - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 15, "cache_read": 0.25 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "o1": { - "id": "o1", - "name": "o1", - "family": "o", + "deepseek-3.2": { + "id": "deepseek-3.2", + "name": "DeepSeek V3.2", + "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2023-09", - "release_date": "2024-12-05", - "last_updated": "2024-12-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 60, "cache_read": 7.5 }, - "limit": { "context": 200000, "output": 100000 } + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2025-12-02", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 64000 + }, + "cost": { + "input": 0.5, + "output": 1.6 + } }, - "o3": { - "id": "o3", - "name": "o3", - "family": "o", - "attachment": true, + "nemotron-3-nano-30b": { + "id": "nemotron-3-nano-30b", + "name": "Nemotron 3 Nano 30B A3B", + "family": "nemotron", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 200000, "output": 100000 } + "temperature": true, + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + } }, - "grok-4-1-fast-non-reasoning": { - "id": "grok-4-1-fast-non-reasoning", - "name": "Grok 4.1 Fast (Non-Reasoning)", - "family": "grok", + "anthropic-claude-opus-4": { + "id": "anthropic-claude-opus-4", + "name": "Claude Opus 4", + "family": "claude-opus", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-06-27", - "last_updated": "2025-06-27", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 }, - "status": "beta" + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "mai-ds-r1": { - "id": "mai-ds-r1", - "name": "MAI-DS-R1", - "family": "mai", + "openai-gpt-oss-20b": { + "id": "openai-gpt-oss-20b", + "name": "gpt-oss-20b", + "family": "gpt-oss", "attachment": false, "reasoning": true, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, "knowledge": "2024-06", - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.35, "output": 5.4 }, - "limit": { "context": 128000, "output": 8192 } + "release_date": "2025-08-05", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.05, + "output": 0.45 + } }, - "mistral-small-2503": { - "id": "mistral-small-2503", - "name": "Mistral Small 3.1", - "family": "mistral-small", - "attachment": true, + "qwen3-coder-flash": { + "id": "qwen3-coder-flash", + "name": "Qwen3 Coder Flash", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-09", - "release_date": "2025-03-01", - "last_updated": "2025-03-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 128000, "output": 32768 } + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.45, + "output": 1.7 + } }, - "gpt-5-chat": { - "id": "gpt-5-chat", - "name": "GPT-5 Chat", - "family": "gpt-codex", - "attachment": true, + "openai-o3-mini": { + "id": "openai-o3-mini", + "name": "o3-mini", + "family": "o-mini", + "attachment": false, "reasoning": true, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": false, - "knowledge": "2024-10-24", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-05", + "release_date": "2024-12-20", + "last_updated": "2025-01-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.13 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.55 + } }, - "cohere-command-r-plus-08-2024": { - "id": "cohere-command-r-plus-08-2024", - "name": "Command R+", - "family": "command-r", + "openai-gpt-oss-120b": { + "id": "openai-gpt-oss-120b", + "name": "gpt-oss-120b", + "family": "gpt-oss", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2024-06-01", - "release_date": "2024-08-30", - "last_updated": "2024-08-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 128000, "output": 4000 } - }, - "meta-llama-3-70b-instruct": { - "id": "meta-llama-3-70b-instruct", - "name": "Meta-Llama-3-70B-Instruct", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-04-18", - "last_updated": "2024-04-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2.68, "output": 3.54 }, - "limit": { "context": 8192, "output": 2048 } - }, - "phi-3.5-mini-instruct": { - "id": "phi-3.5-mini-instruct", - "name": "Phi-3.5-mini-instruct", - "family": "phi", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-08-20", - "last_updated": "2024-08-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-06", + "release_date": "2025-08-05", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.13, "output": 0.52 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.1, + "output": 0.7 + } }, - "meta-llama-3-8b-instruct": { - "id": "meta-llama-3-8b-instruct", - "name": "Meta-Llama-3-8B-Instruct", - "family": "llama", - "attachment": false, + "gemma-4-31B-it": { + "id": "gemma-4-31B-it", + "name": "Gemma 4 31B", + "family": "gemma", + "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-04-18", - "last_updated": "2024-04-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-22", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 0.61 }, - "limit": { "context": 8192, "output": 2048 } + "limit": { + "context": 256000, + "output": 8192 + }, + "cost": { + "input": 0.18, + "output": 0.5 + } }, - "grok-3-mini": { - "id": "grok-3-mini", - "name": "Grok 3 Mini", - "family": "grok", - "attachment": false, + "nemotron-nano-12b-v2-vl": { + "id": "nemotron-nano-12b-v2-vl", + "name": "Nemotron Nano 12B v2 VL", + "family": "nemotron", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 0.5, "reasoning": 0.5, "cache_read": 0.075 }, - "limit": { "context": 131072, "output": 8192 } + "knowledge": "2024-10", + "release_date": "2025-12-01", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.2, + "output": 0.6 + } }, - "claude-opus-4-1": { - "id": "claude-opus-4-1", + "anthropic-claude-4.1-opus": { + "id": "anthropic-claude-4.1-opus", "name": "Claude Opus 4.1", "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, "knowledge": "2025-03-31", - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 }, - "provider": { - "npm": "@ai-sdk/anthropic", - "api": "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 } }, - "gpt-5.4-nano": { - "id": "gpt-5.4-nano", - "name": "GPT-5.4 Nano", - "family": "gpt-nano", + "kimi-k2.6": { + "id": "kimi-k2.6", + "name": "Kimi K2.6", + "family": "kimi", "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-17", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 1.25, "cache_read": 0.02 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "knowledge": "2025-01", + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.95, + "output": 4 + } }, - "gpt-4-32k": { - "id": "gpt-4-32k", - "name": "GPT-4 32K", - "family": "gpt", - "attachment": false, + "openai-gpt-image-2": { + "id": "openai-gpt-image-2", + "name": "GPT Image 2", + "family": "gpt-image", + "attachment": true, "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2025-04-24", + "last_updated": "2025-04-24", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, + "open_weights": false, + "limit": { + "context": 0, + "output": 0 + } + }, + "anthropic-claude-4.6-sonnet": { + "id": "anthropic-claude-4.6-sonnet", + "name": "Claude Sonnet 4.6", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2023-11", - "release_date": "2023-03-14", - "last_updated": "2023-03-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 60, "output": 120 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75, + "tiers": [ + { + "input": 6, + "output": 22.5, + "cache_read": 0.3, + "cache_write": 3.75, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 6, + "output": 22.5, + "cache_read": 0.3, + "cache_write": 3.75 + } + } }, - "mistral-medium-2505": { - "id": "mistral-medium-2505", - "name": "Mistral Medium 3", - "family": "mistral-medium", + "openai-gpt-5-mini": { + "id": "openai-gpt-5-mini", + "name": "GPT-5 mini", + "family": "gpt-mini", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-05", - "release_date": "2025-05-07", - "last_updated": "2025-05-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.025 + } }, - "gpt-4": { - "id": "gpt-4", - "name": "GPT-4", - "family": "gpt", - "attachment": false, - "reasoning": false, + "anthropic-claude-haiku-4.5": { + "id": "anthropic-claude-haiku-4.5", + "name": "Claude Haiku 4.5", + "family": "claude-haiku", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2023-11", - "release_date": "2023-03-14", - "last_updated": "2023-03-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 60, "output": 120 }, - "limit": { "context": 8192, "output": 8192 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 1, + "cache_write": 1.25 + } }, - "gpt-5.3-chat": { - "id": "gpt-5.3-chat", - "name": "GPT-5.3 Chat", - "family": "gpt-codex", - "attachment": true, + "deepseek-v4-pro": { + "id": "deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-03", - "last_updated": "2026-03-03", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 128000, "output": 16384 } + "temperature": true, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 393216 + }, + "cost": { + "input": 1.74, + "output": 3.48 + } }, - "llama-3.2-90b-vision-instruct": { - "id": "llama-3.2-90b-vision-instruct", - "name": "Llama-3.2-90B-Vision-Instruct", - "family": "llama", + "ministral-3-8b-instruct-2512": { + "id": "ministral-3-8b-instruct-2512", + "name": "Ministral 3 8B", + "family": "ministral", "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-12-15", + "last_updated": "2025-12-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 2.04, "output": 2.04 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 262144, + "output": 262144 + } }, - "deepseek-v3.2-speciale": { - "id": "deepseek-v3.2-speciale", - "name": "DeepSeek-V3.2-Speciale", - "family": "deepseek", + "minimax-m2.5": { + "id": "minimax-m2.5", + "name": "MiniMax M2.5", + "family": "minimax-m2.5", "attachment": false, "reasoning": true, - "tool_call": false, + "tool_call": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-08", + "release_date": "2026-02-12", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.58, "output": 1.68 }, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 204800, + "output": 128000 + }, + "status": "beta", + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "text-embedding-ada-002": { - "id": "text-embedding-ada-002", - "name": "text-embedding-ada-002", - "family": "text-embedding", - "attachment": false, + "openai-gpt-image-1": { + "id": "openai-gpt-image-1", + "name": "GPT Image 1", + "family": "gpt-image", + "attachment": true, "reasoning": false, "tool_call": false, - "release_date": "2022-12-15", - "last_updated": "2022-12-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-04-24", + "last_updated": "2025-04-24", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, + "open_weights": false, + "limit": { + "context": 0, + "output": 0 + }, + "cost": { + "input": 5, + "output": 40, + "cache_read": 1.25 + } + }, + "openai-gpt-5.5": { + "id": "openai-gpt-5.5", + "name": "GPT-5.5", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-12-01", + "release_date": "2026-04-23", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0 }, - "limit": { "context": 8192, "output": 1536 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5, + "tiers": [ + { + "input": 10, + "output": 45, + "cache_read": 1, + "tier": { + "type": "context", + "size": 272000 + } + } + ], + "context_over_200k": { + "input": 10, + "output": 45, + "cache_read": 1 + } + } }, - "ministral-3b": { - "id": "ministral-3b", - "name": "Ministral 3B", - "family": "ministral", + "nvidia-nemotron-3-super-120b": { + "id": "nvidia-nemotron-3-super-120b", + "name": "Nemotron-3-Super-120B", + "family": "nemotron", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-03", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2026-02", + "release_date": "2026-03-11", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.04, "output": 0.04 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 256000, + "output": 32768 + }, + "status": "beta", + "cost": { + "input": 0.3, + "output": 0.65 + } }, - "mistral-nemo": { - "id": "mistral-nemo", - "name": "Mistral Nemo", - "family": "mistral-nemo", + "openai-gpt-5.4-pro": { + "id": "openai-gpt-5.4-pro", + "name": "GPT-5.4 pro", + "family": "gpt-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 30, + "output": 180 + } + }, + "all-mini-lm-l6-v2": { + "id": "all-mini-lm-l6-v2", + "name": "All-MiniLM-L6-v2", + "family": "text-embedding", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-07", - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "temperature": false, + "release_date": "2021-08-30", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256, + "output": 384 + }, + "cost": { + "input": 0.009, + "output": 0 + } + }, + "bge-m3": { + "id": "bge-m3", + "name": "BGE M3", + "family": "bge", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2024-01-30", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.15 }, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 8192, + "output": 1024 + }, + "cost": { + "input": 0.02, + "output": 0 + } }, - "gpt-5.2-chat": { - "id": "gpt-5.2-chat", - "name": "GPT-5.2 Chat", + "openai-gpt-5.1-codex-max": { + "id": "openai-gpt-5.1-codex-max", + "name": "GPT-5.1 Codex Max", "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "gpt-5.2": { - "id": "gpt-5.2", - "name": "GPT-5.2", - "family": "gpt", + "anthropic-claude-opus-4.6": { + "id": "anthropic-claude-opus-4.6", + "name": "Claude Opus 4.6", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25, + "tiers": [ + { + "input": 10, + "output": 37.5, + "cache_read": 0.5, + "cache_write": 6.25, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 10, + "output": 37.5, + "cache_read": 0.5, + "cache_write": 6.25 + } + } }, - "gpt-3.5-turbo-0301": { - "id": "gpt-3.5-turbo-0301", - "name": "GPT-3.5 Turbo 0301", + "openai-gpt-4o": { + "id": "openai-gpt-4o", + "name": "GPT-4o", "family": "gpt", - "attachment": false, + "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2021-08", - "release_date": "2023-03-01", - "last_updated": "2023-03-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-09", + "release_date": "2024-05-13", + "last_updated": "2024-08-06", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.5, "output": 2 }, - "limit": { "context": 4096, "output": 4096 } - }, - "phi-4": { - "id": "phi-4", - "name": "Phi-4", - "family": "phi", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.125, "output": 0.5 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10, + "cache_read": 1.25 + } }, - "grok-4-1-fast-reasoning": { - "id": "grok-4-1-fast-reasoning", - "name": "Grok 4.1 Fast (Reasoning)", - "family": "grok", + "openai-gpt-5.4-mini": { + "id": "openai-gpt-5.4-mini", + "name": "GPT-5.4 mini", + "family": "gpt-mini", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2025-06-27", - "last_updated": "2025-06-27", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 }, - "status": "beta" + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.75, + "output": 4.5, + "cache_read": 0.075 + } }, - "gpt-5.1": { - "id": "gpt-5.1", - "name": "GPT-5.1", + "openai-gpt-5": { + "id": "openai-gpt-5", + "name": "GPT-5", "family": "gpt", "attachment": true, "reasoning": true, @@ -47403,5511 +87401,8349 @@ "structured_output": true, "temperature": false, "knowledge": "2024-09-30", - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text", "image", "audio"], "output": ["text", "image", "audio"] }, + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 272000, "output": 128000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "deepseek-v3.1": { - "id": "deepseek-v3.1", - "name": "DeepSeek-V3.1", - "family": "deepseek", + "arcee-trinity-large-thinking": { + "id": "arcee-trinity-large-thinking", + "name": "Trinity Large Thinking", + "family": "trinity", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-08-21", - "last_updated": "2025-08-21", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-02", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.56, "output": 1.68 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 256000, + "output": 128000 + }, + "status": "beta", + "cost": { + "input": 0.25, + "output": 0.9, + "cache_read": 0.06 + } }, - "cohere-command-r-08-2024": { - "id": "cohere-command-r-08-2024", - "name": "Command R", - "family": "command-r", + "mistral-nemo-instruct-2407": { + "id": "mistral-nemo-instruct-2407", + "name": "Mistral Nemo Instruct", + "family": "mistral", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-06-01", - "release_date": "2024-08-30", - "last_updated": "2024-08-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 128000, "output": 4000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "status": "deprecated", + "cost": { + "input": 0.3, + "output": 0.3 + } }, - "gpt-4.1-mini": { - "id": "gpt-4.1-mini", - "name": "GPT-4.1 mini", - "family": "gpt-mini", - "attachment": true, + "deepseek-v3": { + "id": "deepseek-v3", + "name": "DeepSeek V3", + "family": "deepseek", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-05", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 1.6, "cache_read": 0.1 }, - "limit": { "context": 1047576, "output": 32768 } + "knowledge": "2024-07", + "release_date": "2024-12-26", + "last_updated": "2025-03-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 131072 + } }, - "phi-3-medium-128k-instruct": { - "id": "phi-3-medium-128k-instruct", - "name": "Phi-3-medium-instruct (128k)", - "family": "phi", + "bge-reranker-v2-m3": { + "id": "bge-reranker-v2-m3", + "name": "BGE Reranker v2 M3", + "family": "bge", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-04-23", - "last_updated": "2024-04-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2024-03-12", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.17, "output": 0.68 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 8192, + "output": 1 + }, + "cost": { + "input": 0.01, + "output": 0 + } }, - "meta-llama-3.1-405b-instruct": { - "id": "meta-llama-3.1-405b-instruct", - "name": "Meta-Llama-3.1-405B-Instruct", - "family": "llama", - "attachment": false, - "reasoning": false, + "anthropic-claude-3.7-sonnet": { + "id": "anthropic-claude-3.7-sonnet", + "name": "Claude 3.7 Sonnet", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 5.33, "output": 16 }, - "limit": { "context": 128000, "output": 32768 } + "knowledge": "2024-11", + "release_date": "2025-02-24", + "last_updated": "2025-02-24", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "status": "deprecated", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "phi-3.5-moe-instruct": { - "id": "phi-3.5-moe-instruct", - "name": "Phi-3.5-MoE-instruct", - "family": "phi", + "qwen3-tts-voicedesign": { + "id": "qwen3-tts-voicedesign", + "name": "Qwen3 TTS VoiceDesign", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-08-20", - "last_updated": "2024-08-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2026-04-21", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text"], + "output": ["audio"] + }, "open_weights": true, - "cost": { "input": 0.16, "output": 0.64 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 32768, + "output": 1 + } }, - "grok-4-fast-reasoning": { - "id": "grok-4-fast-reasoning", - "name": "Grok 4 Fast (Reasoning)", - "family": "grok", + "openai-gpt-image-1.5": { + "id": "openai-gpt-image-1.5", + "name": "GPT Image 1.5", + "family": "gpt-image", "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-09-19", - "last_updated": "2025-09-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2025-11-25", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5, "cache_read": 0.05 }, - "limit": { "context": 2000000, "output": 30000 } + "limit": { + "context": 0, + "output": 0 + }, + "cost": { + "input": 5, + "output": 10, + "cache_read": 1 + } }, - "gpt-5-nano": { - "id": "gpt-5-nano", - "name": "GPT-5 Nano", - "family": "gpt-nano", + "openai-gpt-5.3-codex": { + "id": "openai-gpt-5.3-codex", + "name": "GPT-5.3 Codex", + "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.05, "output": 0.4, "cache_read": 0.01 }, - "limit": { "context": 272000, "output": 128000 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "cohere-command-a": { - "id": "cohere-command-a", - "name": "Command A", - "family": "command-a", - "attachment": false, - "reasoning": true, + "anthropic-claude-3.5-sonnet": { + "id": "anthropic-claude-3.5-sonnet", + "name": "Claude 3.5 Sonnet", + "family": "claude-sonnet", + "attachment": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-06-01", - "release_date": "2025-03-13", - "last_updated": "2025-03-13", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 256000, "output": 8000 } + "knowledge": "2024-04", + "release_date": "2024-06-20", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "status": "deprecated", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "grok-3": { - "id": "grok-3", - "name": "Grok 3", - "family": "grok", + "fal-ai/fast-sdxl": { + "id": "fal-ai/fast-sdxl", + "name": "Fast SDXL", + "family": "stable-diffusion", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-11", - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.75 }, - "limit": { "context": 131072, "output": 8192 } + "tool_call": false, + "temperature": false, + "release_date": "2023-07-26", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text"], + "output": ["image"] + }, + "open_weights": true, + "limit": { + "context": 0, + "output": 0 + } }, - "phi-4-reasoning-plus": { - "id": "phi-4-reasoning-plus", - "name": "Phi-4-reasoning-plus", - "family": "phi", + "fal-ai/flux/schnell": { + "id": "fal-ai/flux/schnell", + "name": "FLUX.1 [schnell]", + "family": "flux", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": false, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2024-08-01", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text"], + "output": ["image"] + }, "open_weights": true, - "cost": { "input": 0.125, "output": 0.5 }, - "limit": { "context": 32000, "output": 4096 } + "limit": { + "context": 0, + "output": 0 + } }, - "cohere-embed-v-4-0": { - "id": "cohere-embed-v-4-0", - "name": "Embed v4", - "family": "cohere-embed", - "attachment": true, + "fal-ai/elevenlabs/tts/multilingual-v2": { + "id": "fal-ai/elevenlabs/tts/multilingual-v2", + "name": "ElevenLabs Multilingual TTS v2", + "family": "elevenlabs", + "attachment": false, "reasoning": false, "tool_call": false, "temperature": false, - "release_date": "2025-04-15", - "last_updated": "2025-04-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2023-08-22", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text"], + "output": ["audio"] + }, + "open_weights": false, + "limit": { + "context": 0, + "output": 0 + } + }, + "fal-ai/stable-audio-25/text-to-audio": { + "id": "fal-ai/stable-audio-25/text-to-audio", + "name": "Stable Audio 2.5 (Text-to-Audio)", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2025-10-08", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text"], + "output": ["audio"] + }, + "open_weights": false, + "limit": { + "context": 0, + "output": 0 + } + } + } + }, + "vultr": { + "id": "vultr", + "env": ["VULTR_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.vultrinference.com/v1", + "name": "Vultr", + "doc": "https://api.vultrinference.com/", + "models": { + "MiniMax-M2.5": { + "id": "MiniMax-M2.5", + "name": "MiniMax M2.5", + "family": "minimax", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-09", + "release_date": "2025-02-11", + "last_updated": "2025-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.12, "output": 0 }, - "limit": { "context": 128000, "output": 1536 } + "limit": { + "context": 194000, + "output": 4096 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "llama-4-maverick-17b-128e-instruct-fp8": { - "id": "llama-4-maverick-17b-128e-instruct-fp8", - "name": "Llama 4 Maverick 17B 128E Instruct FP8", - "family": "llama", - "attachment": true, + "GLM-5-FP8": { + "id": "GLM-5-FP8", + "name": "GLM 5 FP8", + "family": "glm", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.25, "output": 1 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 0.85, + "output": 3.1 + } }, - "deepseek-r1-0528": { - "id": "deepseek-r1-0528", - "name": "DeepSeek-R1-0528", - "family": "deepseek-thinking", + "DeepSeek-V3.2": { + "id": "DeepSeek-V3.2", + "name": "DeepSeek V3.2", + "family": "deepseek", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, "knowledge": "2024-07", - "release_date": "2025-05-28", - "last_updated": "2025-05-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1.35, "output": 5.4 }, - "limit": { "context": 163840, "output": 163840 } + "limit": { + "context": 127000, + "output": 4096 + }, + "cost": { + "input": 0.55, + "output": 1.65 + } }, - "phi-3-small-128k-instruct": { - "id": "phi-3-small-128k-instruct", - "name": "Phi-3-small-instruct (128k)", - "family": "phi", + "gpt-oss-120b": { + "id": "gpt-oss-120b", + "name": "GPT OSS 120B", + "family": "gpt-oss", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-04-23", - "last_updated": "2024-04-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-06", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 129000, + "output": 4096 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "meta-llama-3.1-70b-instruct": { - "id": "meta-llama-3.1-70b-instruct", - "name": "Meta-Llama-3.1-70B-Instruct", - "family": "llama", + "Kimi-K2.5": { + "id": "Kimi-K2.5", + "name": "Kimi K2 Instruct", + "family": "kimi", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 2.68, "output": 3.54 }, - "limit": { "context": 128000, "output": 32768 } - }, - "gpt-4.1-nano": { - "id": "gpt-4.1-nano", - "name": "GPT-4.1 nano", - "family": "gpt-nano", - "attachment": true, + "limit": { + "context": 254000, + "output": 32768 + }, + "cost": { + "input": 0.55, + "output": 2.75 + } + } + } + }, + "alibaba-coding-plan-cn": { + "id": "alibaba-coding-plan-cn", + "env": ["ALIBABA_CODING_PLAN_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://coding.dashscope.aliyuncs.com/v1", + "name": "Alibaba Coding Plan (China)", + "doc": "https://help.aliyun.com/zh/model-studio/coding-plan", + "models": { + "qwen3-coder-plus": { + "id": "qwen3-coder-plus", + "name": "Qwen3 Coder Plus", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-05", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.03 }, - "limit": { "context": 1047576, "output": 32768 } + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "claude-sonnet-4-5": { - "id": "claude-sonnet-4-5", - "name": "Claude Sonnet 4.5", - "family": "claude-sonnet", + "kimi-k2.5": { + "id": "kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } + }, + "glm-4.7": { + "id": "glm-4.7", + "name": "GLM-4.7", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 }, - "provider": { - "npm": "@ai-sdk/anthropic", - "api": "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 } }, - "o1-preview": { - "id": "o1-preview", - "name": "o1-preview", - "family": "o", + "glm-5": { + "id": "glm-5", + "name": "GLM-5", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2023-09", - "release_date": "2024-09-12", - "last_updated": "2024-09-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 16.5, "output": 66, "cache_read": 8.25 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 202752, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "deepseek-v3.2": { - "id": "deepseek-v3.2", - "name": "DeepSeek-V3.2", - "family": "deepseek", + "MiniMax-M2.5": { + "id": "MiniMax-M2.5", + "name": "MiniMax-M2.5", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.58, "output": 1.68 }, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 196608, + "output": 24576 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "gpt-3.5-turbo-0125": { - "id": "gpt-3.5-turbo-0125", - "name": "GPT-3.5 Turbo 0125", - "family": "gpt", + "qwen3.6-plus": { + "id": "qwen3.6-plus", + "name": "Qwen3.6 Plus", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "knowledge": "2021-08", - "release_date": "2024-01-25", - "last_updated": "2024-01-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 1.5 }, - "limit": { "context": 16384, "output": 16384 } + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "text-embedding-3-small": { - "id": "text-embedding-3-small", - "name": "text-embedding-3-small", - "family": "text-embedding", + "qwen3-max-2026-01-23": { + "id": "qwen3-max-2026-01-23", + "name": "Qwen3 Max", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, - "release_date": "2024-01-25", - "last_updated": "2024-01-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.02, "output": 0 }, - "limit": { "context": 8191, "output": 1536 } - }, - "gpt-5-codex": { - "id": "gpt-5-codex", - "name": "GPT-5-Codex", - "family": "gpt-codex", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.13 }, - "limit": { "context": 400000, "output": 128000 } - }, - "o4-mini": { - "id": "o4-mini", - "name": "o4-mini", - "family": "o-mini", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.28 }, - "limit": { "context": 200000, "output": 100000 } - }, - "claude-haiku-4-5": { - "id": "claude-haiku-4-5", - "name": "Claude Haiku 4.5", - "family": "claude-haiku", - "attachment": true, - "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-02-31", - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2026-01-23", + "last_updated": "2026-01-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 64000 }, - "provider": { - "npm": "@ai-sdk/anthropic", - "api": "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 } }, - "gpt-5.1-codex": { - "id": "gpt-5.1-codex", - "name": "GPT-5.1 Codex", - "family": "gpt-codex", + "qwen3-coder-next": { + "id": "qwen3-coder-next", + "name": "Qwen3 Coder Next", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text", "image", "audio"], "output": ["text", "image", "audio"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } + "temperature": true, + "release_date": "2026-02-03", + "last_updated": "2026-02-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "o1-mini": { - "id": "o1-mini", - "name": "o1-mini", - "family": "o-mini", + "qwen3.5-plus": { + "id": "qwen3.5-plus", + "name": "Qwen3.5 Plus", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2023-09", - "release_date": "2024-09-12", - "last_updated": "2024-09-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-02-16", + "last_updated": "2026-02-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4, "cache_read": 0.55 }, - "limit": { "context": 128000, "output": 65536 } + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } } } }, - "aihubmix": { - "id": "aihubmix", - "env": ["AIHUBMIX_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://aihubmix.com/v1", - "name": "AIHubMix", - "doc": "https://docs.aihubmix.com", + "mistral": { + "id": "mistral", + "env": ["MISTRAL_API_KEY"], + "npm": "@ai-sdk/mistral", + "name": "Mistral", + "doc": "https://docs.mistral.ai/getting-started/models/", "models": { - "gpt-5.2-codex": { - "id": "gpt-5.2-codex", - "name": "GPT-5.2-Codex", - "family": "gpt-codex", + "mistral-small-latest": { + "id": "mistral-small-latest", + "name": "Mistral Small (latest)", + "family": "mistral-small", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-08-31", - "release_date": "2026-01-14", - "last_updated": "2026-01-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "output": 128000 } + "knowledge": "2025-06", + "release_date": "2026-03-16", + "last_updated": "2026-03-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "gemini-3-pro-preview-search": { - "id": "gemini-3-pro-preview-search", - "name": "Gemini 3 Pro Preview Search", - "family": "gemini-pro", - "attachment": true, - "reasoning": true, + "mistral-nemo": { + "id": "mistral-nemo", + "name": "Mistral Nemo", + "family": "mistral-nemo", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-11", - "release_date": "2025-11-19", - "last_updated": "2025-11-19", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 12, "cache_read": 0.5 }, - "limit": { "context": 1000000, "output": 65000 } + "knowledge": "2024-07", + "release_date": "2024-07-01", + "last_updated": "2024-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.15, + "output": 0.15 + } }, - "gpt-5.1-codex-mini": { - "id": "gpt-5.1-codex-mini", - "name": "GPT-5.1 Codex Mini", - "family": "gpt-codex", + "mistral-large-2512": { + "id": "mistral-large-2512", + "name": "Mistral Large 3", + "family": "mistral-large", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-11", - "release_date": "2025-11-15", - "last_updated": "2025-11-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.03 }, - "limit": { "context": 400000, "output": 128000 } + "knowledge": "2024-11", + "release_date": "2024-11-01", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.5, + "output": 1.5 + } }, - "qwen3-235b-a22b-instruct-2507": { - "id": "qwen3-235b-a22b-instruct-2507", - "name": "Qwen3 235B A22B Instruct 2507", - "family": "qwen", + "labs-devstral-small-2512": { + "id": "labs-devstral-small-2512", + "name": "Devstral Small 2", + "family": "devstral", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-30", - "last_updated": "2025-07-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-12", + "release_date": "2025-12-09", + "last_updated": "2025-12-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.28, "output": 1.12 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "qwen3.5-plus": { - "id": "qwen3.5-plus", - "name": "Qwen 3.5 Plus", - "family": "qwen", + "devstral-2512": { + "id": "devstral-2512", + "name": "Devstral 2", + "family": "devstral", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2025-12", + "release_date": "2025-12-09", + "last_updated": "2025-12-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.4, + "output": 2 + } + }, + "magistral-medium-latest": { + "id": "magistral-medium-latest", + "name": "Magistral Medium (latest)", + "family": "magistral-medium", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-02-16", - "last_updated": "2026-02-16", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.11, "output": 0.66 }, - "limit": { "context": 1000000, "output": 65536 } + "knowledge": "2025-06", + "release_date": "2025-03-17", + "last_updated": "2025-03-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2, + "output": 5 + } }, - "deepseek-v3.2-fast": { - "id": "deepseek-v3.2-fast", - "name": "DeepSeek-V3.2-Fast", - "family": "deepseek", + "open-mixtral-8x7b": { + "id": "open-mixtral-8x7b", + "name": "Mixtral 8x7B", + "family": "mixtral", "attachment": false, "reasoning": false, - "tool_call": false, - "knowledge": "2024-07", - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-01", + "release_date": "2023-12-11", + "last_updated": "2023-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1.1, "output": 3.29 }, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 32000, + "output": 32000 + }, + "cost": { + "input": 0.7, + "output": 0.7 + } }, - "gpt-5-pro": { - "id": "gpt-5-pro", - "name": "GPT-5-Pro", - "family": "gpt-pro", + "pixtral-large-latest": { + "id": "pixtral-large-latest", + "name": "Pixtral Large (latest)", + "family": "pixtral", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-09-30", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 7, "output": 28, "cache_read": 3.5 }, - "limit": { "context": 400000, "output": 128000 } + "knowledge": "2024-11", + "release_date": "2024-11-01", + "last_updated": "2024-11-04", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 2, + "output": 6 + } }, - "qwen3-coder-next": { - "id": "qwen3-coder-next", - "name": "Qwen3 Coder Next", - "family": "qwen", + "mistral-large-2411": { + "id": "mistral-large-2411", + "name": "Mistral Large 2.1", + "family": "mistral-large", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": false, "temperature": true, - "release_date": "2026-02-04", - "last_updated": "2026-02-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-11", + "release_date": "2024-11-01", + "last_updated": "2024-11-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 2, + "output": 6 + } + }, + "codestral-latest": { + "id": "codestral-latest", + "name": "Codestral (latest)", + "family": "codestral", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2024-05-29", + "last_updated": "2025-01-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.14, "output": 0.55 }, - "limit": { "context": 262144, "input": 262144, "output": 65536 } + "limit": { + "context": 256000, + "output": 4096 + }, + "cost": { + "input": 0.3, + "output": 0.9 + } }, - "gpt-5": { - "id": "gpt-5", - "name": "GPT-5", - "family": "gpt", + "mistral-large-latest": { + "id": "mistral-large-latest", + "name": "Mistral Large (latest)", + "family": "mistral-large", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-09-30", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5, "output": 20, "cache_read": 2.5 }, - "limit": { "context": 400000, "output": 128000 } + "knowledge": "2024-11", + "release_date": "2024-11-01", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.5, + "output": 1.5 + } }, - "claude-opus-4-5": { - "id": "claude-opus-4-5", - "name": "Claude Opus 4.5", - "attachment": true, - "reasoning": true, + "mistral-small-2506": { + "id": "mistral-small-2506", + "name": "Mistral Small 3.2", + "family": "mistral-small", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, "knowledge": "2025-03", - "release_date": "2025-11-25", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 200000, "output": 32000 } + "release_date": "2025-06-20", + "last_updated": "2025-06-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } }, - "kimi-k2.5": { - "id": "kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi", + "pixtral-12b": { + "id": "pixtral-12b", + "name": "Pixtral 12B", + "family": "pixtral", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2025-07", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2024-09", + "release_date": "2024-09-01", + "last_updated": "2024-09-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 3, "cache_read": 0.1 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.15, + "output": 0.15 + } }, - "gemini-3-pro-preview": { - "id": "gemini-3-pro-preview", - "name": "Gemini 3 Pro Preview", - "family": "gemini-pro", - "attachment": true, - "reasoning": true, + "ministral-8b-latest": { + "id": "ministral-8b-latest", + "name": "Ministral 8B (latest)", + "family": "ministral", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-11", - "release_date": "2025-11-19", - "last_updated": "2025-11-19", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2024-10-01", + "last_updated": "2024-10-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } + }, + "mistral-embed": { + "id": "mistral-embed", + "name": "Mistral Embed", + "family": "mistral-embed", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2023-12-11", + "last_updated": "2023-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 12, "cache_read": 0.5 }, - "limit": { "context": 1000000, "output": 65000 } + "limit": { + "context": 8000, + "output": 3072 + }, + "cost": { + "input": 0.1, + "output": 0 + } }, - "coding-glm-5-free": { - "id": "coding-glm-5-free", - "name": "Coding-GLM-5-Free", - "family": "glm", + "magistral-small": { + "id": "magistral-small", + "name": "Magistral Small", + "family": "magistral-small", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 204800, "output": 131072 } + "knowledge": "2025-06", + "release_date": "2025-03-17", + "last_updated": "2025-03-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.5, + "output": 1.5 + } }, - "gpt-4o": { - "id": "gpt-4o", - "name": "GPT-4o", - "family": "gpt", + "mistral-small-2603": { + "id": "mistral-small-2603", + "name": "Mistral Small 4", + "family": "mistral-small", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2023-09", - "release_date": "2024-05-13", - "last_updated": "2024-08-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 10, "cache_read": 1.25 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2025-06", + "release_date": "2026-03-16", + "last_updated": "2026-03-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "gpt-5-mini": { - "id": "gpt-5-mini", - "name": "GPT-5-Mini", - "family": "gpt-mini", - "attachment": true, - "reasoning": true, + "ministral-3b-latest": { + "id": "ministral-3b-latest", + "name": "Ministral 3B (latest)", + "family": "ministral", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-09-30", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.5, "output": 6, "cache_read": 0.75 }, - "limit": { "context": 200000, "output": 64000 } + "knowledge": "2024-10", + "release_date": "2024-10-01", + "last_updated": "2024-10-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.04, + "output": 0.04 + } }, - "claude-opus-4-6": { - "id": "claude-opus-4-6", - "name": "Claude Opus 4.6", - "family": "claude-opus", - "attachment": true, - "reasoning": true, + "open-mixtral-8x22b": { + "id": "open-mixtral-8x22b", + "name": "Mixtral 8x22B", + "family": "mixtral", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-05", - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { - "input": 5, - "output": 25, - "cache_read": 0.3, - "cache_write": 3.75, - "context_over_200k": { "input": 6, "output": 22, "cache_read": 0.6, "cache_write": 7.5 } + "knowledge": "2024-04", + "release_date": "2024-04-17", + "last_updated": "2024-04-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 64000, + "output": 64000 }, - "limit": { "context": 200000, "output": 128000 } + "cost": { + "input": 2, + "output": 6 + } }, - "gpt-5.1-codex-max": { - "id": "gpt-5.1-codex-max", - "name": "GPT-5.1-Codex-Max", + "mistral-medium-2604": { + "id": "mistral-medium-2604", + "name": "Mistral Medium 3.5", + "family": "mistral-medium", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } - }, - "claude-sonnet-4-6": { - "id": "claude-sonnet-4-6", - "name": "Claude Sonnet 4.6", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, - "tool_call": true, "temperature": true, - "knowledge": "2025-08", - "release_date": "2026-02-17", - "last_updated": "2026-02-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75, - "context_over_200k": { "input": 6, "output": 22.5, "cache_read": 0.6, "cache_write": 7.5 } + "release_date": "2026-04-29", + "last_updated": "2026-04-29", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 }, - "limit": { "context": 200000, "output": 64000 } + "cost": { + "input": 1.5, + "output": 7.5 + } }, - "gpt-4.1": { - "id": "gpt-4.1", - "name": "GPT-4.1", - "family": "gpt", - "attachment": true, + "devstral-small-2505": { + "id": "devstral-small-2505", + "name": "Devstral Small 2505", + "family": "devstral", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 1047576, "output": 32768 } + "knowledge": "2025-05", + "release_date": "2025-05-07", + "last_updated": "2025-05-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } }, - "deepseek-v3.2-think": { - "id": "deepseek-v3.2-think", - "name": "DeepSeek-V3.2-Think", - "family": "deepseek", + "devstral-medium-2507": { + "id": "devstral-medium-2507", + "name": "Devstral Medium", + "family": "devstral", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2025-07-10", + "last_updated": "2025-07-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 0.45 }, - "limit": { "context": 131000, "output": 64000 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.4, + "output": 2 + } }, - "minimax-m2.1": { - "id": "minimax-m2.1", - "name": "MiniMax M2.1", - "family": "minimax", + "open-mistral-7b": { + "id": "open-mistral-7b", + "name": "Mistral 7B", + "family": "mistral", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_details" }, "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2023-09-27", + "last_updated": "2023-09-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.29, "output": 1.15 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 8000, + "output": 8000 + }, + "cost": { + "input": 0.25, + "output": 0.25 + } }, - "claude-sonnet-4-6-think": { - "id": "claude-sonnet-4-6-think", - "name": "Claude Sonnet 4.6 Think", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, + "devstral-medium-latest": { + "id": "devstral-medium-latest", + "name": "Devstral 2 (latest)", + "family": "devstral", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-08", - "release_date": "2026-02-17", - "last_updated": "2026-02-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75, - "context_over_200k": { "input": 6, "output": 22.5, "cache_read": 0.6, "cache_write": 7.5 } + "knowledge": "2025-12", + "release_date": "2025-12-02", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text"], + "output": ["text"] }, - "limit": { "context": 200000, "output": 64000 } + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.4, + "output": 2 + } }, - "claude-opus-4-6-think": { - "id": "claude-opus-4-6-think", - "name": "Claude Opus 4.6 Think", - "family": "claude-opus", + "mistral-medium-2505": { + "id": "mistral-medium-2505", + "name": "Mistral Medium 3", + "family": "mistral-medium", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, "knowledge": "2025-05", - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-05-07", + "last_updated": "2025-05-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { - "input": 5, - "output": 25, - "cache_read": 0.3, - "cache_write": 3.75, - "context_over_200k": { "input": 6, "output": 22, "cache_read": 0.6, "cache_write": 7.5 } + "limit": { + "context": 131072, + "output": 131072 }, - "limit": { "context": 200000, "output": 128000 } + "cost": { + "input": 0.4, + "output": 2 + } }, - "coding-glm-4.7-free": { - "id": "coding-glm-4.7-free", - "name": "Coding GLM 4.7 Free", - "family": "glm", + "devstral-small-2507": { + "id": "devstral-small-2507", + "name": "Devstral Small", + "family": "devstral", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_details" }, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2025-07-10", + "last_updated": "2025-07-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } }, - "claude-opus-4-1": { - "id": "claude-opus-4-1", - "name": "Claude Opus 4.1", - "family": "claude-opus", + "mistral-medium-2508": { + "id": "mistral-medium-2508", + "name": "Mistral Medium 3.1", + "family": "mistral-medium", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-05", + "release_date": "2025-08-12", + "last_updated": "2025-08-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 16.5, "output": 82.5, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.4, + "output": 2 + } }, - "glm-4.6v": { - "id": "glm-4.6v", - "name": "GLM-4.6V", - "family": "glm", + "mistral-medium-latest": { + "id": "mistral-medium-latest", + "name": "Mistral Medium (latest)", + "family": "mistral-medium", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-08", - "last_updated": "2025-12-08", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "release_date": "2026-04-29", + "last_updated": "2026-04-29", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.14, "output": 0.41 }, - "limit": { "context": 128000, "output": 32768 } - }, - "qwen3-coder-480b-a35b-instruct": { - "id": "qwen3-coder-480b-a35b-instruct", - "name": "Qwen3 Coder 480B A35B Instruct", - "family": "qwen", + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 1.5, + "output": 7.5 + } + } + } + }, + "ovhcloud": { + "id": "ovhcloud", + "env": ["OVHCLOUD_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://oai.endpoints.kepler.ai.cloud.ovh.net/v1", + "name": "OVHcloud AI Endpoints", + "doc": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog//", + "models": { + "meta-llama-3_3-70b-instruct": { + "id": "meta-llama-3_3-70b-instruct", + "name": "Meta-Llama-3_3-70B-Instruct", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-08-01", - "last_updated": "2025-08-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.82, "output": 3.29 }, - "limit": { "context": 262144, "output": 131000 } - }, - "gemini-2.5-pro": { - "id": "gemini-2.5-pro", - "name": "Gemini 2.5 Pro", - "family": "gemini-pro", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 5, "cache_read": 0.31 }, - "limit": { "context": 2000000, "output": 65000 } + "release_date": "2025-04-01", + "last_updated": "2025-04-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.74, + "output": 0.74 + } }, - "gemini-2.5-flash": { - "id": "gemini-2.5-flash", - "name": "Gemini 2.5 Flash", - "family": "gemini-flash", - "attachment": true, + "mistral-7b-instruct-v0.3": { + "id": "mistral-7b-instruct-v0.3", + "name": "Mistral-7B-Instruct-v0.3", + "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.075, "output": 0.3, "cache_read": 0.02 }, - "limit": { "context": 1000000, "output": 65000 } + "release_date": "2025-04-01", + "last_updated": "2025-04-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 65536, + "output": 65536 + }, + "cost": { + "input": 0.11, + "output": 0.11 + } }, - "qwen3-235b-a22b-thinking-2507": { - "id": "qwen3-235b-a22b-thinking-2507", - "name": "Qwen3 235B A22B Thinking 2507", - "family": "qwen", + "qwen3-32b": { + "id": "qwen3-32b", + "name": "Qwen3-32B", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-30", - "last_updated": "2025-07-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-07-16", + "last_updated": "2025-07-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.28, "output": 2.8 }, - "limit": { "context": 262144, "output": 262144 } - }, - "gpt-5.2": { - "id": "gpt-5.2", - "name": "GPT-5.2", - "family": "gpt", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14, "cache_read": 0.175 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.09, + "output": 0.25 + } }, - "gpt-5.1": { - "id": "gpt-5.1", - "name": "GPT-5.1", - "family": "gpt", + "qwen2.5-vl-72b-instruct": { + "id": "qwen2.5-vl-72b-instruct", + "name": "Qwen2.5-VL-72B-Instruct", "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-11", - "release_date": "2025-11-15", - "last_updated": "2025-11-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } - }, - "coding-glm-4.7": { - "id": "coding-glm-4.7", - "name": "Coding-GLM-4.7", - "family": "glm", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_details" }, + "reasoning": false, + "tool_call": false, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-03-31", + "last_updated": "2025-03-31", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.27, "output": 1.1, "cache_read": 0.548 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 1.01, + "output": 1.01 + } }, - "gpt-4.1-mini": { - "id": "gpt-4.1-mini", - "name": "GPT-4.1 mini", - "family": "gpt-mini", - "attachment": true, + "qwen3-coder-30b-a3b-instruct": { + "id": "qwen3-coder-30b-a3b-instruct", + "name": "Qwen3-Coder-30B-A3B-Instruct", + "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 1.6, "cache_read": 0.1 }, - "limit": { "context": 1047576, "output": 32768 } + "release_date": "2025-10-28", + "last_updated": "2025-10-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.07, + "output": 0.26 + } }, - "coding-minimax-m2.1-free": { - "id": "coding-minimax-m2.1-free", - "name": "Coding MiniMax M2.1 Free", - "family": "minimax", + "gpt-oss-20b": { + "id": "gpt-oss-20b", + "name": "gpt-oss-20b", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_details" }, - "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-08-28", + "last_updated": "2025-08-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.05, + "output": 0.18 + } }, - "glm-5": { - "id": "glm-5", - "name": "GLM-5", - "family": "glm", - "attachment": false, - "reasoning": true, + "mistral-small-3.2-24b-instruct-2506": { + "id": "mistral-small-3.2-24b-instruct-2506", + "name": "Mistral-Small-3.2-24B-Instruct-2506", + "attachment": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "structured_output": true, "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.88, "output": 2.82 }, - "limit": { "context": 204800, "output": 131072 } + "release_date": "2025-07-16", + "last_updated": "2025-07-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.1, + "output": 0.31 + } }, - "gpt-5-nano": { - "id": "gpt-5-nano", - "name": "GPT-5-Nano", - "family": "gpt-nano", + "qwen3.5-9b": { + "id": "qwen3.5-9b", + "name": "Qwen3.5-9B", "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-09-30", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 2, "cache_read": 0.25 }, - "limit": { "context": 128000, "output": 16384 } + "release_date": "2026-02-15", + "last_updated": "2026-02-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.1, + "output": 0.15 + } }, - "glm-4.7": { - "id": "glm-4.7", - "name": "GLM-4.7", - "family": "glm", + "gpt-oss-120b": { + "id": "gpt-oss-120b", + "name": "gpt-oss-120b", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_details" }, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "release_date": "2025-08-28", + "last_updated": "2025-08-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.27, "output": 1.1, "cache_read": 0.548 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.09, + "output": 0.47 + } }, - "qwen3-max-2026-01-23": { - "id": "qwen3-max-2026-01-23", - "name": "Qwen3 Max", - "family": "qwen", + "mistral-nemo-instruct-2407": { + "id": "mistral-nemo-instruct-2407", + "name": "Mistral-Nemo-Instruct-2407", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-23", - "last_updated": "2025-09-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.34, "output": 1.37 }, - "limit": { "context": 262144, "output": 65536 } + "release_date": "2024-11-20", + "last_updated": "2024-11-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 65536, + "output": 65536 + }, + "cost": { + "input": 0.14, + "output": 0.14 + } }, - "Kimi-K2-0905": { - "id": "Kimi-K2-0905", - "name": "Kimi K2 0905", - "family": "kimi", + "llama-3.1-8b-instruct": { + "id": "llama-3.1-8b-instruct", + "name": "Llama-3.1-8B-Instruct", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-06-11", + "last_updated": "2025-06-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.55, "output": 2.19 }, - "limit": { "context": 262144, "output": 262144 } - }, - "gpt-4.1-nano": { - "id": "gpt-4.1-nano", - "name": "GPT-4.1 nano", - "family": "gpt-nano", - "attachment": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.11, + "output": 0.11 + } + } + } + }, + "friendli": { + "id": "friendli", + "env": ["FRIENDLI_TOKEN"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.friendli.ai/serverless/v1", + "name": "Friendli", + "doc": "https://friendli.ai/docs/guides/serverless_endpoints/introduction", + "models": { + "Qwen/Qwen3-235B-A22B-Instruct-2507": { + "id": "Qwen/Qwen3-235B-A22B-Instruct-2507", + "name": "Qwen3 235B A22B Instruct 2507", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.03 }, - "limit": { "context": 1047576, "output": 32768 } - }, - "claude-sonnet-4-5": { - "id": "claude-sonnet-4-5", - "name": "Claude Sonnet 4.5", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3.3, "output": 16.5, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "release_date": "2025-07-29", + "last_updated": "2026-01-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.2, + "output": 0.8 + } }, - "deepseek-v3.2": { - "id": "deepseek-v3.2", - "name": "DeepSeek-V3.2", - "family": "deepseek", + "zai-org/GLM-5.1": { + "id": "zai-org/GLM-5.1", + "name": "GLM-5.1", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-07", + "last_updated": "2026-04-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 0.45 }, - "limit": { "context": 131000, "output": 64000 } + "limit": { + "context": 202752, + "output": 202752 + }, + "cost": { + "input": 1.4, + "output": 4.4, + "cache_read": 0.26 + } }, - "gpt-5-codex": { - "id": "gpt-5-codex", - "name": "GPT-5-Codex", - "family": "gpt-codex", + "zai-org/GLM-5": { + "id": "zai-org/GLM-5", + "name": "GLM-5", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.13 }, - "limit": { "context": 400000, "output": 128000 } + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 202752 + }, + "cost": { + "input": 1, + "output": 3.2, + "cache_read": 0.5 + } }, - "o4-mini": { - "id": "o4-mini", - "name": "o4-mini", - "family": "o-mini", + "meta-llama/Llama-3.3-70B-Instruct": { + "id": "meta-llama/Llama-3.3-70B-Instruct", + "name": "Llama 3.3 70B Instruct", + "family": "llama", "attachment": false, - "reasoning": true, - "tool_call": false, - "temperature": false, - "knowledge": "2024-09", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.5, "output": 6, "cache_read": 0.75 }, - "limit": { "context": 200000, "output": 65536 } - }, - "claude-haiku-4-5": { - "id": "claude-haiku-4-5", - "name": "Claude Haiku 4.5", - "family": "claude-haiku", - "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.1, "output": 5.5, "cache_read": 0.11, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 64000 } + "release_date": "2024-08-01", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 0.6 + } }, - "gpt-5.1-codex": { - "id": "gpt-5.1-codex", - "name": "GPT-5.1 Codex", - "family": "gpt-codex", - "attachment": true, - "reasoning": true, + "meta-llama/Llama-3.1-8B-Instruct": { + "id": "meta-llama/Llama-3.1-8B-Instruct", + "name": "Llama 3.1 8B Instruct", + "family": "llama", + "attachment": false, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-11", - "release_date": "2025-11-15", - "last_updated": "2025-11-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.13 }, - "limit": { "context": 400000, "output": 128000 } + "release_date": "2024-08-01", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8000 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "minimax-m2.5": { - "id": "minimax-m2.5", + "MiniMaxAI/MiniMax-M2.5": { + "id": "MiniMaxAI/MiniMax-M2.5", "name": "MiniMax-M2.5", "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, "release_date": "2026-02-12", "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.29, "output": 1.15 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 196608, + "output": 196608 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06 + } } } }, - "minimax-cn-coding-plan": { - "id": "minimax-cn-coding-plan", - "env": ["MINIMAX_API_KEY"], - "npm": "@ai-sdk/anthropic", - "api": "https://api.minimaxi.com/anthropic/v1", - "name": "MiniMax Coding Plan (minimaxi.com)", - "doc": "https://platform.minimaxi.com/docs/coding-plan/intro", + "cortecs": { + "id": "cortecs", + "env": ["CORTECS_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.cortecs.ai/v1", + "name": "Cortecs", + "doc": "https://api.cortecs.ai/v1/models", "models": { - "MiniMax-M2.7": { - "id": "MiniMax-M2.7", - "name": "MiniMax-M2.7", + "minimax-m2.7": { + "id": "minimax-m2.7", + "name": "MiniMax-m2.7", "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, "release_date": "2026-03-18", "last_updated": "2026-03-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 202752, + "output": 196072 + }, + "cost": { + "input": 0.47, + "output": 1.4 + } }, - "MiniMax-M2.1": { - "id": "MiniMax-M2.1", - "name": "MiniMax-M2.1", - "family": "minimax", - "attachment": false, + "claude-haiku-4-5": { + "id": "claude-haiku-4-5", + "name": "Claude Haiku 4.5", + "family": "claude-haiku", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 204800, "output": 131072 } + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 200000 + }, + "cost": { + "input": 1.09, + "output": 5.43 + } }, - "MiniMax-M2.5": { - "id": "MiniMax-M2.5", - "name": "MiniMax-M2.5", - "family": "minimax", + "qwen3-235b-a22b-instruct-2507": { + "id": "qwen3-235b-a22b-instruct-2507", + "name": "Qwen3 235B A22B Instruct 2507", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.062, + "output": 0.408 + } }, - "MiniMax-M2": { - "id": "MiniMax-M2", - "name": "MiniMax-M2", - "family": "minimax", + "kimi-k2.5": { + "id": "kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi-thinking", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-10-27", - "last_updated": "2025-10-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 196608, "output": 128000 } + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.55, + "output": 2.76 + } }, - "MiniMax-M2.5-highspeed": { - "id": "MiniMax-M2.5-highspeed", - "name": "MiniMax-M2.5-highspeed", - "family": "minimax", + "deepseek-v3-0324": { + "id": "deepseek-v3-0324", + "name": "DeepSeek V3 0324", + "family": "deepseek", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2026-02-13", - "last_updated": "2026-02-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-07", + "release_date": "2025-03-24", + "last_updated": "2025-03-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.551, + "output": 1.654 + } }, - "MiniMax-M2.7-highspeed": { - "id": "MiniMax-M2.7-highspeed", - "name": "MiniMax-M2.7-highspeed", - "family": "minimax", + "glm-4.7": { + "id": "glm-4.7", + "name": "GLM 4.7", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } - } - } - }, - "ovhcloud": { - "id": "ovhcloud", - "env": ["OVHCLOUD_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://oai.endpoints.kepler.ai.cloud.ovh.net/v1", - "name": "OVHcloud AI Endpoints", - "doc": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog//", - "models": { - "qwen3-coder-30b-a3b-instruct": { - "id": "qwen3-coder-30b-a3b-instruct", - "name": "Qwen3-Coder-30B-A3B-Instruct", - "attachment": false, - "reasoning": false, + "limit": { + "context": 198000, + "output": 198000 + }, + "cost": { + "input": 0.45, + "output": 2.23 + } + }, + "claude-opus4-7": { + "id": "claude-opus4-7", + "name": "Claude Opus 4.7", + "family": "claude-opus", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-10-28", - "last_updated": "2025-10-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.07, "output": 0.26 }, - "limit": { "context": 262144, "output": 262144 } + "temperature": false, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5.6, + "output": 27.99, + "cache_read": 0.56, + "cache_write": 6.99 + } }, - "mistral-nemo-instruct-2407": { - "id": "mistral-nemo-instruct-2407", - "name": "Mistral-Nemo-Instruct-2407", + "glm-5": { + "id": "glm-5", + "name": "GLM 5", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2024-11-20", - "last_updated": "2024-11-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.14, "output": 0.14 }, - "limit": { "context": 65536, "output": 65536 } + "limit": { + "context": 202752, + "output": 202752 + }, + "cost": { + "input": 1.08, + "output": 3.44 + } }, - "deepseek-r1-distill-llama-70b": { - "id": "deepseek-r1-distill-llama-70b", - "name": "DeepSeek-R1-Distill-Llama-70B", + "nova-pro-v1": { + "id": "nova-pro-v1", + "name": "Nova Pro 1.0", + "family": "nova-pro", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-01-30", - "last_updated": "2025-01-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.74, "output": 0.74 }, - "limit": { "context": 131072, "output": 131072 } + "knowledge": "2024-04", + "release_date": "2024-12-03", + "last_updated": "2024-12-03", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 300000, + "output": 5000 + }, + "cost": { + "input": 1.016, + "output": 4.061 + } }, - "llama-3.1-8b-instruct": { - "id": "llama-3.1-8b-instruct", - "name": "Llama-3.1-8B-Instruct", + "devstral-2512": { + "id": "devstral-2512", + "name": "Devstral 2 2512", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-06-11", - "last_updated": "2025-06-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-12", + "release_date": "2025-12-09", + "last_updated": "2025-12-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.11, "output": 0.11 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gpt-oss-120b": { - "id": "gpt-oss-120b", - "name": "gpt-oss-120b", + "qwen3-32b": { + "id": "qwen3-32b", + "name": "Qwen3 32B", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, - "release_date": "2025-08-28", - "last_updated": "2025-08-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2025-04-29", + "last_updated": "2025-04-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.09, "output": 0.47 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 16384, + "output": 16384 + }, + "cost": { + "input": 0.099, + "output": 0.33 + } }, - "qwen2.5-coder-32b-instruct": { - "id": "qwen2.5-coder-32b-instruct", - "name": "Qwen2.5-Coder-32B-Instruct", + "codestral-2508": { + "id": "codestral-2508", + "name": "Codestral 2508", + "family": "mistral", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": true, + "tool_call": true, "temperature": true, - "release_date": "2025-03-24", - "last_updated": "2025-03-24", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-03", + "release_date": "2025-07-30", + "last_updated": "2025-07-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.96, "output": 0.96 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.3, + "output": 0.9, + "cache_read": 0.03 + } }, - "qwen2.5-vl-72b-instruct": { - "id": "qwen2.5-vl-72b-instruct", - "name": "Qwen2.5-VL-72B-Instruct", + "claude-4-5-sonnet": { + "id": "claude-4-5-sonnet", + "name": "Claude 4.5 Sonnet", + "family": "claude-sonnet", "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": true, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-03-31", - "last_updated": "2025-03-31", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1.01, "output": 1.01 }, - "limit": { "context": 32768, "output": 32768 } + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 200000 + }, + "cost": { + "input": 3.259, + "output": 16.296 + } }, - "mistral-7b-instruct-v0.3": { - "id": "mistral-7b-instruct-v0.3", - "name": "Mistral-7B-Instruct-v0.3", + "kimi-k2-instruct": { + "id": "kimi-k2-instruct", + "name": "Kimi K2 Instruct", + "family": "kimi", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-04-01", - "last_updated": "2025-04-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-07", + "release_date": "2025-07-11", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.11, "output": 0.11 }, - "limit": { "context": 65536, "output": 65536 } + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.551, + "output": 2.646 + } }, - "gpt-oss-20b": { - "id": "gpt-oss-20b", - "name": "gpt-oss-20b", + "nemotron-3-super-120b-a12b": { + "id": "nemotron-3-super-120b-a12b", + "name": "Nemotron 3 Super 120B A12B", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-08-28", - "last_updated": "2025-08-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-12", + "release_date": "2026-03-11", + "last_updated": "2026-03-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.05, "output": 0.18 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.266, + "output": 0.799 + } }, - "qwen3-32b": { - "id": "qwen3-32b", - "name": "Qwen3-32B", + "minimax-m2": { + "id": "minimax-m2", + "name": "MiniMax-M2", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-07-16", - "last_updated": "2025-07-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-11", + "release_date": "2025-10-27", + "last_updated": "2025-10-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.09, "output": 0.25 }, - "limit": { "context": 32768, "output": 32768 } + "limit": { + "context": 400000, + "output": 400000 + }, + "cost": { + "input": 0.39, + "output": 1.57 + } }, - "mistral-small-3.2-24b-instruct-2506": { - "id": "mistral-small-3.2-24b-instruct-2506", - "name": "Mistral-Small-3.2-24B-Instruct-2506", - "attachment": true, + "gemini-2.5-pro": { + "id": "gemini-2.5-pro", + "name": "Gemini 2.5 Pro", + "family": "gemini-pro", + "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-16", - "last_updated": "2025-07-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.31 }, - "limit": { "context": 131072, "output": 131072 } + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65535 + }, + "cost": { + "input": 1.654, + "output": 11.024 + } }, - "mixtral-8x7b-instruct-v0.1": { - "id": "mixtral-8x7b-instruct-v0.1", - "name": "Mixtral-8x7B-Instruct-v0.1", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": true, + "claude-opus4-6": { + "id": "claude-opus4-6", + "name": "Claude Opus 4.6", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-04-01", - "last_updated": "2025-04-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.7, "output": 0.7 }, - "limit": { "context": 32768, "output": 32768 } + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 1000000 + }, + "cost": { + "input": 5.98, + "output": 29.89 + } }, - "meta-llama-3_3-70b-instruct": { - "id": "meta-llama-3_3-70b-instruct", - "name": "Meta-Llama-3_3-70B-Instruct", + "devstral-small-2512": { + "id": "devstral-small-2512", + "name": "Devstral Small 2 2512", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-04-01", - "last_updated": "2025-04-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.74, "output": 0.74 }, - "limit": { "context": 131072, "output": 131072 } - } - } - }, - "minimax-cn": { - "id": "minimax-cn", - "env": ["MINIMAX_API_KEY"], - "npm": "@ai-sdk/anthropic", - "api": "https://api.minimaxi.com/anthropic/v1", - "name": "MiniMax (minimaxi.com)", - "doc": "https://platform.minimaxi.com/docs/guides/quickstart", - "models": { - "MiniMax-M2.7": { - "id": "MiniMax-M2.7", - "name": "MiniMax-M2.7", - "family": "minimax", - "attachment": false, - "reasoning": true, - "tool_call": true, "temperature": true, - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-12", + "release_date": "2025-12-09", + "last_updated": "2025-12-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.06, "cache_write": 0.375 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "MiniMax-M2.1": { - "id": "MiniMax-M2.1", + "minimax-m2.1": { + "id": "minimax-m2.1", "name": "MiniMax-M2.1", "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, "release_date": "2025-12-23", "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 196000, + "output": 196000 + }, + "cost": { + "input": 0.34, + "output": 1.34 + } }, - "MiniMax-M2.5": { - "id": "MiniMax-M2.5", - "name": "MiniMax-M2.5", - "family": "minimax", + "glm-5.1": { + "id": "glm-5.1", + "name": "GLM-5.1", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-14", + "last_updated": "2026-04-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.03, "cache_write": 0.375 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 1.31, + "output": 4.1, + "cache_read": 0.24 + } }, - "MiniMax-M2": { - "id": "MiniMax-M2", - "name": "MiniMax-M2", - "family": "minimax", + "glm-4.5": { + "id": "glm-4.5", + "name": "GLM 4.5", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-10-27", - "last_updated": "2025-10-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-29", + "last_updated": "2025-07-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 196608, "output": 128000 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.67, + "output": 2.46 + } }, - "MiniMax-M2.5-highspeed": { - "id": "MiniMax-M2.5-highspeed", - "name": "MiniMax-M2.5-highspeed", - "family": "minimax", + "claude-opus4-5": { + "id": "claude-opus4-5", + "name": "Claude Opus 4.5", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-11-24", + "last_updated": "2025-11-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 200000 + }, + "cost": { + "input": 5.98, + "output": 29.89 + } + }, + "claude-sonnet-4": { + "id": "claude-sonnet-4", + "name": "Claude Sonnet 4", + "family": "claude-sonnet", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3.307, + "output": 16.536 + } + }, + "qwen3-next-80b-a3b-thinking": { + "id": "qwen3-next-80b-a3b-thinking", + "name": "Qwen3 Next 80B A3B Thinking", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-02-13", - "last_updated": "2026-02-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-09-11", + "last_updated": "2025-09-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.4, "cache_read": 0.06, "cache_write": 0.375 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.164, + "output": 1.311 + } }, - "MiniMax-M2.7-highspeed": { - "id": "MiniMax-M2.7-highspeed", - "name": "MiniMax-M2.7-highspeed", - "family": "minimax", + "glm-4.5-air": { + "id": "glm-4.5-air", + "name": "GLM 4.5 Air", + "family": "glm-air", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-08-01", + "last_updated": "2025-08-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.4, "cache_read": 0.06, "cache_write": 0.375 }, - "limit": { "context": 204800, "output": 131072 } - } - } - }, - "qihang-ai": { - "id": "qihang-ai", - "env": ["QIHANG_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.qhaigc.net/v1", - "name": "QiHang", - "doc": "https://www.qhaigc.net/docs", - "models": { - "gpt-5.2-codex": { - "id": "gpt-5.2-codex", - "name": "GPT-5.2 Codex", - "family": "gpt-codex", - "attachment": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.22, + "output": 1.34 + } + }, + "kimi-k2.6": { + "id": "kimi-k2.6", + "name": "Kimi K2.6", + "family": "kimi-thinking", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.14, "output": 1.14 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-04-17", + "last_updated": "2026-04-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.81, + "output": 3.54, + "cache_read": 0.2 + } }, - "claude-opus-4-5-20251101": { - "id": "claude-opus-4-5-20251101", - "name": "Claude Opus 4.5", - "family": "claude-opus", - "attachment": true, + "qwen3-coder-next": { + "id": "qwen3-coder-next", + "name": "Qwen3 Coder Next 80B", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-11-01", - "last_updated": "2025-11-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.71, "output": 3.57 }, - "limit": { "context": 200000, "output": 32000 } + "knowledge": "2025-04", + "release_date": "2026-02-04", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 65536 + }, + "cost": { + "input": 0.158, + "output": 0.84 + } }, - "gemini-3-pro-preview": { - "id": "gemini-3-pro-preview", - "name": "Gemini 3 Pro Preview", - "family": "gemini-pro", + "claude-4-6-sonnet": { + "id": "claude-4-6-sonnet", + "name": "Claude Sonnet 4.6", + "family": "claude-sonnet", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-11", - "release_date": "2025-11-19", - "last_updated": "2025-11-19", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.57, "output": 3.43 }, - "limit": { "context": 1000000, "output": 65000 } + "limit": { + "context": 1000000, + "output": 1000000 + }, + "cost": { + "input": 3.59, + "output": 17.92 + } }, - "gpt-5-mini": { - "id": "gpt-5-mini", - "name": "GPT-5-Mini", - "family": "gpt-mini", - "attachment": true, - "reasoning": true, + "qwen3-coder-480b-a35b-instruct": { + "id": "qwen3-coder-480b-a35b-instruct", + "name": "Qwen3 Coder 480B A35B Instruct", + "family": "qwen", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-09-30", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.04, "output": 0.29 }, - "limit": { "context": 200000, "output": 64000 } + "knowledge": "2025-01", + "release_date": "2025-07-25", + "last_updated": "2025-07-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.441, + "output": 1.984 + } }, - "claude-sonnet-4-5-20250929": { - "id": "claude-sonnet-4-5-20250929", - "name": "Claude Sonnet 4.5", - "family": "claude-sonnet", - "attachment": true, + "mixtral-8x7B-instruct-v0.1": { + "id": "mixtral-8x7B-instruct-v0.1", + "name": "Mixtral 8x7B Instruct v0.1", + "attachment": false, "reasoning": true, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.43, "output": 2.14 }, - "limit": { "context": 200000, "output": 64000 } + "knowledge": "2023-09", + "release_date": "2023-12-11", + "last_updated": "2023-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32000, + "output": 32000 + }, + "cost": { + "input": 0.438, + "output": 0.68 + } }, - "gemini-3-flash-preview": { - "id": "gemini-3-flash-preview", - "name": "Gemini 3 Flash Preview", - "family": "gemini-flash", - "attachment": true, + "hermes-4-70b": { + "id": "hermes-4-70b", + "name": "Hermes 4 70B", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.07, "output": 0.43, "context_over_200k": { "input": 0.07, "output": 0.43 } }, - "limit": { "context": 1048576, "output": 65536 } + "knowledge": "2023-12", + "release_date": "2025-08-26", + "last_updated": "2025-08-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.116, + "output": 0.358 + } }, - "gemini-2.5-flash": { - "id": "gemini-2.5-flash", - "name": "Gemini 2.5 Flash", - "family": "gemini-flash", - "attachment": true, + "minimax-m2.5": { + "id": "minimax-m2.5", + "name": "MiniMax-M2.5", + "family": "minimax", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.09, "output": 0.71, "context_over_200k": { "input": 0.09, "output": 0.71 } }, - "limit": { "context": 1048576, "output": 65536 } + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 196608, + "output": 196608 + }, + "cost": { + "input": 0.32, + "output": 1.18 + } }, - "gpt-5.2": { - "id": "gpt-5.2", - "name": "GPT-5.2", - "family": "gpt", - "attachment": true, + "deepseek-v3.2": { + "id": "deepseek-v3.2", + "name": "DeepSeek V3.2", + "family": "deepseek", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 2 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "knowledge": "2024-07", + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 163840 + }, + "cost": { + "input": 0.266, + "output": 0.444 + } }, - "claude-haiku-4-5-20251001": { - "id": "claude-haiku-4-5-20251001", - "name": "Claude Haiku 4.5", - "family": "claude-haiku", + "intellect-3": { + "id": "intellect-3", + "name": "INTELLECT 3", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-10-01", - "last_updated": "2025-10-01", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.14, "output": 0.71 }, - "limit": { "context": 200000, "output": 64000 } - } - } - }, - "moonshotai": { - "id": "moonshotai", - "env": ["MOONSHOT_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.moonshot.ai/v1", - "name": "Moonshot AI", - "doc": "https://platform.moonshot.ai/docs/api/chat", - "models": { - "kimi-k2-thinking": { - "id": "kimi-k2-thinking", - "name": "Kimi K2 Thinking", - "family": "kimi-thinking", + "knowledge": "2025-11", + "release_date": "2025-11-26", + "last_updated": "2025-11-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.219, + "output": 1.202 + } + }, + "glm-4.7-flash": { + "id": "glm-4.7-flash", + "name": "GLM-4.7-Flash", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-11-06", - "last_updated": "2025-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-08-08", + "last_updated": "2025-08-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.5, "cache_read": 0.15 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 203000, + "output": 203000 + }, + "cost": { + "input": 0.09, + "output": 0.53 + } }, - "kimi-k2.5": { - "id": "kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi", + "gpt-oss-120b": { + "id": "gpt-oss-120b", + "name": "GPT Oss 120b", + "family": "gpt-oss", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, - "temperature": false, - "knowledge": "2025-01", - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-01", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 3, "cache_read": 0.1 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "kimi-k2-thinking-turbo": { - "id": "kimi-k2-thinking-turbo", - "name": "Kimi K2 Thinking Turbo", - "family": "kimi-thinking", + "qwen-2.5-72b-instruct": { + "id": "qwen-2.5-72b-instruct", + "name": "Qwen2.5 72B Instruct", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-11-06", - "last_updated": "2025-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-06", + "release_date": "2024-09-19", + "last_updated": "2024-09-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1.15, "output": 8, "cache_read": 0.15 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 33000, + "output": 33000 + }, + "cost": { + "input": 0.062, + "output": 0.231 + } }, - "kimi-k2-0905-preview": { - "id": "kimi-k2-0905-preview", - "name": "Kimi K2 0905", - "family": "kimi", + "deepseek-r1-0528": { + "id": "deepseek-r1-0528", + "name": "DeepSeek R1 0528", + "family": "deepseek-thinking", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-07", + "release_date": "2025-05-28", + "last_updated": "2025-05-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.5, "cache_read": 0.15 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 164000, + "output": 164000 + }, + "cost": { + "input": 0.585, + "output": 2.307 + } }, - "kimi-k2-turbo-preview": { - "id": "kimi-k2-turbo-preview", - "name": "Kimi K2 Turbo", - "family": "kimi", + "gpt-4.1": { + "id": "gpt-4.1", + "name": "GPT 4.1", + "family": "gpt", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2.4, "output": 10, "cache_read": 0.6 }, - "limit": { "context": 262144, "output": 262144 } + "knowledge": "2024-06", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 2.354, + "output": 9.417 + } }, - "kimi-k2-0711-preview": { - "id": "kimi-k2-0711-preview", - "name": "Kimi K2 0711", - "family": "kimi", - "attachment": false, - "reasoning": false, + "kimi-k2-thinking": { + "id": "kimi-k2-thinking", + "name": "Kimi K2 Thinking", + "attachment": true, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-07-14", - "last_updated": "2025-07-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-12", + "release_date": "2025-12-08", + "last_updated": "2025-12-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.5, "cache_read": 0.15 }, - "limit": { "context": 131072, "output": 16384 } - } - } - }, - "alibaba": { - "id": "alibaba", - "env": ["DASHSCOPE_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://dashscope-intl.aliyuncs.com/compatible-mode/v1", - "name": "Alibaba", - "doc": "https://www.alibabacloud.com/help/en/model-studio/models", - "models": { - "qwen2-5-72b-instruct": { - "id": "qwen2-5-72b-instruct", - "name": "Qwen2.5 72B Instruct", - "family": "qwen", + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.656, + "output": 2.731 + } + }, + "llama-3.1-405b-instruct": { + "id": "llama-3.1-405b-instruct", + "name": "Llama 3.1 405B Instruct", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-09", - "last_updated": "2024-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1.4, "output": 5.6 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "qwen3-coder-30b-a3b-instruct": { - "id": "qwen3-coder-30b-a3b-instruct", - "name": "Qwen3-Coder 30B-A3B Instruct", + "qwen3.5-122b-a10b": { + "id": "qwen3.5-122b-a10b", + "name": "Qwen3.5 122B A10B", "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2026-01", + "release_date": "2026-02-24", + "last_updated": "2026-02-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.45, "output": 2.25 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.444, + "output": 3.106 + } }, - "qwen3-8b": { - "id": "qwen3-8b", - "name": "Qwen3 8B", - "family": "qwen", + "llama-3.3-70b-instruct": { + "id": "llama-3.3-70b-instruct", + "name": "Llama 3.3 70B Instruct", + "family": "llama", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.18, "output": 0.7, "reasoning": 2.1 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.089, + "output": 0.275 + } }, - "qwen-mt-plus": { - "id": "qwen-mt-plus", - "name": "Qwen-MT Plus", - "family": "qwen", - "attachment": false, + "mistral-large-2512": { + "id": "mistral-large-2512", + "name": "Mistral Large 3 2512", + "family": "mistral-large", + "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-01", - "last_updated": "2025-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.46, "output": 7.37 }, - "limit": { "context": 16384, "output": 8192 } + "knowledge": "2025-12", + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.5, + "output": 1.5, + "cache_read": 0.05 + } }, - "qwen3.5-plus": { - "id": "qwen3.5-plus", - "name": "Qwen3.5 Plus", + "qwen3.5-397b-a17b": { + "id": "qwen3.5-397b-a17b", + "name": "Qwen3.5 397B A17B", "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-04", + "knowledge": "2026-01", "release_date": "2026-02-16", "last_updated": "2026-02-16", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 2.4, "reasoning": 2.4 }, - "limit": { "context": 1000000, "output": 65536 } + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 250000, + "output": 250000 + }, + "cost": { + "input": 0.6, + "output": 3.6 + } }, - "qwen2-5-omni-7b": { - "id": "qwen2-5-omni-7b", - "name": "Qwen2.5-Omni 7B", - "family": "qwen", + "deepseek-v4-flash": { + "id": "deepseek-v4-flash", + "name": "DeepSeek V4 Flash", + "family": "deepseek-flash", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-12", - "last_updated": "2024-12", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text", "audio"] }, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.1, "output": 0.4, "input_audio": 6.76 }, - "limit": { "context": 32768, "output": 2048 } + "limit": { + "context": 1048576, + "output": 384000 + }, + "cost": { + "input": 0.133, + "output": 0.266, + "cache_read": 0.028 + } }, - "qwen-turbo": { - "id": "qwen-turbo", - "name": "Qwen Turbo", - "family": "qwen", + "deepseek-v4-pro": { + "id": "deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-11-01", - "last_updated": "2025-04-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.05, "output": 0.2, "reasoning": 0.5 }, - "limit": { "context": 1000000, "output": 16384 } + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 384000 + }, + "cost": { + "input": 1.553, + "output": 3.106, + "cache_read": 0.145 + } }, - "qwen-vl-max": { - "id": "qwen-vl-max", - "name": "Qwen-VL Max", + "qwen3-coder-30b-a3b-instruct": { + "id": "qwen3-coder-30b-a3b-instruct", + "name": "Qwen3 Coder 30B A3B Instruct", "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-04-08", - "last_updated": "2025-08-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-31", + "last_updated": "2025-07-31", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.053, + "output": 0.222 + } + } + } + }, + "siliconflow": { + "id": "siliconflow", + "env": ["SILICONFLOW_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.siliconflow.com/v1", + "name": "SiliconFlow", + "doc": "https://cloud.siliconflow.com/models", + "models": { + "nex-agi/DeepSeek-V3.1-Nex-N1": { + "id": "nex-agi/DeepSeek-V3.1-Nex-N1", + "name": "nex-agi/DeepSeek-V3.1-Nex-N1", + "family": "deepseek", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-01-01", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.8, "output": 3.2 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.5, + "output": 2 + } }, - "qwen-omni-turbo-realtime": { - "id": "qwen-omni-turbo-realtime", - "name": "Qwen-Omni Turbo Realtime", + "Qwen/Qwen2.5-VL-72B-Instruct": { + "id": "Qwen/Qwen2.5-VL-72B-Instruct", + "name": "Qwen/Qwen2.5-VL-72B-Instruct", "family": "qwen", - "attachment": false, + "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-05-08", - "last_updated": "2025-05-08", - "modalities": { "input": ["text", "image", "audio"], "output": ["text", "audio"] }, + "release_date": "2025-01-28", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.27, "output": 1.07, "input_audio": 4.44, "output_audio": 8.89 }, - "limit": { "context": 32768, "output": 2048 } + "limit": { + "context": 131000, + "output": 4000 + }, + "cost": { + "input": 0.59, + "output": 0.59 + } }, - "qwen-vl-plus": { - "id": "qwen-vl-plus", - "name": "Qwen-VL Plus", + "Qwen/Qwen3-VL-32B-Thinking": { + "id": "Qwen/Qwen3-VL-32B-Thinking", + "name": "Qwen/Qwen3-VL-32B-Thinking", "family": "qwen", - "attachment": false, - "reasoning": false, + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-01-25", - "last_updated": "2025-08-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-10-21", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.21, "output": 0.63 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.2, + "output": 1.5 + } }, - "qwen-max": { - "id": "qwen-max", - "name": "Qwen Max", + "Qwen/Qwen3-30B-A3B-Thinking-2507": { + "id": "Qwen/Qwen3-30B-A3B-Thinking-2507", + "name": "Qwen/Qwen3-30B-A3B-Thinking-2507", "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-04-03", - "last_updated": "2025-01-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-07-31", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.6, "output": 6.4 }, - "limit": { "context": 32768, "output": 8192 } + "limit": { + "context": 262000, + "output": 131000 + }, + "cost": { + "input": 0.09, + "output": 0.3 + } }, - "qvq-max": { - "id": "qvq-max", - "name": "QVQ Max", - "family": "qvq", - "attachment": false, + "Qwen/Qwen3-VL-235B-A22B-Thinking": { + "id": "Qwen/Qwen3-VL-235B-A22B-Thinking", + "name": "Qwen/Qwen3-VL-235B-A22B-Thinking", + "family": "qwen", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-03-25", - "last_updated": "2025-03-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-10-04", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.2, "output": 4.8 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.45, + "output": 3.5 + } }, - "qwen-plus-character-ja": { - "id": "qwen-plus-character-ja", - "name": "Qwen Plus Character (Japanese)", + "Qwen/Qwen2.5-7B-Instruct": { + "id": "Qwen/Qwen2.5-7B-Instruct", + "name": "Qwen/Qwen2.5-7B-Instruct", "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-01", - "last_updated": "2024-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-09-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 1.4 }, - "limit": { "context": 8192, "output": 512 } + "limit": { + "context": 33000, + "output": 4000 + }, + "cost": { + "input": 0.05, + "output": 0.05 + } }, - "qwq-plus": { - "id": "qwq-plus", - "name": "QwQ Plus", + "Qwen/Qwen2.5-Coder-32B-Instruct": { + "id": "Qwen/Qwen2.5-Coder-32B-Instruct", + "name": "Qwen/Qwen2.5-Coder-32B-Instruct", "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-03-05", - "last_updated": "2025-03-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-11-11", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.8, "output": 2.4 }, - "limit": { "context": 131072, "output": 8192 } + "limit": { + "context": 33000, + "output": 4000 + }, + "cost": { + "input": 0.18, + "output": 0.18 + } }, - "qwen3-omni-flash": { - "id": "qwen3-omni-flash", - "name": "Qwen3-Omni Flash", + "Qwen/Qwen3-VL-30B-A3B-Instruct": { + "id": "Qwen/Qwen3-VL-30B-A3B-Instruct", + "name": "Qwen/Qwen3-VL-30B-A3B-Instruct", "family": "qwen", - "attachment": false, - "reasoning": true, + "attachment": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text", "audio"] }, + "release_date": "2025-10-05", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.43, "output": 1.66, "input_audio": 3.81, "output_audio": 15.11 }, - "limit": { "context": 65536, "output": 16384 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.29, + "output": 1 + } }, - "qwen3-14b": { - "id": "qwen3-14b", - "name": "Qwen3 14B", + "Qwen/QwQ-32B": { + "id": "Qwen/QwQ-32B", + "name": "Qwen/QwQ-32B", "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.35, "output": 1.4, "reasoning": 4.2 }, - "limit": { "context": 131072, "output": 8192 } + "release_date": "2025-03-06", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.15, + "output": 0.58 + } }, - "qwen-plus": { - "id": "qwen-plus", - "name": "Qwen Plus", + "Qwen/Qwen3-235B-A22B": { + "id": "Qwen/Qwen3-235B-A22B", + "name": "Qwen/Qwen3-235B-A22B", "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-01-25", - "last_updated": "2025-09-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-04-30", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 1.2, "reasoning": 4 }, - "limit": { "context": 1000000, "output": 32768 } + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.35, + "output": 1.42 + } }, - "qwen2-5-7b-instruct": { - "id": "qwen2-5-7b-instruct", - "name": "Qwen2.5 7B Instruct", + "Qwen/Qwen3-Omni-30B-A3B-Captioner": { + "id": "Qwen/Qwen3-Omni-30B-A3B-Captioner", + "name": "Qwen/Qwen3-Omni-30B-A3B-Captioner", "family": "qwen", - "attachment": false, + "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-09", - "last_updated": "2024-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.175, "output": 0.7 }, - "limit": { "context": 131072, "output": 8192 } + "release_date": "2025-10-04", + "last_updated": "2025-11-25", + "modalities": { + "input": ["audio"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 66000, + "output": 66000 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } }, - "qwen2-5-32b-instruct": { - "id": "qwen2-5-32b-instruct", - "name": "Qwen2.5 32B Instruct", + "Qwen/Qwen3-VL-8B-Thinking": { + "id": "Qwen/Qwen3-VL-8B-Thinking", + "name": "Qwen/Qwen3-VL-8B-Thinking", "family": "qwen", - "attachment": false, - "reasoning": false, + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-09", - "last_updated": "2024-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.7, "output": 2.8 }, - "limit": { "context": 131072, "output": 8192 } + "release_date": "2025-10-15", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.18, + "output": 2 + } }, - "qwen3-omni-flash-realtime": { - "id": "qwen3-omni-flash-realtime", - "name": "Qwen3-Omni Flash Realtime", + "Qwen/Qwen2.5-VL-7B-Instruct": { + "id": "Qwen/Qwen2.5-VL-7B-Instruct", + "name": "Qwen/Qwen2.5-VL-7B-Instruct", "family": "qwen", - "attachment": false, + "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text", "audio"] }, + "release_date": "2025-01-28", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.52, "output": 1.99, "input_audio": 4.57, "output_audio": 18.13 }, - "limit": { "context": 65536, "output": 16384 } + "limit": { + "context": 33000, + "output": 4000 + }, + "cost": { + "input": 0.05, + "output": 0.05 + } }, - "qwen3-coder-480b-a35b-instruct": { - "id": "qwen3-coder-480b-a35b-instruct", - "name": "Qwen3-Coder 480B-A35B Instruct", + "Qwen/Qwen3-Next-80B-A3B-Instruct": { + "id": "Qwen/Qwen3-Next-80B-A3B-Instruct", + "name": "Qwen/Qwen3-Next-80B-A3B-Instruct", "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1.5, "output": 7.5 }, - "limit": { "context": 262144, "output": 65536 } + "release_date": "2025-09-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.14, + "output": 1.4 + } }, - "qwen3-next-80b-a3b-thinking": { - "id": "qwen3-next-80b-a3b-thinking", - "name": "Qwen3-Next 80B-A3B (Thinking)", + "Qwen/Qwen3-8B": { + "id": "Qwen/Qwen3-8B", + "name": "Qwen/Qwen3-8B", "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09", - "last_updated": "2025-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.5, "output": 6 }, - "limit": { "context": 131072, "output": 32768 } - }, - "qwen3-vl-30b-a3b": { - "id": "qwen3-vl-30b-a3b", - "name": "Qwen3-VL 30B-A3B", + "release_date": "2025-04-30", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.06, + "output": 0.06 + } + }, + "Qwen/Qwen3-30B-A3B-Instruct-2507": { + "id": "Qwen/Qwen3-30B-A3B-Instruct-2507", + "name": "Qwen/Qwen3-30B-A3B-Instruct-2507", "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.8, "reasoning": 2.4 }, - "limit": { "context": 131072, "output": 32768 } + "release_date": "2025-07-30", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.09, + "output": 0.3 + } }, - "qwen3-next-80b-a3b-instruct": { - "id": "qwen3-next-80b-a3b-instruct", - "name": "Qwen3-Next 80B-A3B Instruct", + "Qwen/Qwen3-235B-A22B-Instruct-2507": { + "id": "Qwen/Qwen3-235B-A22B-Instruct-2507", + "name": "Qwen/Qwen3-235B-A22B-Instruct-2507", "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09", - "last_updated": "2025-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.5, "output": 2 }, - "limit": { "context": 131072, "output": 32768 } + "release_date": "2025-07-23", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.09, + "output": 0.6 + } }, - "qwen-mt-turbo": { - "id": "qwen-mt-turbo", - "name": "Qwen-MT Turbo", + "Qwen/Qwen3-32B": { + "id": "Qwen/Qwen3-32B", + "name": "Qwen/Qwen3-32B", "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-01", - "last_updated": "2025-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-04-30", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.16, "output": 0.49 }, - "limit": { "context": 16384, "output": 8192 } + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.14, + "output": 0.57 + } }, - "qwen3-vl-plus": { - "id": "qwen3-vl-plus", - "name": "Qwen3-VL Plus", + "Qwen/Qwen3-Coder-30B-A3B-Instruct": { + "id": "Qwen/Qwen3-Coder-30B-A3B-Instruct", + "name": "Qwen/Qwen3-Coder-30B-A3B-Instruct", "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-23", - "last_updated": "2025-09-23", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-01", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 1.6, "reasoning": 4.8 }, - "limit": { "context": 262144, "output": 32768 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.07, + "output": 0.28 + } }, - "qwen3-235b-a22b": { - "id": "qwen3-235b-a22b", - "name": "Qwen3 235B-A22B", + "Qwen/Qwen3-Omni-30B-A3B-Instruct": { + "id": "Qwen/Qwen3-Omni-30B-A3B-Instruct", + "name": "Qwen/Qwen3-Omni-30B-A3B-Instruct", "family": "qwen", - "attachment": false, - "reasoning": true, + "attachment": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.7, "output": 2.8, "reasoning": 8.4 }, - "limit": { "context": 131072, "output": 16384 } + "release_date": "2025-10-04", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 66000, + "output": 66000 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } }, - "qwen2-5-vl-7b-instruct": { - "id": "qwen2-5-vl-7b-instruct", - "name": "Qwen2.5-VL 7B Instruct", + "Qwen/Qwen3-14B": { + "id": "Qwen/Qwen3-14B", + "name": "Qwen/Qwen3-14B", "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-09", - "last_updated": "2024-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.35, "output": 1.05 }, - "limit": { "context": 131072, "output": 8192 } + "release_date": "2025-04-30", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.07, + "output": 0.28 + } }, - "qwen-vl-ocr": { - "id": "qwen-vl-ocr", - "name": "Qwen-VL OCR", + "Qwen/Qwen2.5-72B-Instruct-128K": { + "id": "Qwen/Qwen2.5-72B-Instruct-128K", + "name": "Qwen/Qwen2.5-72B-Instruct-128K", "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-10-28", - "last_updated": "2025-04-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2024-09-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.72, "output": 0.72 }, - "limit": { "context": 34096, "output": 4096 } + "limit": { + "context": 131000, + "output": 4000 + }, + "cost": { + "input": 0.59, + "output": 0.59 + } }, - "qwen-omni-turbo": { - "id": "qwen-omni-turbo", - "name": "Qwen-Omni Turbo", + "Qwen/Qwen2.5-32B-Instruct": { + "id": "Qwen/Qwen2.5-32B-Instruct", + "name": "Qwen/Qwen2.5-32B-Instruct", "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-01-19", - "last_updated": "2025-03-26", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text", "audio"] }, + "release_date": "2024-09-19", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.07, "output": 0.27, "input_audio": 4.44, "output_audio": 8.89 }, - "limit": { "context": 32768, "output": 2048 } + "limit": { + "context": 33000, + "output": 4000 + }, + "cost": { + "input": 0.18, + "output": 0.18 + } }, - "qwen3.5-397b-a17b": { - "id": "qwen3.5-397b-a17b", - "name": "Qwen3.5 397B-A17B", + "Qwen/Qwen3-235B-A22B-Thinking-2507": { + "id": "Qwen/Qwen3-235B-A22B-Thinking-2507", + "name": "Qwen/Qwen3-235B-A22B-Thinking-2507", "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-02-16", - "last_updated": "2026-02-16", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 3.6, "reasoning": 3.6 }, - "limit": { "context": 262144, "output": 65536 } + "release_date": "2025-07-28", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.13, + "output": 0.6 + } }, - "qwen3-livetranslate-flash-realtime": { - "id": "qwen3-livetranslate-flash-realtime", - "name": "Qwen3-LiveTranslate Flash Realtime", + "Qwen/Qwen3-Omni-30B-A3B-Thinking": { + "id": "Qwen/Qwen3-Omni-30B-A3B-Thinking", + "name": "Qwen/Qwen3-Omni-30B-A3B-Thinking", "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": false, + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-09-22", - "last_updated": "2025-09-22", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text", "audio"] }, + "release_date": "2025-10-04", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 10, "output": 10, "input_audio": 10, "output_audio": 38 }, - "limit": { "context": 53248, "output": 4096 } + "limit": { + "context": 66000, + "output": 66000 + }, + "cost": { + "input": 0.1, + "output": 0.4 + } }, - "qwen3-coder-plus": { - "id": "qwen3-coder-plus", - "name": "Qwen3 Coder Plus", + "Qwen/Qwen2.5-VL-32B-Instruct": { + "id": "Qwen/Qwen2.5-VL-32B-Instruct", + "name": "Qwen/Qwen2.5-VL-32B-Instruct", "family": "qwen", - "attachment": false, + "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1, "output": 5 }, - "limit": { "context": 1048576, "output": 65536 } + "release_date": "2025-03-24", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.27, + "output": 0.27 + } }, - "qwen-flash": { - "id": "qwen-flash", - "name": "Qwen Flash", + "Qwen/Qwen3-Next-80B-A3B-Thinking": { + "id": "Qwen/Qwen3-Next-80B-A3B-Thinking", + "name": "Qwen/Qwen3-Next-80B-A3B-Thinking", "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-09-25", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.05, "output": 0.4 }, - "limit": { "context": 1000000, "output": 32768 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.14, + "output": 0.57 + } }, - "qwen3-max": { - "id": "qwen3-max", - "name": "Qwen3 Max", + "Qwen/Qwen3-VL-235B-A22B-Instruct": { + "id": "Qwen/Qwen3-VL-235B-A22B-Instruct", + "name": "Qwen/Qwen3-VL-235B-A22B-Instruct", "family": "qwen", - "attachment": false, + "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-23", - "last_updated": "2025-09-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-10-04", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.2, "output": 6 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.3, + "output": 1.5 + } }, - "qwen2-5-vl-72b-instruct": { - "id": "qwen2-5-vl-72b-instruct", - "name": "Qwen2.5-VL 72B Instruct", + "Qwen/Qwen2.5-14B-Instruct": { + "id": "Qwen/Qwen2.5-14B-Instruct", + "name": "Qwen/Qwen2.5-14B-Instruct", "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-09", - "last_updated": "2024-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2.8, "output": 8.4 }, - "limit": { "context": 131072, "output": 8192 } + "release_date": "2024-09-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 33000, + "output": 4000 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "qwen3-32b": { - "id": "qwen3-32b", - "name": "Qwen3 32B", + "Qwen/Qwen3-VL-30B-A3B-Thinking": { + "id": "Qwen/Qwen3-VL-30B-A3B-Thinking", + "name": "Qwen/Qwen3-VL-30B-A3B-Thinking", "family": "qwen", - "attachment": false, + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.7, "output": 2.8, "reasoning": 8.4 }, - "limit": { "context": 131072, "output": 16384 } + "release_date": "2025-10-11", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.29, + "output": 1 + } }, - "qwen3-asr-flash": { - "id": "qwen3-asr-flash", - "name": "Qwen3-ASR Flash", + "Qwen/Qwen3-VL-32B-Instruct": { + "id": "Qwen/Qwen3-VL-32B-Instruct", + "name": "Qwen/Qwen3-VL-32B-Instruct", "family": "qwen", - "attachment": false, + "attachment": true, "reasoning": false, - "tool_call": false, - "temperature": false, - "knowledge": "2024-04", - "release_date": "2025-09-08", - "last_updated": "2025-09-08", - "modalities": { "input": ["audio"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-10-21", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.035, "output": 0.035 }, - "limit": { "context": 53248, "output": 4096 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.2, + "output": 0.6 + } }, - "qwen3-coder-flash": { - "id": "qwen3-coder-flash", - "name": "Qwen3 Coder Flash", + "Qwen/Qwen3-VL-8B-Instruct": { + "id": "Qwen/Qwen3-VL-8B-Instruct", + "name": "Qwen/Qwen3-VL-8B-Instruct", "family": "qwen", - "attachment": false, + "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-10-15", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.5 }, - "limit": { "context": 1000000, "output": 65536 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.18, + "output": 0.68 + } }, - "qwen2-5-14b-instruct": { - "id": "qwen2-5-14b-instruct", - "name": "Qwen2.5 14B Instruct", + "Qwen/Qwen3-Coder-480B-A35B-Instruct": { + "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct", + "name": "Qwen/Qwen3-Coder-480B-A35B-Instruct", "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-09", - "last_updated": "2024-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.35, "output": 1.4 }, - "limit": { "context": 131072, "output": 8192 } + "release_date": "2025-07-31", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.25, + "output": 1 + } }, - "qwen3-vl-235b-a22b": { - "id": "qwen3-vl-235b-a22b", - "name": "Qwen3-VL 235B-A22B", + "Qwen/Qwen2.5-72B-Instruct": { + "id": "Qwen/Qwen2.5-72B-Instruct", + "name": "Qwen/Qwen2.5-72B-Instruct", "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2025-04", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.7, "output": 2.8, "reasoning": 8.4 }, - "limit": { "context": 131072, "output": 32768 } - } - } - }, - "sap-ai-core": { - "id": "sap-ai-core", - "env": ["AICORE_SERVICE_KEY"], - "npm": "@jerome-benoit/sap-ai-provider-v2", - "name": "SAP AI Core", - "doc": "https://help.sap.com/docs/sap-ai-core", - "models": { - "gemini-2.5-flash-lite": { - "id": "gemini-2.5-flash-lite", - "name": "gemini-2.5-flash-lite", - "family": "gemini-flash-lite", - "attachment": true, + "release_date": "2024-09-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 33000, + "output": 4000 + }, + "cost": { + "input": 0.59, + "output": 0.59 + } + }, + "stepfun-ai/Step-3.5-Flash": { + "id": "stepfun-ai/Step-3.5-Flash", + "name": "stepfun-ai/Step-3.5-Flash", + "family": "step", + "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4, "cache_read": 0.025 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } }, - "anthropic--claude-4.6-sonnet": { - "id": "anthropic--claude-4.6-sonnet", - "name": "anthropic--claude-4.6-sonnet", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, + "zai-org/GLM-4.5": { + "id": "zai-org/GLM-4.5", + "name": "zai-org/GLM-4.5", + "family": "glm", + "attachment": false, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-08", - "release_date": "2026-02-17", - "last_updated": "2026-03-13", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-07-28", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 1000000, "output": 64000 } + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.4, + "output": 2 + } }, - "anthropic--claude-4.5-haiku": { - "id": "anthropic--claude-4.5-haiku", - "name": "anthropic--claude-4.5-haiku", - "family": "claude-haiku", + "zai-org/GLM-5V-Turbo": { + "id": "zai-org/GLM-5V-Turbo", + "name": "zai-org/GLM-5V-Turbo", + "family": "glm", "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-02-28", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-04-01", + "last_updated": "2026-04-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 5, "cache_read": 0.1, "cache_write": 1.25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 1.2, + "output": 4, + "cache_write": 0 + } }, - "anthropic--claude-4-opus": { - "id": "anthropic--claude-4-opus", - "name": "anthropic--claude-4-opus", - "family": "claude-opus", - "attachment": true, + "zai-org/GLM-4.7": { + "id": "zai-org/GLM-4.7", + "name": "zai-org/GLM-4.7", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 205000, + "output": 205000 + }, + "cost": { + "input": 0.6, + "output": 2.2 + } }, - "anthropic--claude-3-opus": { - "id": "anthropic--claude-3-opus", - "name": "anthropic--claude-3-opus", - "family": "claude-opus", - "attachment": true, - "reasoning": false, + "zai-org/GLM-5.1": { + "id": "zai-org/GLM-5.1", + "name": "zai-org/GLM-5.1", + "family": "glm", + "attachment": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2023-08-31", - "release_date": "2024-02-29", - "last_updated": "2024-02-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 15, "output": 75, "cache_read": 1.5, "cache_write": 18.75 }, - "limit": { "context": 200000, "output": 4096 } + "release_date": "2026-04-08", + "last_updated": "2026-04-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 205000, + "output": 205000 + }, + "cost": { + "input": 1.4, + "output": 4.4, + "cache_write": 0 + } }, - "gpt-5": { - "id": "gpt-5", - "name": "gpt-5", - "family": "gpt", - "attachment": true, - "reasoning": true, + "zai-org/GLM-4.5-Air": { + "id": "zai-org/GLM-4.5-Air", + "name": "zai-org/GLM-4.5-Air", + "family": "glm-air", + "attachment": false, + "reasoning": false, "tool_call": true, "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "release_date": "2025-07-28", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.14, + "output": 0.86 + } }, - "anthropic--claude-3-sonnet": { - "id": "anthropic--claude-3-sonnet", - "name": "anthropic--claude-3-sonnet", - "family": "claude-sonnet", - "attachment": true, - "reasoning": false, + "zai-org/GLM-5": { + "id": "zai-org/GLM-5", + "name": "zai-org/GLM-5", + "family": "glm", + "attachment": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2023-08-31", - "release_date": "2024-03-04", - "last_updated": "2024-03-04", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 4096 } + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 205000, + "output": 205000 + }, + "cost": { + "input": 1, + "output": 3.2 + } }, - "anthropic--claude-4.5-opus": { - "id": "anthropic--claude-4.5-opus", - "name": "anthropic--claude-4.5-opus", - "family": "claude-opus", + "zai-org/GLM-4.6V": { + "id": "zai-org/GLM-4.6V", + "name": "zai-org/GLM-4.6V", + "family": "glm", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2025-05", - "release_date": "2025-11-24", - "last_updated": "2025-11-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-12-07", + "last_updated": "2025-12-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.3, + "output": 0.9 + } }, - "anthropic--claude-3-haiku": { - "id": "anthropic--claude-3-haiku", - "name": "anthropic--claude-3-haiku", - "family": "claude-haiku", - "attachment": true, + "zai-org/GLM-4.6": { + "id": "zai-org/GLM-4.6", + "name": "zai-org/GLM-4.6", + "family": "glm", + "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2023-08-31", - "release_date": "2024-03-13", - "last_updated": "2024-03-13", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-10-04", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 1.25, "cache_read": 0.03, "cache_write": 0.3 }, - "limit": { "context": 200000, "output": 4096 } + "limit": { + "context": 205000, + "output": 205000 + }, + "cost": { + "input": 0.5, + "output": 1.9 + } }, - "gpt-5-mini": { - "id": "gpt-5-mini", - "name": "gpt-5-mini", - "family": "gpt-mini", + "zai-org/GLM-4.5V": { + "id": "zai-org/GLM-4.5V", + "name": "zai-org/GLM-4.5V", + "family": "glm", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "structured_output": true, - "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "release_date": "2025-08-13", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 2, "cache_read": 0.025 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 66000, + "output": 66000 + }, + "cost": { + "input": 0.14, + "output": 0.86 + } }, - "gpt-4.1": { - "id": "gpt-4.1", - "name": "gpt-4.1", - "family": "gpt", - "attachment": true, + "meta-llama/Meta-Llama-3.1-8B-Instruct": { + "id": "meta-llama/Meta-Llama-3.1-8B-Instruct", + "name": "meta-llama/Meta-Llama-3.1-8B-Instruct", + "family": "llama", + "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-04-23", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8, "cache_read": 0.5 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 33000, + "output": 4000 + }, + "cost": { + "input": 0.06, + "output": 0.06 + } }, - "sonar-deep-research": { - "id": "sonar-deep-research", - "name": "sonar-deep-research", - "family": "sonar-deep-research", + "inclusionAI/Ring-flash-2.0": { + "id": "inclusionAI/Ring-flash-2.0", + "name": "inclusionAI/Ring-flash-2.0", + "family": "ring", "attachment": false, "reasoning": true, - "tool_call": false, - "temperature": false, - "knowledge": "2025-01", - "release_date": "2025-02-01", - "last_updated": "2025-09-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-09-29", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8, "reasoning": 3 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.14, + "output": 0.57 + } }, - "sonar": { - "id": "sonar", - "name": "sonar", - "family": "sonar", + "inclusionAI/Ling-mini-2.0": { + "id": "inclusionAI/Ling-mini-2.0", + "name": "inclusionAI/Ling-mini-2.0", + "family": "ling", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-09-01", - "release_date": "2024-01-01", - "last_updated": "2025-09-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-09-10", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 1 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.07, + "output": 0.28 + } }, - "anthropic--claude-4.5-sonnet": { - "id": "anthropic--claude-4.5-sonnet", - "name": "anthropic--claude-4.5-sonnet", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, + "inclusionAI/Ling-flash-2.0": { + "id": "inclusionAI/Ling-flash-2.0", + "name": "inclusionAI/Ling-flash-2.0", + "family": "ling", + "attachment": false, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-09-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.14, + "output": 0.57 + } }, - "anthropic--claude-3.7-sonnet": { - "id": "anthropic--claude-3.7-sonnet", - "name": "anthropic--claude-3.7-sonnet", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, + "tencent/Hunyuan-A13B-Instruct": { + "id": "tencent/Hunyuan-A13B-Instruct", + "name": "tencent/Hunyuan-A13B-Instruct", + "family": "hunyuan", + "attachment": false, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10-31", - "release_date": "2025-02-24", - "last_updated": "2025-02-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-06-30", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.14, + "output": 0.57 + } }, - "gemini-2.5-pro": { - "id": "gemini-2.5-pro", - "name": "gemini-2.5-pro", - "family": "gemini-pro", - "attachment": true, - "reasoning": true, + "tencent/Hunyuan-MT-7B": { + "id": "tencent/Hunyuan-MT-7B", + "name": "tencent/Hunyuan-MT-7B", + "family": "hunyuan", + "attachment": false, + "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-03-25", - "last_updated": "2025-06-05", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "release_date": "2025-09-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10, "cache_read": 0.125 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 33000, + "output": 33000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gemini-2.5-flash": { - "id": "gemini-2.5-flash", - "name": "gemini-2.5-flash", - "family": "gemini-flash", - "attachment": true, + "deepseek-ai/DeepSeek-V3.1": { + "id": "deepseek-ai/DeepSeek-V3.1", + "name": "deepseek-ai/DeepSeek-V3.1", + "family": "deepseek", + "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-03-25", - "last_updated": "2025-06-05", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "release_date": "2025-08-25", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 2.5, "cache_read": 0.03, "input_audio": 1 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 164000, + "output": 164000 + }, + "cost": { + "input": 0.27, + "output": 1 + } }, - "sonar-pro": { - "id": "sonar-pro", - "name": "sonar-pro", - "family": "sonar-pro", + "deepseek-ai/deepseek-vl2": { + "id": "deepseek-ai/deepseek-vl2", + "name": "deepseek-ai/deepseek-vl2", + "family": "deepseek", "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-09-01", - "release_date": "2024-01-01", - "last_updated": "2025-09-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2024-12-13", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 200000, "output": 8192 } + "limit": { + "context": 4000, + "output": 4000 + }, + "cost": { + "input": 0.15, + "output": 0.15 + } }, - "gpt-4.1-mini": { - "id": "gpt-4.1-mini", - "name": "gpt-4.1-mini", - "family": "gpt-mini", - "attachment": true, + "deepseek-ai/DeepSeek-V3": { + "id": "deepseek-ai/DeepSeek-V3", + "name": "deepseek-ai/DeepSeek-V3", + "family": "deepseek", + "attachment": false, "reasoning": false, "tool_call": true, "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2024-12-26", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 1.6, "cache_read": 0.1 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 164000, + "output": 164000 + }, + "cost": { + "input": 0.25, + "output": 1 + } }, - "gpt-5-nano": { - "id": "gpt-5-nano", - "name": "gpt-5-nano", - "family": "gpt-nano", - "attachment": true, + "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B": { + "id": "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", + "name": "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.05, "output": 0.4, "cache_read": 0.005 }, - "limit": { "context": 400000, "output": 128000 } - }, - "anthropic--claude-4.6-opus": { - "id": "anthropic--claude-4.6-opus", - "name": "anthropic--claude-4.6-opus", - "family": "claude-opus", - "attachment": true, - "reasoning": true, - "tool_call": true, "temperature": true, - "knowledge": "2025-05", - "release_date": "2026-02-05", - "last_updated": "2026-03-13", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-01-20", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25 }, - "limit": { "context": 1000000, "output": 128000 } + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.18, + "output": 0.18 + } }, - "anthropic--claude-3.5-sonnet": { - "id": "anthropic--claude-3.5-sonnet", - "name": "anthropic--claude-3.5-sonnet", - "family": "claude-sonnet", - "attachment": true, - "reasoning": false, + "deepseek-ai/DeepSeek-R1": { + "id": "deepseek-ai/DeepSeek-R1", + "name": "deepseek-ai/DeepSeek-R1", + "family": "deepseek-thinking", + "attachment": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04-30", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-05-28", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 8192 } + "limit": { + "context": 164000, + "output": 164000 + }, + "cost": { + "input": 0.5, + "output": 2.18 + } }, - "anthropic--claude-4-sonnet": { - "id": "anthropic--claude-4-sonnet", - "name": "anthropic--claude-4-sonnet", - "family": "claude-sonnet", - "attachment": true, + "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B": { + "id": "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B", + "name": "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-01-20", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 }, - "limit": { "context": 200000, "output": 64000 } - } - } - }, - "mistral": { - "id": "mistral", - "env": ["MISTRAL_API_KEY"], - "npm": "@ai-sdk/mistral", - "name": "Mistral", - "doc": "https://docs.mistral.ai/getting-started/models/", - "models": { - "devstral-small-2505": { - "id": "devstral-small-2505", - "name": "Devstral Small 2505", - "family": "devstral", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-05", - "release_date": "2025-05-07", - "last_updated": "2025-05-07", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 128000, "output": 128000 } - }, - "pixtral-large-latest": { - "id": "pixtral-large-latest", - "name": "Pixtral Large (latest)", - "family": "pixtral", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-11", - "release_date": "2024-11-01", - "last_updated": "2024-11-04", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2, "output": 6 }, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "mistral-small-2603": { - "id": "mistral-small-2603", - "name": "Mistral Small 4", - "family": "mistral-small", - "attachment": true, + "deepseek-ai/DeepSeek-V3.2-Exp": { + "id": "deepseek-ai/DeepSeek-V3.2-Exp", + "name": "deepseek-ai/DeepSeek-V3.2-Exp", + "family": "deepseek", + "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2026-03-16", - "last_updated": "2026-03-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 256000, "output": 256000 } + "release_date": "2025-10-10", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 164000, + "output": 164000 + }, + "cost": { + "input": 0.27, + "output": 0.41 + } }, - "ministral-3b-latest": { - "id": "ministral-3b-latest", - "name": "Ministral 3B (latest)", - "family": "ministral", + "deepseek-ai/DeepSeek-V3.2": { + "id": "deepseek-ai/DeepSeek-V3.2", + "name": "deepseek-ai/DeepSeek-V3.2", + "family": "deepseek", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-10-01", - "last_updated": "2024-10-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.04, "output": 0.04 }, - "limit": { "context": 128000, "output": 128000 } + "release_date": "2025-12-03", + "last_updated": "2025-12-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 164000, + "output": 164000 + }, + "cost": { + "input": 0.27, + "output": 0.42 + } }, - "magistral-small": { - "id": "magistral-small", - "name": "Magistral Small", - "family": "magistral-small", + "deepseek-ai/DeepSeek-V3.1-Terminus": { + "id": "deepseek-ai/DeepSeek-V3.1-Terminus", + "name": "deepseek-ai/DeepSeek-V3.1-Terminus", + "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-03-17", - "last_updated": "2025-03-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.5, "output": 1.5 }, - "limit": { "context": 128000, "output": 128000 } + "release_date": "2025-09-29", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 164000, + "output": 164000 + }, + "cost": { + "input": 0.27, + "output": 1 + } }, - "devstral-2512": { - "id": "devstral-2512", - "name": "Devstral 2", - "family": "devstral", + "openai/gpt-oss-20b": { + "id": "openai/gpt-oss-20b", + "name": "openai/gpt-oss-20b", + "family": "gpt-oss", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-12", - "release_date": "2025-12-09", - "last_updated": "2025-12-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 262144, "output": 262144 } + "release_date": "2025-08-13", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 8000 + }, + "cost": { + "input": 0.04, + "output": 0.18 + } }, - "codestral-latest": { - "id": "codestral-latest", - "name": "Codestral (latest)", - "family": "codestral", + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "name": "openai/gpt-oss-120b", + "family": "gpt-oss", "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-05-29", - "last_updated": "2025-01-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 0.9 }, - "limit": { "context": 256000, "output": 4096 } - }, - "mistral-large-latest": { - "id": "mistral-large-latest", - "name": "Mistral Large (latest)", - "family": "mistral-large", - "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-11", - "release_date": "2024-11-01", - "last_updated": "2025-12-02", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.5, "output": 1.5 }, - "limit": { "context": 262144, "output": 262144 } + "release_date": "2025-08-13", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 8000 + }, + "cost": { + "input": 0.05, + "output": 0.45 + } }, - "devstral-medium-latest": { - "id": "devstral-medium-latest", - "name": "Devstral 2 (latest)", - "family": "devstral", + "baidu/ERNIE-4.5-300B-A47B": { + "id": "baidu/ERNIE-4.5-300B-A47B", + "name": "baidu/ERNIE-4.5-300B-A47B", + "family": "ernie", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-12", - "release_date": "2025-12-02", - "last_updated": "2025-12-02", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 262144, "output": 262144 } + "release_date": "2025-07-02", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.28, + "output": 1.1 + } }, - "mistral-embed": { - "id": "mistral-embed", - "name": "Mistral Embed", - "family": "mistral-embed", + "THUDM/GLM-Z1-9B-0414": { + "id": "THUDM/GLM-Z1-9B-0414", + "name": "THUDM/GLM-Z1-9B-0414", + "family": "glm-z", "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2023-12-11", - "last_updated": "2023-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-04-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0 }, - "limit": { "context": 8000, "output": 3072 } + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.086, + "output": 0.086 + } }, - "mistral-large-2411": { - "id": "mistral-large-2411", - "name": "Mistral Large 2.1", - "family": "mistral-large", + "THUDM/GLM-4-9B-0414": { + "id": "THUDM/GLM-4-9B-0414", + "name": "THUDM/GLM-4-9B-0414", + "family": "glm", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-11", - "release_date": "2024-11-01", - "last_updated": "2024-11-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2, "output": 6 }, - "limit": { "context": 131072, "output": 16384 } + "release_date": "2025-04-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 33000, + "output": 33000 + }, + "cost": { + "input": 0.086, + "output": 0.086 + } }, - "mistral-small-2506": { - "id": "mistral-small-2506", - "name": "Mistral Small 3.2", - "family": "mistral-small", + "THUDM/GLM-4-32B-0414": { + "id": "THUDM/GLM-4-32B-0414", + "name": "THUDM/GLM-4-32B-0414", + "family": "glm", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-06-20", - "last_updated": "2025-06-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 128000, "output": 16384 } + "release_date": "2025-04-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 33000, + "output": 33000 + }, + "cost": { + "input": 0.27, + "output": 0.27 + } }, - "devstral-medium-2507": { - "id": "devstral-medium-2507", - "name": "Devstral Medium", - "family": "devstral", + "THUDM/GLM-Z1-32B-0414": { + "id": "THUDM/GLM-Z1-32B-0414", + "name": "THUDM/GLM-Z1-32B-0414", + "family": "glm-z", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-05", - "release_date": "2025-07-10", - "last_updated": "2025-07-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 128000, "output": 128000 } + "release_date": "2025-04-18", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.14, + "output": 0.57 + } }, - "magistral-medium-latest": { - "id": "magistral-medium-latest", - "name": "Magistral Medium (latest)", - "family": "magistral-medium", + "moonshotai/Kimi-K2-Thinking": { + "id": "moonshotai/Kimi-K2-Thinking", + "name": "moonshotai/Kimi-K2-Thinking", + "family": "kimi-thinking", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-03-17", - "last_updated": "2025-03-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2, "output": 5 }, - "limit": { "context": 128000, "output": 16384 } + "release_date": "2025-11-07", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.55, + "output": 2.5 + } }, - "labs-devstral-small-2512": { - "id": "labs-devstral-small-2512", - "name": "Devstral Small 2", - "family": "devstral", + "moonshotai/Kimi-K2.6": { + "id": "moonshotai/Kimi-K2.6", + "name": "moonshotai/Kimi-K2.6", + "family": "kimi", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2025-12", - "release_date": "2025-12-09", - "last_updated": "2025-12-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 256000 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.95, + "output": 4, + "cache_read": 0.16 + } }, - "mistral-medium-latest": { - "id": "mistral-medium-latest", - "name": "Mistral Medium (latest)", - "family": "mistral-medium", + "moonshotai/Kimi-K2-Instruct": { + "id": "moonshotai/Kimi-K2-Instruct", + "name": "moonshotai/Kimi-K2-Instruct", + "family": "kimi", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-05", - "release_date": "2025-05-07", - "last_updated": "2025-05-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 128000, "output": 16384 } + "release_date": "2025-07-13", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.58, + "output": 2.29 + } }, - "mistral-medium-2505": { - "id": "mistral-medium-2505", - "name": "Mistral Medium 3", - "family": "mistral-medium", - "attachment": true, + "moonshotai/Kimi-K2-Instruct-0905": { + "id": "moonshotai/Kimi-K2-Instruct-0905", + "name": "moonshotai/Kimi-K2-Instruct-0905", + "family": "kimi", + "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-05", - "release_date": "2025-05-07", - "last_updated": "2025-05-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-09-08", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.4, + "output": 2 + } }, - "mistral-nemo": { - "id": "mistral-nemo", - "name": "Mistral Nemo", - "family": "mistral-nemo", + "moonshotai/Kimi-K2.5": { + "id": "moonshotai/Kimi-K2.5", + "name": "moonshotai/Kimi-K2.5", + "family": "kimi", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2024-07-01", - "last_updated": "2024-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.15 }, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.45, + "output": 2.25 + } }, - "open-mixtral-8x22b": { - "id": "open-mixtral-8x22b", - "name": "Mixtral 8x22B", - "family": "mixtral", + "MiniMaxAI/MiniMax-M2.5": { + "id": "MiniMaxAI/MiniMax-M2.5", + "name": "MiniMaxAI/MiniMax-M2.5", + "family": "minimax", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-04-17", - "last_updated": "2024-04-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2, "output": 6 }, - "limit": { "context": 64000, "output": 64000 } + "release_date": "2026-02-15", + "last_updated": "2026-02-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 197000, + "output": 131000 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "ministral-8b-latest": { - "id": "ministral-8b-latest", - "name": "Ministral 8B (latest)", - "family": "ministral", + "MiniMaxAI/MiniMax-M2.1": { + "id": "MiniMaxAI/MiniMax-M2.1", + "name": "MiniMaxAI/MiniMax-M2.1", + "family": "minimax", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-10-01", - "last_updated": "2024-10-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.1 }, - "limit": { "context": 128000, "output": 128000 } + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 197000, + "output": 131000 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "open-mixtral-8x7b": { - "id": "open-mixtral-8x7b", - "name": "Mixtral 8x7B", - "family": "mixtral", + "ByteDance-Seed/Seed-OSS-36B-Instruct": { + "id": "ByteDance-Seed/Seed-OSS-36B-Instruct", + "name": "ByteDance-Seed/Seed-OSS-36B-Instruct", + "family": "seed", "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-01", - "release_date": "2023-12-11", - "last_updated": "2023-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.7, "output": 0.7 }, - "limit": { "context": 32000, "output": 32000 } - }, - "pixtral-12b": { - "id": "pixtral-12b", - "name": "Pixtral 12B", - "family": "pixtral", - "attachment": true, + "release_date": "2025-09-04", + "last_updated": "2025-11-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.21, + "output": 0.57 + } + } + } + }, + "vercel": { + "id": "vercel", + "env": ["AI_GATEWAY_API_KEY"], + "npm": "@ai-sdk/gateway", + "name": "Vercel AI Gateway", + "doc": "https://github.com/vercel/ai/tree/5eb85cc45a259553501f535b8ac79a77d0e79223/packages/gateway", + "models": { + "alibaba/qwen3-coder-plus": { + "id": "alibaba/qwen3-coder-plus", + "name": "Qwen3 Coder Plus", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-09", - "release_date": "2024-09-01", - "last_updated": "2024-09-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.15 }, - "limit": { "context": 128000, "output": 128000 } + "limit": { + "context": 1000000, + "output": 1000000 + }, + "cost": { + "input": 1, + "output": 5 + } }, - "mistral-small-latest": { - "id": "mistral-small-latest", - "name": "Mistral Small (latest)", - "family": "mistral-small", + "alibaba/qwen3.6-27b": { + "id": "alibaba/qwen3.6-27b", + "name": "Qwen 3.6 27B", + "family": "qwen3.6", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-06", - "release_date": "2026-03-16", - "last_updated": "2026-03-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 256000, "output": 256000 } + "release_date": "2026-04-22", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.6, + "output": 3.5999999999999996 + } }, - "open-mistral-7b": { - "id": "open-mistral-7b", - "name": "Mistral 7B", - "family": "mistral", + "alibaba/qwen3-embedding-8b": { + "id": "alibaba/qwen3-embedding-8b", + "name": "Qwen3 Embedding 8B", + "family": "qwen", "attachment": false, "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2025-06-05", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.05, + "output": 0 + } + }, + "alibaba/qwen-3-30b": { + "id": "alibaba/qwen-3-30b", + "name": "Qwen3-30B-A3B", + "family": "qwen", + "attachment": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2023-09-27", - "last_updated": "2023-09-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.25, "output": 0.25 }, - "limit": { "context": 8000, "output": 8000 } + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 40960, + "output": 16384 + }, + "cost": { + "input": 0.08, + "output": 0.29 + } }, - "devstral-small-2507": { - "id": "devstral-small-2507", - "name": "Devstral Small", - "family": "devstral", + "alibaba/qwen-3-235b": { + "id": "alibaba/qwen-3-235b", + "name": "Qwen3 235B A22B Instruct 2507", + "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-05", - "release_date": "2025-07-10", - "last_updated": "2025-07-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 128000, "output": 128000 } + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 40960, + "output": 16384 + }, + "cost": { + "input": 0.13, + "output": 0.6 + } }, - "mistral-medium-2508": { - "id": "mistral-medium-2508", - "name": "Mistral Medium 3.1", - "family": "mistral-medium", + "alibaba/qwen3.5-flash": { + "id": "alibaba/qwen3.5-flash", + "name": "Qwen 3.5 Flash", + "family": "qwen", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-05", - "release_date": "2025-08-12", - "last_updated": "2025-08-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-02-24", + "last_updated": "2026-02-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 262144, "output": 262144 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.001, + "cache_write": 0.125 + } }, - "mistral-large-2512": { - "id": "mistral-large-2512", - "name": "Mistral Large 3", - "family": "mistral-large", + "alibaba/qwen3.6-plus": { + "id": "alibaba/qwen3.6-plus", + "name": "Qwen 3.6 Plus", + "family": "qwen", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-11", - "release_date": "2024-11-01", - "last_updated": "2025-12-02", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.5, "output": 1.5 }, - "limit": { "context": 262144, "output": 262144 } - } - } - }, - "github-copilot": { - "id": "github-copilot", - "env": ["GITHUB_TOKEN"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.githubcopilot.com", - "name": "GitHub Copilot", - "doc": "https://docs.github.com/en/copilot", - "models": { - "gpt-5.2-codex": { - "id": "gpt-5.2-codex", - "name": "GPT-5.2-Codex", - "family": "gpt-codex", + "release_date": "2026-04-02", + "last_updated": "2026-04-03", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.09999999999999999, + "cache_write": 0.625 + } + }, + "alibaba/qwen3-max": { + "id": "alibaba/qwen3-max", + "name": "Qwen3 Max", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-09-23", + "last_updated": "2025-09-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 1.2, + "output": 6 + } }, - "gpt-5.1-codex-mini": { - "id": "gpt-5.1-codex-mini", - "name": "GPT-5.1-Codex-mini", - "family": "gpt-codex", + "alibaba/qwen3-embedding-0.6b": { + "id": "alibaba/qwen3-embedding-0.6b", + "name": "Qwen3 Embedding 0.6B", + "family": "qwen", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-11-14", + "last_updated": "2025-11-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 400000, "input": 128000, "output": 128000 } + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.01, + "output": 0 + } }, - "claude-opus-4.6": { - "id": "claude-opus-4.6", - "name": "Claude Opus 4.6", - "family": "claude-opus", - "attachment": true, + "alibaba/qwen-3-32b": { + "id": "alibaba/qwen-3-32b", + "name": "Qwen 3.32B", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 144000, "input": 128000, "output": 64000 } - }, - "gpt-5.4-mini": { - "id": "gpt-5.4-mini", - "name": "GPT-5.4 mini", - "family": "gpt-mini", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-17", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } - }, - "gpt-5": { - "id": "gpt-5", - "name": "GPT-5", - "family": "gpt", + "limit": { + "context": 40960, + "output": 16384 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } + }, + "alibaba/qwen-3.6-max-preview": { + "id": "alibaba/qwen-3.6-max-preview", + "name": "Qwen 3.6 Max Preview", + "family": "qwen", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 128000 } + "release_date": "2026-04-20", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 240000, + "output": 64000 + }, + "cost": { + "input": 1.3, + "output": 7.8, + "cache_read": 0.26, + "cache_write": 1.625 + } }, - "claude-haiku-4.5": { - "id": "claude-haiku-4.5", - "name": "Claude Haiku 4.5", - "family": "claude-haiku", - "attachment": true, + "alibaba/qwen3-next-80b-a3b-thinking": { + "id": "alibaba/qwen3-next-80b-a3b-thinking", + "name": "Qwen3 Next 80B A3B Thinking", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-02-28", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 144000, "input": 128000, "output": 32000 } + "knowledge": "2025-09", + "release_date": "2025-09-12", + "last_updated": "2025-09-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.15, + "output": 1.5 + } }, - "gemini-3.1-pro-preview": { - "id": "gemini-3.1-pro-preview", - "name": "Gemini 3.1 Pro Preview", - "family": "gemini-pro", + "alibaba/qwen3-vl-thinking": { + "id": "alibaba/qwen3-vl-thinking", + "name": "Qwen3 VL Thinking", + "family": "qwen", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-02-19", - "last_updated": "2026-02-19", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "input": 128000, "output": 64000 } + "knowledge": "2025-09", + "release_date": "2025-09-24", + "last_updated": "2025-09-24", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 129024 + }, + "cost": { + "input": 0.7, + "output": 8.4 + } }, - "gemini-3-pro-preview": { - "id": "gemini-3-pro-preview", - "name": "Gemini 3 Pro Preview", - "family": "gemini-pro", + "alibaba/qwen3-235b-a22b-thinking": { + "id": "alibaba/qwen3-235b-a22b-thinking", + "name": "Qwen3 235B A22B Thinking 2507", + "family": "qwen", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "input": 128000, "output": 64000 } + "limit": { + "context": 262114, + "output": 262114 + }, + "cost": { + "input": 0.3, + "output": 2.9 + } }, - "gpt-4o": { - "id": "gpt-4o", - "name": "GPT-4o", - "family": "gpt", - "attachment": true, + "alibaba/qwen3-next-80b-a3b-instruct": { + "id": "alibaba/qwen3-next-80b-a3b-instruct", + "name": "Qwen3 Next 80B A3B Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-09", - "release_date": "2024-05-13", - "last_updated": "2024-05-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "input": 64000, "output": 4096 } + "knowledge": "2025-04", + "release_date": "2025-09-12", + "last_updated": "2025-09-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0.09, + "output": 1.1 + } }, - "grok-code-fast-1": { - "id": "grok-code-fast-1", - "name": "Grok Code Fast 1", - "family": "grok", + "alibaba/qwen3-coder-next": { + "id": "alibaba/qwen3-coder-next", + "name": "Qwen3 Coder Next", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-08", - "release_date": "2025-08-27", - "last_updated": "2025-08-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-07-22", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "input": 128000, "output": 64000 } + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.5, + "output": 1.2 + } }, - "gpt-5.3-codex": { - "id": "gpt-5.3-codex", - "name": "GPT-5.3-Codex", - "family": "gpt-codex", + "alibaba/qwen3-embedding-4b": { + "id": "alibaba/qwen3-embedding-4b", + "name": "Qwen3 Embedding 4B", + "family": "qwen", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-02-24", - "last_updated": "2026-02-24", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-06-05", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.02, + "output": 0 + } }, - "gpt-5-mini": { - "id": "gpt-5-mini", - "name": "GPT-5-mini", - "family": "gpt-mini", - "attachment": true, + "alibaba/qwen3-max-thinking": { + "id": "alibaba/qwen3-max-thinking", + "name": "Qwen 3 Max Thinking", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-06", - "release_date": "2025-08-13", - "last_updated": "2025-08-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 264000, "input": 128000, "output": 64000 } + "knowledge": "2025-01", + "release_date": "2025-01", + "last_updated": "2025-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 65536 + }, + "cost": { + "input": 1.2, + "output": 6, + "cache_read": 0.24 + } }, - "gpt-5.1-codex-max": { - "id": "gpt-5.1-codex-max", - "name": "GPT-5.1-Codex-max", - "family": "gpt-codex", + "alibaba/qwen3-vl-235b-a22b-instruct": { + "id": "alibaba/qwen3-vl-235b-a22b-instruct", + "name": "Qwen3 VL 235B A22B Instruct", + "family": "qwen", "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-12-04", - "last_updated": "2025-12-04", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-09-24", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 400000, "input": 128000, "output": 128000 } + "limit": { + "context": 131072, + "output": 129024 + }, + "cost": { + "input": 0.39999999999999997, + "output": 1.5999999999999999 + } }, - "gpt-4.1": { - "id": "gpt-4.1", - "name": "GPT-4.1", - "family": "gpt", - "attachment": true, + "alibaba/qwen3-coder": { + "id": "alibaba/qwen3-coder", + "name": "Qwen3 Coder 480B A35B Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "input": 64000, "output": 16384 } + "limit": { + "context": 262144, + "output": 66536 + }, + "cost": { + "input": 0.38, + "output": 1.53 + } }, - "gpt-5.4": { - "id": "gpt-5.4", - "name": "GPT-5.4", - "family": "gpt", + "alibaba/qwen3-max-preview": { + "id": "alibaba/qwen3-max-preview", + "name": "Qwen3 Max Preview", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-09-23", + "last_updated": "2025-09-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 1.2, + "output": 6, + "cache_read": 0.24 + } }, - "gemini-3-flash-preview": { - "id": "gemini-3-flash-preview", - "name": "Gemini 3 Flash", - "family": "gemini-flash", + "alibaba/qwen3.5-plus": { + "id": "alibaba/qwen3.5-plus", + "name": "Qwen 3.5 Plus", + "family": "qwen", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, + "release_date": "2026-02-16", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "input": 128000, "output": 64000 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 0.4, + "output": 2.4, + "cache_read": 0.04, + "cache_write": 0.5 + } }, - "claude-sonnet-4.6": { - "id": "claude-sonnet-4.6", - "name": "Claude Sonnet 4.6", - "family": "claude-sonnet", - "attachment": true, + "alibaba/qwen-3-14b": { + "id": "alibaba/qwen-3-14b", + "name": "Qwen3-14B", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-02-17", - "last_updated": "2026-02-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 200000, "input": 128000, "output": 32000 } + "limit": { + "context": 40960, + "output": 16384 + }, + "cost": { + "input": 0.06, + "output": 0.24 + } }, - "gemini-2.5-pro": { - "id": "gemini-2.5-pro", - "name": "Gemini 2.5 Pro", - "family": "gemini-pro", + "alibaba/qwen3-vl-instruct": { + "id": "alibaba/qwen3-vl-instruct", + "name": "Qwen3 VL Instruct", + "family": "qwen", "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-03-20", - "last_updated": "2025-06-05", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "input": 128000, "output": 64000 } + "knowledge": "2025-04", + "release_date": "2025-09-24", + "last_updated": "2025-09-24", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 129024 + }, + "cost": { + "input": 0.7, + "output": 2.8 + } }, - "claude-sonnet-4": { - "id": "claude-sonnet-4", - "name": "Claude Sonnet 4", - "family": "claude-sonnet", - "attachment": true, + "alibaba/qwen3-coder-30b-a3b": { + "id": "alibaba/qwen3-coder-30b-a3b", + "name": "Qwen 3 Coder 30B A3B Instruct", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 216000, "input": 128000, "output": 16000 } + "limit": { + "context": 160000, + "output": 32768 + }, + "cost": { + "input": 0.07, + "output": 0.27 + } }, - "gpt-5.2": { - "id": "gpt-5.2", - "name": "GPT-5.2", - "family": "gpt", + "perplexity/sonar-pro": { + "id": "perplexity/sonar-pro", + "name": "Sonar Pro", + "family": "sonar-pro", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-09", + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 264000, "input": 128000, "output": 64000 } + "limit": { + "context": 200000, + "output": 8000 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "gpt-5.1": { - "id": "gpt-5.1", - "name": "GPT-5.1", - "family": "gpt", + "perplexity/sonar": { + "id": "perplexity/sonar", + "name": "Sonar", + "family": "sonar", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-02", + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 264000, "input": 128000, "output": 64000 } + "limit": { + "context": 127000, + "output": 8000 + }, + "cost": { + "input": 1, + "output": 1 + } }, - "claude-opus-4.5": { - "id": "claude-opus-4.5", - "name": "Claude Opus 4.5", - "family": "claude-opus", - "attachment": true, + "perplexity/sonar-reasoning": { + "id": "perplexity/sonar-reasoning", + "name": "Sonar Reasoning", + "family": "sonar-reasoning", + "attachment": false, "reasoning": true, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-11-24", - "last_updated": "2025-08-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-09", + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 160000, "input": 128000, "output": 32000 } + "limit": { + "context": 127000, + "output": 8000 + }, + "cost": { + "input": 1, + "output": 5 + } }, - "claude-opus-41": { - "id": "claude-opus-41", - "name": "Claude Opus 4.1", - "family": "claude-opus", - "attachment": true, + "perplexity/sonar-reasoning-pro": { + "id": "perplexity/sonar-reasoning-pro", + "name": "Sonar Reasoning Pro", + "family": "sonar-reasoning", + "attachment": false, "reasoning": true, "tool_call": false, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-09", + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 80000, "output": 16000 } + "limit": { + "context": 127000, + "output": 8000 + }, + "cost": { + "input": 2, + "output": 8 + } }, - "claude-sonnet-4.5": { - "id": "claude-sonnet-4.5", - "name": "Claude Sonnet 4.5", - "family": "claude-sonnet", - "attachment": true, + "deepseek/deepseek-v3.2-thinking": { + "id": "deepseek/deepseek-v3.2-thinking", + "name": "DeepSeek V3.2 Thinking", + "family": "deepseek-thinking", + "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-07", + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 144000, "input": 128000, "output": 32000 } + "limit": { + "context": 128000, + "output": 64000 + }, + "cost": { + "input": 0.28, + "output": 0.42, + "cache_read": 0.03 + } }, - "gpt-5.1-codex": { - "id": "gpt-5.1-codex", - "name": "GPT-5.1-Codex", - "family": "gpt-codex", + "deepseek/deepseek-v3.2-exp": { + "id": "deepseek/deepseek-v3.2-exp", + "name": "DeepSeek V3.2 Exp", + "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 400000, "input": 128000, "output": 128000 } - } - } - }, - "scaleway": { - "id": "scaleway", - "env": ["SCALEWAY_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.scaleway.ai/v1", - "name": "Scaleway", - "doc": "https://www.scaleway.com/en/docs/generative-apis/", - "models": { - "qwen3-coder-30b-a3b-instruct": { - "id": "qwen3-coder-30b-a3b-instruct", - "name": "Qwen3-Coder 30B-A3B Instruct", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-04", - "last_updated": "2026-03-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 128000, "output": 32768 } - }, - "qwen3-235b-a22b-instruct-2507": { - "id": "qwen3-235b-a22b-instruct-2507", - "name": "Qwen3 235B A22B Instruct 2507", - "family": "qwen", - "attachment": true, - "reasoning": false, - "tool_call": true, "temperature": true, - "release_date": "2025-07-01", - "last_updated": "2026-03-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.75, "output": 2.25 }, - "limit": { "context": 260000, "output": 16384 } - }, - "qwen3-embedding-8b": { - "id": "qwen3-embedding-8b", - "name": "Qwen3 Embedding 8B", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": false, - "release_date": "2025-25-11", - "last_updated": "2026-03-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-09", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0 }, - "limit": { "context": 32768, "output": 4096 } + "limit": { + "context": 163840, + "output": 163840 + }, + "cost": { + "input": 0.27, + "output": 0.4 + } }, - "mistral-nemo-instruct-2407": { - "id": "mistral-nemo-instruct-2407", - "name": "Mistral Nemo Instruct 2407", - "family": "mistral-nemo", - "attachment": true, - "reasoning": false, + "deepseek/deepseek-v3.1": { + "id": "deepseek/deepseek-v3.1", + "name": "DeepSeek-V3.1", + "family": "deepseek", + "attachment": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-07-25", - "last_updated": "2026-03-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 128000, "output": 8192 } + "knowledge": "2024-07", + "release_date": "2025-08-21", + "last_updated": "2025-08-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 163840, + "output": 128000 + }, + "cost": { + "input": 0.3, + "output": 1 + } }, - "deepseek-r1-distill-llama-70b": { - "id": "deepseek-r1-distill-llama-70b", - "name": "DeepSeek R1 Distill Llama 70B", - "family": "deepseek-thinking", + "deepseek/deepseek-v4-flash": { + "id": "deepseek/deepseek-v4-flash", + "name": "DeepSeek V4 Flash", + "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-01-20", - "last_updated": "2026-03-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-23", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.9, "output": 0.9 }, - "limit": { "context": 32000, "output": 8196 } + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 0.14, + "output": 0.28, + "cache_read": 0.028 + } }, - "devstral-2-123b-instruct-2512": { - "id": "devstral-2-123b-instruct-2512", - "name": "Devstral 2 123B Instruct (2512)", - "family": "devstral", + "deepseek/deepseek-v4-pro": { + "id": "deepseek/deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-01-07", - "last_updated": "2026-03-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-23", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 256000, "output": 16384 } + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 1.74, + "output": 3.48, + "cache_read": 0.145 + } }, - "llama-3.3-70b-instruct": { - "id": "llama-3.3-70b-instruct", - "name": "Llama-3.3-70B-Instruct", - "family": "llama", - "attachment": true, + "deepseek/deepseek-v3.2": { + "id": "deepseek/deepseek-v3.2", + "name": "DeepSeek V3.2", + "family": "deepseek", + "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-12-06", - "last_updated": "2026-03-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.9, "output": 0.9 }, - "limit": { "context": 100000, "output": 16384 } + "knowledge": "2024-07", + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 163842, + "output": 8000 + }, + "cost": { + "input": 0.27, + "output": 0.4, + "cache_read": 0.22 + } }, - "gemma-3-27b-it": { - "id": "gemma-3-27b-it", - "name": "Gemma-3-27B-IT", - "family": "gemma", - "attachment": true, - "reasoning": true, + "deepseek/deepseek-v3": { + "id": "deepseek/deepseek-v3", + "name": "DeepSeek V3 0324", + "family": "deepseek", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2024-12-01", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-07", + "release_date": "2024-12-26", + "last_updated": "2024-12-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 0.5 }, - "limit": { "context": 40000, "output": 8192 } + "limit": { + "context": 163840, + "output": 16384 + }, + "cost": { + "input": 0.77, + "output": 0.77 + } }, - "llama-3.1-8b-instruct": { - "id": "llama-3.1-8b-instruct", - "name": "Llama 3.1 8B Instruct", - "family": "llama", + "deepseek/deepseek-v3.1-terminus": { + "id": "deepseek/deepseek-v3.1-terminus", + "name": "DeepSeek V3.1 Terminus", + "family": "deepseek", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2025-01-01", - "last_updated": "2026-03-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-09-22", + "last_updated": "2025-09-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.27, + "output": 1 + } }, - "gpt-oss-120b": { - "id": "gpt-oss-120b", - "name": "GPT-OSS 120B", - "family": "gpt-oss", - "attachment": true, - "reasoning": false, + "deepseek/deepseek-r1": { + "id": "deepseek/deepseek-r1", + "name": "DeepSeek-R1", + "family": "deepseek-thinking", + "attachment": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-01-01", - "last_updated": "2026-03-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 128000, "output": 32768 } + "knowledge": "2024-07", + "release_date": "2025-01-20", + "last_updated": "2025-05-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 1.35, + "output": 5.4 + } }, - "bge-multilingual-gemma2": { - "id": "bge-multilingual-gemma2", - "name": "BGE Multilingual Gemma2", - "family": "gemma", + "arcee-ai/trinity-mini": { + "id": "arcee-ai/trinity-mini", + "name": "Trinity Mini", + "family": "trinity", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": false, - "release_date": "2024-07-26", - "last_updated": "2025-06-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-12", + "last_updated": "2025-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0 }, - "limit": { "context": 8191, "output": 3072 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.05, + "output": 0.15 + } }, - "pixtral-12b-2409": { - "id": "pixtral-12b-2409", - "name": "Pixtral 12B 2409", - "family": "pixtral", - "attachment": true, - "reasoning": false, + "arcee-ai/trinity-large-thinking": { + "id": "arcee-ai/trinity-large-thinking", + "name": "Trinity Large Thinking", + "family": "trinity", + "attachment": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-09-25", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-04-01", + "last_updated": "2026-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 262100, + "output": 80000 + }, + "cost": { + "input": 0.25, + "output": 0.8999999999999999 + } }, - "voxtral-small-24b-2507": { - "id": "voxtral-small-24b-2507", - "name": "Voxtral Small 24B 2507", - "family": "voxtral", - "attachment": true, + "arcee-ai/trinity-large-preview": { + "id": "arcee-ai/trinity-large-preview", + "name": "Trinity Large Preview", + "family": "trinity", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2025-07-01", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "audio"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.35 }, - "limit": { "context": 32000, "output": 16384 } + "knowledge": "2024-10", + "release_date": "2025-01", + "last_updated": "2025-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131000, + "output": 131000 + }, + "cost": { + "input": 0.25, + "output": 1 + } }, - "whisper-large-v3": { - "id": "whisper-large-v3", - "name": "Whisper Large v3", - "family": "whisper", + "recraft/recraft-v3": { + "id": "recraft/recraft-v3", + "name": "Recraft V3", + "family": "recraft", "attachment": false, "reasoning": false, "tool_call": false, "temperature": false, - "knowledge": "2023-09", - "release_date": "2023-09-01", - "last_updated": "2026-03-17", - "modalities": { "input": ["audio"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.003, "output": 0 }, - "limit": { "context": 0, "output": 8192 } + "release_date": "2024-10", + "last_updated": "2024-10", + "modalities": { + "input": ["text"], + "output": ["image"] + }, + "open_weights": false, + "limit": { + "context": 512, + "output": 0 + } }, - "qwen3.5-397b-a17b": { - "id": "qwen3.5-397b-a17b", - "name": "Qwen3.5 397B A17B", - "family": "qwen", + "recraft/recraft-v2": { + "id": "recraft/recraft-v2", + "name": "Recraft V2", + "family": "recraft", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-03-17", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 3.6 }, - "limit": { "context": 256000, "output": 16384 } + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2024-03", + "last_updated": "2024-03", + "modalities": { + "input": ["text"], + "output": ["image"] + }, + "open_weights": false, + "limit": { + "context": 512, + "output": 0 + } }, - "mistral-small-3.2-24b-instruct-2506": { - "id": "mistral-small-3.2-24b-instruct-2506", - "name": "Mistral Small 3.2 24B Instruct (2506)", - "family": "mistral-small", + "voyage/voyage-3-large": { + "id": "voyage/voyage-3-large", + "name": "voyage-3-large", + "family": "voyage", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2025-06-20", - "last_updated": "2026-03-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.35 }, - "limit": { "context": 128000, "output": 32768 } - } - } - }, - "iflowcn": { - "id": "iflowcn", - "env": ["IFLOW_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://apis.iflow.cn/v1", - "name": "iFlow", - "doc": "https://platform.iflow.cn/en/docs", - "models": { - "deepseek-r1": { - "id": "deepseek-r1", - "name": "DeepSeek-R1", - "family": "deepseek-thinking", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 32000 } + "tool_call": false, + "temperature": false, + "release_date": "2024-09", + "last_updated": "2024-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8192, + "output": 1536 + }, + "cost": { + "input": 0.18, + "output": 0 + } }, - "qwen3-max-preview": { - "id": "qwen3-max-preview", - "name": "Qwen3-Max-Preview", - "family": "qwen", + "voyage/voyage-4-large": { + "id": "voyage/voyage-4-large", + "name": "voyage-4-large", + "family": "voyage", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-06", + "last_updated": "2026-03-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 32000 } + "limit": { + "context": 32000, + "output": 0 + } }, - "deepseek-v3": { - "id": "deepseek-v3", - "name": "DeepSeek-V3", - "family": "deepseek", + "voyage/voyage-3.5-lite": { + "id": "voyage/voyage-3.5-lite", + "name": "voyage-3.5-lite", + "family": "voyage", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-12-26", - "last_updated": "2024-12-26", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 32000 } + "tool_call": false, + "temperature": false, + "release_date": "2025-05-20", + "last_updated": "2025-05-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8192, + "output": 1536 + }, + "cost": { + "input": 0.02, + "output": 0 + } }, - "kimi-k2-0905": { - "id": "kimi-k2-0905", - "name": "Kimi-K2-0905", - "family": "kimi", + "voyage/voyage-code-3": { + "id": "voyage/voyage-code-3", + "name": "voyage-code-3", + "family": "voyage", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "temperature": false, + "release_date": "2024-09", + "last_updated": "2024-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 64000 } + "limit": { + "context": 8192, + "output": 1536 + }, + "cost": { + "input": 0.18, + "output": 0 + } }, - "kimi-k2": { - "id": "kimi-k2", - "name": "Kimi-K2", - "family": "kimi", + "voyage/voyage-finance-2": { + "id": "voyage/voyage-finance-2", + "name": "voyage-finance-2", + "family": "voyage", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "temperature": false, + "release_date": "2024-03", + "last_updated": "2024-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 64000 } + "limit": { + "context": 8192, + "output": 1536 + }, + "cost": { + "input": 0.12, + "output": 0 + } }, - "qwen3-235b": { - "id": "qwen3-235b", - "name": "Qwen3-235B-A22B", - "family": "qwen", + "voyage/voyage-4-lite": { + "id": "voyage/voyage-4-lite", + "name": "voyage-4-lite", + "family": "voyage", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 32000 } + "release_date": "2026-03-06", + "last_updated": "2026-03-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32000, + "output": 0 + } }, - "glm-4.6": { - "id": "glm-4.6", - "name": "GLM-4.6", - "family": "glm", + "voyage/voyage-4": { + "id": "voyage/voyage-4", + "name": "voyage-4", + "family": "voyage", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-12-01", - "last_updated": "2025-11-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-06", + "last_updated": "2026-03-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 200000, "output": 128000 } + "limit": { + "context": 32000, + "output": 0 + } }, - "qwen3-235b-a22b-instruct": { - "id": "qwen3-235b-a22b-instruct", - "name": "Qwen3-235B-A22B-Instruct", - "family": "qwen", + "voyage/voyage-code-2": { + "id": "voyage/voyage-code-2", + "name": "voyage-code-2", + "family": "voyage", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-01", - "last_updated": "2025-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 64000 } + "tool_call": false, + "temperature": false, + "release_date": "2024-01", + "last_updated": "2024-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8192, + "output": 1536 + }, + "cost": { + "input": 0.12, + "output": 0 + } }, - "qwen3-235b-a22b-thinking-2507": { - "id": "qwen3-235b-a22b-thinking-2507", - "name": "Qwen3-235B-A22B-Thinking", - "family": "qwen", + "voyage/voyage-law-2": { + "id": "voyage/voyage-law-2", + "name": "voyage-law-2", + "family": "voyage", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-01", - "last_updated": "2025-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 64000 } + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2024-03", + "last_updated": "2024-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8192, + "output": 1536 + }, + "cost": { + "input": 0.12, + "output": 0 + } }, - "qwen3-vl-plus": { - "id": "qwen3-vl-plus", - "name": "Qwen3-VL-Plus", - "family": "qwen", - "attachment": true, + "voyage/voyage-3.5": { + "id": "voyage/voyage-3.5", + "name": "voyage-3.5", + "family": "voyage", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "temperature": false, + "release_date": "2025-05-20", + "last_updated": "2025-05-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 32000 } + "limit": { + "context": 8192, + "output": 1536 + }, + "cost": { + "input": 0.06, + "output": 0 + } }, - "qwen3-coder-plus": { - "id": "qwen3-coder-plus", - "name": "Qwen3-Coder-Plus", - "family": "qwen", + "morph/morph-v3-large": { + "id": "morph/morph-v3-large", + "name": "Morph v3 Large", + "family": "morph", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-01", - "last_updated": "2025-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 64000 } + "tool_call": false, + "temperature": false, + "release_date": "2024-08-15", + "last_updated": "2024-08-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32000, + "output": 32000 + }, + "cost": { + "input": 0.9, + "output": 1.9 + } }, - "qwen3-max": { - "id": "qwen3-max", - "name": "Qwen3-Max", - "family": "qwen", + "morph/morph-v3-fast": { + "id": "morph/morph-v3-fast", + "name": "Morph v3 Fast", + "family": "morph", "attachment": false, "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2024-08-15", + "last_updated": "2024-08-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16000, + "output": 16000 + }, + "cost": { + "input": 0.8, + "output": 1.2 + } + }, + "zai/glm-5v-turbo": { + "id": "zai/glm-5v-turbo", + "name": "GLM 5V Turbo", + "family": "glm", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-01", + "last_updated": "2026-04-03", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 32000 } + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 1.2, + "output": 4, + "cache_read": 0.24 + } }, - "qwen3-32b": { - "id": "qwen3-32b", - "name": "Qwen3-32B", - "family": "qwen", + "zai/glm-4.7": { + "id": "zai/glm-4.7", + "name": "GLM 4.7", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": true, "temperature": true, "knowledge": "2024-10", - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 32000 } + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 202752, + "output": 120000 + }, + "cost": { + "input": 0.43, + "output": 1.75, + "cache_read": 0.08 + } }, - "deepseek-v3.2": { - "id": "deepseek-v3.2", - "name": "DeepSeek-V3.2-Exp", - "family": "deepseek", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 64000 } - } - } - }, - "venice": { - "id": "venice", - "env": ["VENICE_API_KEY"], - "npm": "venice-ai-sdk-provider", - "name": "Venice AI", - "doc": "https://docs.venice.ai", - "models": { - "qwen3-235b-a22b-instruct-2507": { - "id": "qwen3-235b-a22b-instruct-2507", - "name": "Qwen 3 235B A22B Instruct 2507", - "family": "qwen", + "zai/glm-5": { + "id": "zai/glm-5", + "name": "GLM-5", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-04-29", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-12", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.75 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 202800, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3.2, + "cache_read": 0.2 + } }, - "qwen3-coder-480b-a35b-instruct-turbo": { - "id": "qwen3-coder-480b-a35b-instruct-turbo", - "name": "Qwen 3 Coder 480B Turbo", - "family": "qwen", + "zai/glm-4.7-flashx": { + "id": "zai/glm-4.7-flashx", + "name": "GLM 4.7 FlashX", + "family": "glm-flash", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": true, "temperature": true, - "release_date": "2026-01-27", - "last_updated": "2026-02-26", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-01", + "last_updated": "2025-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.35, "output": 1.5, "cache_read": 0.04 }, - "limit": { "context": 256000, "output": 65536 } + "limit": { + "context": 200000, + "output": 128000 + }, + "cost": { + "input": 0.06, + "output": 0.4, + "cache_read": 0.01 + } }, - "aion-labs.aion-2-0": { - "id": "aion-labs.aion-2-0", - "name": "Aion 2.0", - "family": "o", - "attachment": false, - "reasoning": false, - "tool_call": false, + "zai/glm-5.1": { + "id": "zai/glm-5.1", + "name": "GLM 5.1", + "family": "glm", + "attachment": true, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2026-03-24", - "last_updated": "2026-03-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-07", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 2, "cache_read": 0.25 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 202752, + "output": 202752 + }, + "cost": { + "input": 1.4, + "output": 4.4, + "cache_read": 0.26 + } }, - "qwen3-next-80b": { - "id": "qwen3-next-80b", - "name": "Qwen 3 Next 80b", - "family": "qwen", - "attachment": false, - "reasoning": false, + "zai/glm-4.6v-flash": { + "id": "zai/glm-4.6v-flash", + "name": "GLM-4.6V-Flash", + "family": "glm", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-04-29", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.35, "output": 1.9 }, - "limit": { "context": 256000, "output": 16384 } + "knowledge": "2024-10", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 24000 + } }, - "kimi-k2-thinking": { - "id": "kimi-k2-thinking", - "name": "Kimi K2 Thinking", - "family": "kimi-thinking", + "zai/glm-4.5": { + "id": "zai/glm-4.5", + "name": "GLM 4.5", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-12-10", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.75, "output": 3.2, "cache_read": 0.375 }, - "limit": { "context": 256000, "output": 65536 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 2.2 + } }, - "zai-org-glm-4.7": { - "id": "zai-org-glm-4.7", - "name": "GLM 4.7", - "family": "glm", + "zai/glm-4.5-air": { + "id": "zai/glm-4.5-air", + "name": "GLM 4.5 Air", + "family": "glm-air", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, "knowledge": "2025-04", - "release_date": "2025-12-24", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.55, "output": 2.65, "cache_read": 0.11 }, - "limit": { "context": 198000, "output": 16384 } + "limit": { + "context": 128000, + "output": 96000 + }, + "cost": { + "input": 0.2, + "output": 1.1 + } }, - "openai-gpt-53-codex": { - "id": "openai-gpt-53-codex", - "name": "GPT-5.3 Codex", - "family": "gpt-codex", - "attachment": true, + "zai/glm-5-turbo": { + "id": "zai/glm-5-turbo", + "name": "GLM 5 Turbo", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-02-24", - "last_updated": "2026-03-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-03-15", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.19, "output": 17.5, "cache_read": 0.219 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 202800, + "output": 131100 + }, + "cost": { + "input": 1.2, + "output": 4, + "cache_read": 0.24 + } }, - "qwen3-5-35b-a3b": { - "id": "qwen3-5-35b-a3b", - "name": "Qwen 3.5 35B A3B", - "family": "qwen", + "zai/glm-4.5v": { + "id": "zai/glm-4.5v", + "name": "GLM 4.5V", + "family": "glm", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-02-25", - "last_updated": "2026-03-09", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2025-08", + "release_date": "2025-08-11", + "last_updated": "2025-08-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3125, "output": 1.25, "cache_read": 0.15625 }, - "limit": { "context": 256000, "output": 65536 } + "limit": { + "context": 66000, + "output": 66000 + }, + "cost": { + "input": 0.6, + "output": 1.8 + } }, - "olafangensan-glm-4.7-flash-heretic": { - "id": "olafangensan-glm-4.7-flash-heretic", - "name": "GLM 4.7 Flash Heretic", - "family": "glm-flash", + "zai/glm-4.6": { + "id": "zai/glm-4.6", + "name": "GLM 4.6", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": true, "temperature": true, - "release_date": "2026-02-04", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.14, "output": 0.8 }, - "limit": { "context": 200000, "output": 24000 } + "limit": { + "context": 200000, + "output": 96000 + }, + "cost": { + "input": 0.45, + "output": 1.8 + } }, - "grok-code-fast-1": { - "id": "grok-code-fast-1", - "name": "Grok Code Fast 1", - "family": "grok", - "attachment": false, + "zai/glm-4.6v": { + "id": "zai/glm-4.6v", + "name": "GLM-4.6V", + "family": "glm", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-12-01", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 1.87, "cache_read": 0.03 }, - "limit": { "context": 256000, "output": 10000 } + "limit": { + "context": 128000, + "output": 24000 + }, + "cost": { + "input": 0.3, + "output": 0.9, + "cache_read": 0.05 + } }, - "openai-gpt-4o-2024-11-20": { - "id": "openai-gpt-4o-2024-11-20", - "name": "GPT-4o", - "family": "gpt", - "attachment": true, - "reasoning": false, + "zai/glm-4.7-flash": { + "id": "zai/glm-4.7-flash", + "name": "GLM 4.7 Flash", + "family": "glm", + "attachment": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-02-28", - "last_updated": "2026-03-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-03-13", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3.125, "output": 12.5 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 200000, + "output": 131000 + }, + "cost": { + "input": 0.07, + "output": 0.39999999999999997 + } }, - "grok-4-20-beta": { - "id": "grok-4-20-beta", - "name": "Grok 4.20 Beta", - "family": "grok-beta", - "attachment": true, - "reasoning": true, + "cohere/command-a": { + "id": "cohere/command-a", + "name": "Command A", + "family": "command", + "attachment": false, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-03-12", - "last_updated": "2026-03-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-03-13", + "last_updated": "2025-03-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, + "limit": { + "context": 256000, + "output": 8000 + }, "cost": { "input": 2.5, - "output": 7.5, - "cache_read": 0.25, - "context_over_200k": { "input": 5, "output": 15, "cache_read": 0.25 } - }, - "limit": { "context": 2000000, "output": 128000 } + "output": 10 + } }, - "grok-4-20-multi-agent-beta": { - "id": "grok-4-20-multi-agent-beta", - "name": "Grok 4.20 Multi-Agent Beta", - "family": "grok-beta", - "attachment": true, - "reasoning": true, + "cohere/embed-v4.0": { + "id": "cohere/embed-v4.0", + "name": "Embed v4.0", + "family": "cohere-embed", + "attachment": false, + "reasoning": false, "tool_call": false, - "structured_output": true, - "temperature": true, - "release_date": "2026-03-12", - "last_updated": "2026-03-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-04-15", + "last_updated": "2025-04-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { - "input": 2.5, - "output": 7.5, - "cache_read": 0.25, - "context_over_200k": { "input": 5, "output": 15, "cache_read": 0.25 } + "limit": { + "context": 8192, + "output": 1536 }, - "limit": { "context": 2000000, "output": 128000 } + "cost": { + "input": 0.12, + "output": 0 + } }, - "claude-opus-4-6": { - "id": "claude-opus-4-6", - "name": "Claude Opus 4.6", - "family": "claude-opus", - "attachment": true, + "prime-intellect/intellect-3": { + "id": "prime-intellect/intellect-3", + "name": "INTELLECT 3", + "family": "intellect", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-02-05", - "last_updated": "2026-03-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-11-26", + "last_updated": "2025-11-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 6, "output": 30, "cache_read": 0.6, "cache_write": 7.5 }, - "limit": { "context": 1000000, "output": 128000 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.2, + "output": 1.1 + } }, - "claude-sonnet-4-6": { - "id": "claude-sonnet-4-6", - "name": "Claude Sonnet 4.6", - "family": "claude-sonnet", + "xai/grok-4.3": { + "id": "xai/grok-4.3", + "name": "Grok 4.3", + "family": "grok", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-02-17", - "last_updated": "2026-03-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-04-30", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3.6, "output": 18, "cache_read": 0.36, "cache_write": 4.5 }, - "limit": { "context": 1000000, "output": 64000 } + "limit": { + "context": 1000000, + "output": 1000000 + }, + "cost": { + "input": 1.25, + "output": 2.5, + "cache_read": 0.19999999999999998 + } }, - "openai-gpt-54-pro": { - "id": "openai-gpt-54-pro", - "name": "GPT-5.4 Pro", - "family": "gpt-pro", + "xai/grok-4.20-non-reasoning": { + "id": "xai/grok-4.20-non-reasoning", + "name": "Grok 4.20 Non-Reasoning", + "family": "grok", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-03-05", - "last_updated": "2026-03-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-03-09", + "last_updated": "2026-03-23", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 37.5, "output": 225, "context_over_200k": { "input": 75, "output": 337.5 } }, - "limit": { "context": 1000000, "output": 128000 } + "limit": { + "context": 2000000, + "output": 2000000 + }, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.19999999999999998 + } }, - "kimi-k2-5": { - "id": "kimi-k2-5", - "name": "Kimi K2.5", - "family": "kimi", + "xai/grok-4.20-non-reasoning-beta": { + "id": "xai/grok-4.20-non-reasoning-beta", + "name": "Grok 4.20 Beta Non-Reasoning", + "family": "grok", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2026-01-27", - "last_updated": "2026-03-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.56, "output": 3.5, "cache_read": 0.11 }, - "limit": { "context": 256000, "output": 65536 } + "release_date": "2026-03-11", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 2000000 + }, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.19999999999999998 + } }, - "claude-opus-45": { - "id": "claude-opus-45", - "name": "Claude Opus 4.5", - "family": "claude-opus", + "xai/grok-4.20-reasoning": { + "id": "xai/grok-4.20-reasoning", + "name": "Grok 4.20 Reasoning", + "family": "grok", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-03", - "release_date": "2025-12-06", - "last_updated": "2026-01-28", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2026-03-09", + "last_updated": "2026-03-23", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 6, "output": 30, "cache_read": 0.6, "cache_write": 7.5 }, - "limit": { "context": 198000, "output": 49500 } + "limit": { + "context": 2000000, + "output": 2000000 + }, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.19999999999999998 + } }, - "llama-3.2-3b": { - "id": "llama-3.2-3b", - "name": "Llama 3.2 3B", - "family": "llama", + "xai/grok-imagine-image": { + "id": "xai/grok-imagine-image", + "name": "Grok Imagine Image", + "family": "grok", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-10-03", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 128000, "output": 4096 } - }, - "qwen3-5-9b": { - "id": "qwen3-5-9b", - "name": "Qwen 3.5 9B", - "family": "qwen", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, + "tool_call": false, "temperature": true, - "release_date": "2026-03-05", - "last_updated": "2026-03-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.05, "output": 0.15 }, - "limit": { "context": 256000, "output": 65536 } + "release_date": "2026-01-28", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text"], + "output": ["text", "image"] + }, + "open_weights": false, + "limit": { + "context": 0, + "output": 0 + } }, - "minimax-m27": { - "id": "minimax-m27", - "name": "MiniMax M2.7", - "family": "minimax", + "xai/grok-4.20-multi-agent-beta": { + "id": "xai/grok-4.20-multi-agent-beta", + "name": "Grok 4.20 Multi Agent Beta", + "family": "grok", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.375, "output": 1.5, "cache_read": 0.075 }, - "limit": { "context": 198000, "output": 32768 } + "release_date": "2026-03-11", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 2000000 + }, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.19999999999999998 + } }, - "venice-uncensored": { - "id": "venice-uncensored", - "name": "Venice Uncensored 1.1", - "family": "venice", + "xai/grok-imagine-image-pro": { + "id": "xai/grok-imagine-image-pro", + "name": "Grok Imagine Image Pro", + "family": "grok", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2025-03-18", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.9 }, - "limit": { "context": 32000, "output": 8192 } + "release_date": "2026-01-28", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text"], + "output": ["text", "image"] + }, + "open_weights": false, + "limit": { + "context": 0, + "output": 0 + } }, - "openai-gpt-oss-120b": { - "id": "openai-gpt-oss-120b", - "name": "OpenAI GPT OSS 120B", - "family": "gpt-oss", + "xai/grok-4-fast-reasoning": { + "id": "xai/grok-4-fast-reasoning", + "name": "Grok 4 Fast Reasoning", + "family": "grok", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 256000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } + }, + "xai/grok-4.1-fast-non-reasoning": { + "id": "xai/grok-4.1-fast-non-reasoning", + "name": "Grok 4.1 Fast Non-Reasoning", + "family": "grok", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-11-06", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.07, "output": 0.3 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2024-10", + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "gemini-3-flash-preview": { - "id": "gemini-3-flash-preview", - "name": "Gemini 3 Flash Preview", - "family": "gemini-flash", + "xai/grok-4.20-reasoning-beta": { + "id": "xai/grok-4.20-reasoning-beta", + "name": "Grok 4.20 Beta Reasoning", + "family": "grok", "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-12-19", - "last_updated": "2026-03-12", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "release_date": "2026-03-11", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.7, "output": 3.75, "cache_read": 0.07 }, - "limit": { "context": 256000, "output": 65536 } + "limit": { + "context": 2000000, + "output": 2000000 + }, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.19999999999999998 + } }, - "openai-gpt-52": { - "id": "openai-gpt-52", - "name": "GPT-5.2", - "family": "gpt", + "xai/grok-4.20-multi-agent": { + "id": "xai/grok-4.20-multi-agent", + "name": "Grok 4.20 Multi-Agent", + "family": "grok", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-08-31", - "release_date": "2025-12-13", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-09", + "last_updated": "2026-03-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.19, "output": 17.5, "cache_read": 0.219 }, - "limit": { "context": 256000, "output": 65536 } + "limit": { + "context": 2000000, + "output": 2000000 + }, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.19999999999999998 + } }, - "gemini-3-1-pro-preview": { - "id": "gemini-3-1-pro-preview", - "name": "Gemini 3.1 Pro Preview", - "family": "gemini-pro", - "attachment": true, + "xai/grok-4.1-fast-reasoning": { + "id": "xai/grok-4.1-fast-reasoning", + "name": "Grok 4.1 Fast Reasoning", + "family": "grok", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-02-19", - "last_updated": "2026-03-12", - "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { - "input": 2.5, - "output": 15, - "cache_read": 0.5, - "cache_write": 0.5, - "context_over_200k": { "input": 5, "output": 22.5, "cache_read": 0.5 } + "limit": { + "context": 2000000, + "output": 30000 }, - "limit": { "context": 1000000, "output": 32768 } + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "qwen3-coder-480b-a35b-instruct": { - "id": "qwen3-coder-480b-a35b-instruct", - "name": "Qwen 3 Coder 480b", - "family": "qwen", - "attachment": false, + "xai/grok-4-fast-non-reasoning": { + "id": "xai/grok-4-fast-non-reasoning", + "name": "Grok 4 Fast (Non-Reasoning)", + "family": "grok", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, "knowledge": "2025-07", - "release_date": "2025-04-29", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.75, "output": 3 }, - "limit": { "context": 256000, "output": 65536 } + "release_date": "2025-09-19", + "last_updated": "2025-09-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "zai-org-glm-4.6": { - "id": "zai-org-glm-4.6", - "name": "GLM 4.6", - "family": "glm", + "xai/grok-3": { + "id": "xai/grok-3", + "name": "Grok 3", + "family": "grok", "attachment": false, "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "release_date": "2024-04-01", - "last_updated": "2026-03-16", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.85, "output": 2.75, "cache_read": 0.3 }, - "limit": { "context": 198000, "output": 16384 } + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75 + } }, - "qwen3-235b-a22b-thinking-2507": { - "id": "qwen3-235b-a22b-thinking-2507", - "name": "Qwen 3 235B A22B Thinking 2507", - "family": "qwen", + "xai/grok-3-mini": { + "id": "xai/grok-3-mini", + "name": "Grok 3 Mini", + "family": "grok", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-04-29", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.45, "output": 3.5 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 0.5, + "reasoning": 0.5, + "cache_read": 0.075 + } }, - "google-gemma-3-27b-it": { - "id": "google-gemma-3-27b-it", - "name": "Google Gemma 3 27B Instruct", - "family": "gemma", + "xai/grok-2-vision": { + "id": "xai/grok-2-vision", + "name": "Grok 2 Vision", + "family": "grok", "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, + "temperature": true, + "knowledge": "2024-08", + "release_date": "2024-08-20", + "last_updated": "2024-08-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8192, + "output": 4096 + }, + "cost": { + "input": 2, + "output": 10, + "cache_read": 2 + } + }, + "xai/grok-4": { + "id": "xai/grok-4", + "name": "Grok 4", + "family": "grok", + "attachment": false, + "reasoning": true, + "tool_call": true, "temperature": true, "knowledge": "2025-07", - "release_date": "2025-11-04", - "last_updated": "2026-03-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.12, "output": 0.2 }, - "limit": { "context": 198000, "output": 16384 } + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "reasoning": 15, + "cache_read": 0.75 + } }, - "qwen3-4b": { - "id": "qwen3-4b", - "name": "Venice Small", - "family": "qwen", + "xai/grok-code-fast-1": { + "id": "xai/grok-code-fast-1", + "name": "Grok Code Fast 1", + "family": "grok", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-04-29", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.05, "output": 0.15 }, - "limit": { "context": 32000, "output": 4096 } + "knowledge": "2023-10", + "release_date": "2025-08-28", + "last_updated": "2025-08-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 10000 + }, + "cost": { + "input": 0.2, + "output": 1.5, + "cache_read": 0.02 + } }, - "hermes-3-llama-3.1-405b": { - "id": "hermes-3-llama-3.1-405b", - "name": "Hermes 3 Llama 3.1 405b", - "family": "hermes", + "xai/grok-3-fast": { + "id": "xai/grok-3-fast", + "name": "Grok 3 Fast", + "family": "grok", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-09-25", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1.1, "output": 3 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 1.25 + } }, - "claude-sonnet-45": { - "id": "claude-sonnet-45", - "name": "Claude Sonnet 4.5", - "family": "claude-sonnet", - "attachment": true, + "xai/grok-3-mini-fast": { + "id": "xai/grok-3-mini-fast", + "name": "Grok 3 Mini Fast", + "family": "grok", + "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-01-15", - "last_updated": "2026-01-28", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3.75, "output": 18.75, "cache_read": 0.375, "cache_write": 4.69 }, - "limit": { "context": 198000, "output": 49500 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.6, + "output": 4, + "reasoning": 4, + "cache_read": 0.15 + } }, - "llama-3.3-70b": { - "id": "llama-3.3-70b", - "name": "Llama 3.3 70B", - "family": "llama", + "nvidia/nemotron-3-super-120b-a12b": { + "id": "nvidia/nemotron-3-super-120b-a12b", + "name": "NVIDIA Nemotron 3 Super 120B A12B", + "family": "nemotron", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2023-12", - "release_date": "2025-04-06", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.7, "output": 2.8 }, - "limit": { "context": 128000, "output": 4096 } + "release_date": "2026-03-18", + "last_updated": "2026-03-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 32000 + }, + "cost": { + "input": 0.15, + "output": 0.65 + } }, - "mistral-31-24b": { - "id": "mistral-31-24b", - "name": "Venice Medium", - "family": "mistral", + "nvidia/nemotron-3-nano-30b-a3b": { + "id": "nvidia/nemotron-3-nano-30b-a3b", + "name": "Nemotron 3 Nano 30B A3B", + "family": "nemotron", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2024-12", + "last_updated": "2024-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.06, + "output": 0.24 + } + }, + "nvidia/nemotron-nano-12b-v2-vl": { + "id": "nvidia/nemotron-nano-12b-v2-vl", + "name": "Nvidia Nemotron Nano 12B V2 VL", + "family": "nemotron", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2025-03-18", - "last_updated": "2026-03-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.5, "output": 2 }, - "limit": { "context": 128000, "output": 4096 } + "knowledge": "2024-10", + "release_date": "2024-12", + "last_updated": "2024-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.2, + "output": 0.6 + } }, - "zai-org-glm-5": { - "id": "zai-org-glm-5", - "name": "GLM 5", - "family": "glm", + "nvidia/nemotron-nano-9b-v2": { + "id": "nvidia/nemotron-nano-9b-v2", + "name": "Nvidia Nemotron Nano 9B V2", + "family": "nemotron", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1, "output": 3.2, "cache_read": 0.2 }, - "limit": { "context": 198000, "output": 32000 } + "knowledge": "2024-10", + "release_date": "2025-08-18", + "last_updated": "2025-08-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.04, + "output": 0.16 + } }, - "minimax-m25": { - "id": "minimax-m25", - "name": "MiniMax M2.5", - "family": "minimax", + "inception/mercury-edit-2": { + "id": "inception/mercury-edit-2", + "name": "Mercury Edit 2", "attachment": false, "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "tool_call": false, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-03-16", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.34, "output": 1.19, "cache_read": 0.04 }, - "limit": { "context": 198000, "output": 32768 } + "release_date": "2026-03-30", + "last_updated": "2026-03-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.25, + "output": 0.75, + "cache_read": 0.025 + } }, - "openai-gpt-54": { - "id": "openai-gpt-54", - "name": "GPT-5.4", - "family": "gpt", - "attachment": true, + "inception/mercury-2": { + "id": "inception/mercury-2", + "name": "Mercury 2", + "family": "mercury", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-03-05", - "last_updated": "2026-03-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-02-24", + "last_updated": "2026-03-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3.13, "output": 18.8, "cache_read": 0.313 }, - "limit": { "context": 1000000, "output": 131072 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 0.75, + "cache_read": 0.024999999999999998 + } }, - "nvidia-nemotron-3-nano-30b-a3b": { - "id": "nvidia-nemotron-3-nano-30b-a3b", - "name": "NVIDIA Nemotron 3 Nano 30B", - "family": "nemotron", + "inception/mercury-coder-small": { + "id": "inception/mercury-coder-small", + "name": "Mercury Coder Small Beta", + "family": "mercury", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-01-27", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.075, "output": 0.3 }, - "limit": { "context": 128000, "output": 16384 } + "release_date": "2025-02-26", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 32000, + "output": 16384 + }, + "cost": { + "input": 0.25, + "output": 1 + } }, - "deepseek-v3.2": { - "id": "deepseek-v3.2", - "name": "DeepSeek V3.2", - "family": "deepseek", - "attachment": false, + "openai/gpt-5.1-codex-max": { + "id": "openai/gpt-5.1-codex-max", + "name": "GPT 5.1 Codex Max", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-10", - "release_date": "2025-12-04", - "last_updated": "2026-03-24", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.33, "output": 0.48, "cache_read": 0.16 }, - "limit": { "context": 160000, "output": 32768 } + "knowledge": "2024-10", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.13 + } }, - "venice-uncensored-role-play": { - "id": "venice-uncensored-role-play", - "name": "Venice Role Play Uncensored", - "family": "venice", + "openai/gpt-5.2-chat": { + "id": "openai/gpt-5.2-chat", + "name": "GPT-5.2 Chat", + "family": "gpt", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-02-20", - "last_updated": "2026-03-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.5, "output": 2 }, - "limit": { "context": 128000, "output": 4096 } + "knowledge": "2024-10", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 111616, + "output": 16384 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.18 + } }, - "qwen3-vl-235b-a22b": { - "id": "qwen3-vl-235b-a22b", - "name": "Qwen3 VL 235B", - "family": "qwen", + "openai/gpt-4o-mini-search-preview": { + "id": "openai/gpt-4o-mini-search-preview", + "name": "GPT 4o Mini Search Preview", + "family": "gpt-mini", + "attachment": false, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "temperature": true, + "knowledge": "2023-09", + "release_date": "2025-01", + "last_updated": "2025-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 111616, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } + }, + "openai/codex-mini": { + "id": "openai/codex-mini", + "name": "Codex Mini", + "family": "gpt-codex-mini", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-01-16", - "last_updated": "2026-03-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.25, "output": 1.5 }, - "limit": { "context": 256000, "output": 16384 } + "knowledge": "2024-10", + "release_date": "2025-05-16", + "last_updated": "2025-05-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "input": 100000, + "output": 100000 + }, + "cost": { + "input": 1.5, + "output": 6, + "cache_read": 0.38 + } }, - "grok-41-fast": { - "id": "grok-41-fast", - "name": "Grok 4.1 Fast", - "family": "grok", + "openai/gpt-5-chat": { + "id": "openai/gpt-5-chat", + "name": "GPT-5 Chat", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-12-01", - "last_updated": "2026-03-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text", "image"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 0.625, "cache_read": 0.0625 }, - "limit": { "context": 1000000, "output": 30000 } + "limit": { + "context": 128000, + "input": 111616, + "output": 16384 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.13 + } }, - "openai-gpt-4o-mini-2024-07-18": { - "id": "openai-gpt-4o-mini-2024-07-18", - "name": "GPT-4o Mini", - "family": "gpt-mini", + "openai/gpt-5.3-chat": { + "id": "openai/gpt-5.3-chat", + "name": "GPT-5.3 Chat", + "family": "gpt", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-02-28", + "release_date": "2026-03-03", "last_updated": "2026-03-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1875, "output": 0.75, "cache_read": 0.09375 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "input": 111616, + "output": 16384 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "zai-org-glm-4.7-flash": { - "id": "zai-org-glm-4.7-flash", - "name": "GLM 4.7 Flash", - "family": "glm-flash", - "attachment": false, + "openai/gpt-5.2-pro": { + "id": "openai/gpt-5.2-pro", + "name": "GPT 5.2 ", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-01-29", - "last_updated": "2026-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.125, "output": 0.5 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2024-10", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 21, + "output": 168 + } }, - "mistral-small-3-2-24b-instruct": { - "id": "mistral-small-3-2-24b-instruct", - "name": "Mistral Small 3.2 24B Instruct", - "family": "mistral-small", + "openai/text-embedding-3-large": { + "id": "openai/text-embedding-3-large", + "name": "text-embedding-3-large", + "family": "text-embedding", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-01-15", - "last_updated": "2026-03-16", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.09375, "output": 0.25 }, - "limit": { "context": 256000, "output": 16384 } + "tool_call": false, + "temperature": false, + "release_date": "2024-01-25", + "last_updated": "2024-01-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8192, + "input": 6656, + "output": 1536 + }, + "cost": { + "input": 0.13, + "output": 0 + } }, - "openai-gpt-52-codex": { - "id": "openai-gpt-52-codex", - "name": "GPT-5.2 Codex", - "family": "gpt-codex", + "openai/gpt-5.5": { + "id": "openai/gpt-5.5", + "name": "GPT 5.5", + "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-08", - "release_date": "2025-01-15", - "last_updated": "2026-03-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.19, "output": 17.5, "cache_read": 0.219 }, - "limit": { "context": 256000, "output": 65536 } + "limit": { + "context": 1000000, + "input": 872000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5 + } }, - "minimax-m21": { - "id": "minimax-m21", - "name": "MiniMax M2.1", - "family": "minimax", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, - "temperature": true, - "release_date": "2025-12-01", - "last_updated": "2026-03-16", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.35, "output": 1.5, "cache_read": 0.04 }, - "limit": { "context": 198000, "output": 32768 } - } - } - }, - "submodel": { - "id": "submodel", - "env": ["SUBMODEL_INSTAGEN_ACCESS_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://llm.submodel.ai/v1", - "name": "submodel", - "doc": "https://submodel.gitbook.io", - "models": { - "openai/gpt-oss-120b": { - "id": "openai/gpt-oss-120b", - "name": "GPT OSS 120B", - "family": "gpt-oss", - "attachment": false, + "openai/gpt-5.3-codex": { + "id": "openai/gpt-5.3-codex", + "name": "GPT 5.3 Codex", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-08-23", - "last_updated": "2025-08-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.5 }, - "limit": { "context": 131072, "output": 32768 } + "release_date": "2026-02-24", + "last_updated": "2026-02-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "zai-org/GLM-4.5-Air": { - "id": "zai-org/GLM-4.5-Air", - "name": "GLM 4.5 Air", - "family": "glm-air", + "openai/text-embedding-ada-002": { + "id": "openai/text-embedding-ada-002", + "name": "text-embedding-ada-002", + "family": "text-embedding", "attachment": false, "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2022-12-15", + "last_updated": "2022-12-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8192, + "input": 6656, + "output": 1536 + }, + "cost": { + "input": 0.1, + "output": 0 + } + }, + "openai/gpt-5.2": { + "id": "openai/gpt-5.2", + "name": "GPT-5.2", + "family": "gpt", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.5 }, - "limit": { "context": 131072, "output": 131072 } + "knowledge": "2024-10", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.18 + } }, - "zai-org/GLM-4.5-FP8": { - "id": "zai-org/GLM-4.5-FP8", - "name": "GLM 4.5 FP8", - "family": "glm", - "attachment": false, + "openai/o3-pro": { + "id": "openai/o3-pro", + "name": "o3 Pro", + "family": "o-pro", + "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 131072, "output": 131072 } + "temperature": false, + "knowledge": "2024-10", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "input": 100000, + "output": 100000 + }, + "cost": { + "input": 20, + "output": 80 + } }, - "deepseek-ai/DeepSeek-V3.1": { - "id": "deepseek-ai/DeepSeek-V3.1", - "name": "DeepSeek V3.1", - "family": "deepseek", - "attachment": false, + "openai/gpt-5.4-mini": { + "id": "openai/gpt-5.4-mini", + "name": "GPT 5.4 Mini", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-08-23", - "last_updated": "2025-08-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 75000, "output": 163840 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.75, + "output": 4.5, + "cache_read": 0.075 + } }, - "deepseek-ai/DeepSeek-V3-0324": { - "id": "deepseek-ai/DeepSeek-V3-0324", - "name": "DeepSeek V3 0324", - "family": "deepseek", - "attachment": false, - "reasoning": false, + "openai/gpt-5.4-nano": { + "id": "openai/gpt-5.4-nano", + "name": "GPT 5.4 Nano", + "family": "gpt", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-08-23", - "last_updated": "2025-08-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 75000, "output": 163840 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.19999999999999998, + "output": 1.25, + "cache_read": 0.02 + } }, - "deepseek-ai/DeepSeek-R1-0528": { - "id": "deepseek-ai/DeepSeek-R1-0528", - "name": "DeepSeek R1 0528", - "family": "deepseek-thinking", - "attachment": false, + "openai/gpt-5.2-codex": { + "id": "openai/gpt-5.2-codex", + "name": "GPT-5.2-Codex", + "family": "gpt-codex", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-08-23", - "last_updated": "2025-08-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-12", + "last_updated": "2025-12", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 2.15 }, - "limit": { "context": 75000, "output": 163840 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "Qwen/Qwen3-235B-A22B-Thinking-2507": { - "id": "Qwen/Qwen3-235B-A22B-Thinking-2507", - "name": "Qwen3 235B A22B Thinking 2507", - "family": "qwen", - "attachment": false, + "openai/gpt-5.1-codex-mini": { + "id": "openai/gpt-5.1-codex-mini", + "name": "GPT-5.1 Codex mini", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-08-23", - "last_updated": "2025-08-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.6 }, - "limit": { "context": 262144, "output": 131072 } + "knowledge": "2024-10", + "release_date": "2025-05-16", + "last_updated": "2025-05-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.03 + } }, - "Qwen/Qwen3-235B-A22B-Instruct-2507": { - "id": "Qwen/Qwen3-235B-A22B-Instruct-2507", - "name": "Qwen3 235B A22B Instruct 2507", - "family": "qwen", - "attachment": false, - "reasoning": false, + "openai/gpt-5.1-thinking": { + "id": "openai/gpt-5.1-thinking", + "name": "GPT 5.1 Thinking", + "family": "gpt", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2025-08-23", - "last_updated": "2025-08-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.3 }, - "limit": { "context": 262144, "output": 131072 } + "temperature": false, + "knowledge": "2024-10", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text", "image"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.13 + } }, - "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8": { - "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8", - "name": "Qwen3 Coder 480B A35B Instruct", - "family": "qwen", - "attachment": false, - "reasoning": false, + "openai/gpt-5.4-pro": { + "id": "openai/gpt-5.4-pro", + "name": "GPT 5.4 Pro", + "family": "gpt", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-08-23", - "last_updated": "2025-08-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-05", + "last_updated": "2026-03-06", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 262144, "output": 262144 } - } - } - }, - "vultr": { - "id": "vultr", - "env": ["VULTR_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.vultrinference.com/v1", - "name": "Vultr", - "doc": "https://api.vultrinference.com/", - "models": { - "Kimi-K2.5": { - "id": "Kimi-K2.5", - "name": "Kimi K2 Instruct", - "family": "kimi", + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 30, + "output": 180 + } + }, + "openai/gpt-3.5-turbo": { + "id": "openai/gpt-3.5-turbo", + "name": "GPT-3.5 Turbo", + "family": "gpt", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.55, "output": 2.75 }, - "limit": { "context": 261000, "output": 32768 } + "knowledge": "2021-09", + "release_date": "2023-03-01", + "last_updated": "2023-03-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 16385, + "input": 12289, + "output": 4096 + }, + "cost": { + "input": 0.5, + "output": 1.5 + } }, - "MiniMax-M2.5": { - "id": "MiniMax-M2.5", - "name": "MiniMax M2.5", - "family": "minimax", - "attachment": false, - "reasoning": false, + "openai/o3-deep-research": { + "id": "openai/o3-deep-research", + "name": "o3-deep-research", + "family": "o", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": true, + "temperature": false, "knowledge": "2024-10", - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 196000, "output": 4096 } + "release_date": "2024-06-26", + "last_updated": "2024-06-26", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "input": 100000, + "output": 100000 + }, + "cost": { + "input": 10, + "output": 40, + "cache_read": 2.5 + } }, - "GLM-5-FP8": { - "id": "GLM-5-FP8", - "name": "GLM 5 FP8", - "family": "glm", + "openai/text-embedding-3-small": { + "id": "openai/text-embedding-3-small", + "name": "text-embedding-3-small", + "family": "text-embedding", "attachment": false, "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2024-01-25", + "last_updated": "2024-01-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8192, + "input": 6656, + "output": 1536 + }, + "cost": { + "input": 0.02, + "output": 0 + } + }, + "openai/gpt-5.4": { + "id": "openai/gpt-5.4", + "name": "GPT 5.4", + "family": "gpt", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.85, "output": 3.1 }, - "limit": { "context": 202000, "output": 131072 } + "release_date": "2026-03-05", + "last_updated": "2026-03-06", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 2.5, + "output": 15, + "cache_read": 0.25 + } }, - "DeepSeek-V3.2": { - "id": "DeepSeek-V3.2", - "name": "DeepSeek V3.2", - "family": "deepseek", + "openai/gpt-oss-20b": { + "id": "openai/gpt-oss-20b", + "name": "GPT OSS 20B", + "family": "gpt-oss", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, "knowledge": "2024-10", - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.55, "output": 1.65 }, - "limit": { "context": 163000, "output": 4096 } - } - } - }, - "github-models": { - "id": "github-models", - "env": ["GITHUB_TOKEN"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://models.github.ai/inference", - "name": "GitHub Models", - "doc": "https://docs.github.com/en/github-models", - "models": { - "mistral-ai/codestral-2501": { - "id": "mistral-ai/codestral-2501", - "name": "Codestral 25.01", - "family": "codestral", - "attachment": false, + "limit": { + "context": 131072, + "input": 98304, + "output": 32768 + }, + "cost": { + "input": 0.07, + "output": 0.3 + } + }, + "openai/gpt-5-pro": { + "id": "openai/gpt-5-pro", + "name": "GPT-5 pro", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-03", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text", "image"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 32000, "output": 8192 } + "limit": { + "context": 400000, + "input": 128000, + "output": 272000 + }, + "cost": { + "input": 15, + "output": 120 + } }, - "mistral-ai/mistral-large-2411": { - "id": "mistral-ai/mistral-large-2411", - "name": "Mistral Large 24.11", - "family": "mistral-large", + "openai/gpt-oss-safeguard-20b": { + "id": "openai/gpt-oss-safeguard-20b", + "name": "gpt-oss-safeguard-20b", + "family": "gpt-oss", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-09", - "release_date": "2024-11-01", - "last_updated": "2024-11-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 131072, + "input": 65536, + "output": 65536 + }, + "cost": { + "input": 0.08, + "output": 0.3, + "cache_read": 0.04 + } }, - "mistral-ai/mistral-small-2503": { - "id": "mistral-ai/mistral-small-2503", - "name": "Mistral Small 3.1", - "family": "mistral-small", + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "name": "GPT OSS 120B", + "family": "gpt-oss", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-09", - "release_date": "2025-03-01", - "last_updated": "2025-03-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 32768 } + "knowledge": "2024-10", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.1, + "output": 0.5 + } }, - "mistral-ai/mistral-medium-2505": { - "id": "mistral-ai/mistral-medium-2505", - "name": "Mistral Medium 3 (25.05)", - "family": "mistral-medium", - "attachment": false, + "openai/gpt-5.5-pro": { + "id": "openai/gpt-5.5-pro", + "name": "GPT 5.5 Pro", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-09", - "release_date": "2025-05-01", - "last_updated": "2025-05-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 1000000, + "input": 872000, + "output": 128000 + }, + "cost": { + "input": 30, + "output": 180 + } }, - "mistral-ai/ministral-3b": { - "id": "mistral-ai/ministral-3b", - "name": "Ministral 3B", - "family": "ministral", + "openai/gpt-3.5-turbo-instruct": { + "id": "openai/gpt-3.5-turbo-instruct", + "name": "GPT-3.5 Turbo Instruct", + "family": "gpt", "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "knowledge": "2021-09", + "release_date": "2023-03-01", + "last_updated": "2023-03-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8192, + "input": 4096, + "output": 4096 + }, + "cost": { + "input": 1.5, + "output": 2 + } + }, + "openai/gpt-5.1-instant": { + "id": "openai/gpt-5.1-instant", + "name": "GPT-5.1 Instant", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-03", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 8192 } + "knowledge": "2024-10", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text", "image"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "input": 111616, + "output": 16384 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.13 + } }, - "mistral-ai/mistral-nemo": { - "id": "mistral-ai/mistral-nemo", - "name": "Mistral Nemo", - "family": "mistral-nemo", - "attachment": false, + "openai/gpt-5.1-codex": { + "id": "openai/gpt-5.1-codex", + "name": "GPT-5.1-Codex", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-03", - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 8192 } + "knowledge": "2024-10", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.13 + } }, - "ai21-labs/ai21-jamba-1.5-mini": { - "id": "ai21-labs/ai21-jamba-1.5-mini", - "name": "AI21 Jamba 1.5 Mini", - "family": "jamba", - "attachment": false, - "reasoning": true, + "openai/gpt-4.1-mini": { + "id": "openai/gpt-4.1-mini", + "name": "GPT-4.1 mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-03", - "release_date": "2024-08-29", - "last_updated": "2024-08-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 4096 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 1.6, + "cache_read": 0.1 + } }, - "ai21-labs/ai21-jamba-1.5-large": { - "id": "ai21-labs/ai21-jamba-1.5-large", - "name": "AI21 Jamba 1.5 Large", - "family": "jamba", - "attachment": false, - "reasoning": true, + "openai/gpt-4.1": { + "id": "openai/gpt-4.1", + "name": "GPT-4.1", + "family": "gpt", + "attachment": true, + "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-03", - "release_date": "2024-08-29", - "last_updated": "2024-08-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 256000, "output": 4096 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } }, - "openai/o3-mini": { - "id": "openai/o3-mini", - "name": "OpenAI o3-mini", - "family": "o-mini", - "attachment": false, + "openai/gpt-5": { + "id": "openai/gpt-5", + "name": "GPT-5", + "family": "gpt", + "attachment": true, "reasoning": true, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": false, - "knowledge": "2024-04", - "release_date": "2025-01-31", - "last_updated": "2025-01-31", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-09-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, "openai/gpt-4o": { "id": "openai/gpt-4o", @@ -52916,12348 +95752,21632 @@ "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2023-10", + "knowledge": "2023-09", "release_date": "2024-05-13", - "last_updated": "2024-05-13", - "modalities": { "input": ["text", "image", "audio"], "output": ["text"] }, + "last_updated": "2024-08-06", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10, + "cache_read": 1.25 + } }, - "openai/gpt-4o-mini": { - "id": "openai/gpt-4o-mini", - "name": "GPT-4o mini", - "family": "gpt-mini", + "openai/o3": { + "id": "openai/o3", + "name": "o3", + "family": "o", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text", "image", "audio"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } }, - "openai/gpt-4.1": { - "id": "openai/gpt-4.1", - "name": "GPT-4.1", - "family": "gpt", + "openai/gpt-4.1-nano": { + "id": "openai/gpt-4.1-nano", + "name": "GPT-4.1 nano", + "family": "gpt-nano", "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, "knowledge": "2024-04", "release_date": "2025-04-14", "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.03 + } + }, + "openai/gpt-5-codex": { + "id": "openai/gpt-5-codex", + "name": "GPT-5-Codex", + "family": "gpt-codex", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-09-15", + "last_updated": "2025-09-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } + }, + "openai/o3-mini": { + "id": "openai/o3-mini", + "name": "o3-mini", + "family": "o-mini", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2024-12-20", + "last_updated": "2025-01-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.55 + } }, "openai/o1": { "id": "openai/o1", - "name": "OpenAI o1", + "name": "o1", "family": "o", - "attachment": false, + "attachment": true, "reasoning": true, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": false, - "knowledge": "2023-10", - "release_date": "2024-09-12", - "last_updated": "2024-12-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2023-09", + "release_date": "2024-12-05", + "last_updated": "2024-12-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 15, + "output": 60, + "cache_read": 7.5 + } }, - "openai/o3": { - "id": "openai/o3", - "name": "OpenAI o3", - "family": "o", - "attachment": false, + "openai/o4-mini": { + "id": "openai/o4-mini", + "name": "o4-mini", + "family": "o-mini", + "attachment": true, "reasoning": true, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": false, - "knowledge": "2024-04", - "release_date": "2025-01-31", - "last_updated": "2025-01-31", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-05", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.28 + } }, - "openai/gpt-4.1-mini": { - "id": "openai/gpt-4.1-mini", - "name": "GPT-4.1-mini", + "openai/gpt-4o-mini": { + "id": "openai/gpt-4o-mini", + "name": "GPT-4o mini", "family": "gpt-mini", "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2023-09", + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.08 + } }, - "openai/gpt-4.1-nano": { - "id": "openai/gpt-4.1-nano", - "name": "GPT-4.1-nano", - "family": "gpt-nano", + "openai/gpt-4-turbo": { + "id": "openai/gpt-4-turbo", + "name": "GPT-4 Turbo", + "family": "gpt", "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": false, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2023-11-06", + "last_updated": "2024-04-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 10, + "output": 30 + } }, - "openai/o1-preview": { - "id": "openai/o1-preview", - "name": "OpenAI o1-preview", - "family": "o", - "attachment": false, + "openai/gpt-5-nano": { + "id": "openai/gpt-5-nano", + "name": "GPT-5 Nano", + "family": "gpt-nano", + "attachment": true, "reasoning": true, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": false, - "knowledge": "2023-10", - "release_date": "2024-09-12", - "last_updated": "2024-09-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.05, + "output": 0.4, + "cache_read": 0.005 + } }, - "openai/o4-mini": { - "id": "openai/o4-mini", - "name": "OpenAI o4-mini", - "family": "o-mini", - "attachment": false, + "openai/gpt-5-mini": { + "id": "openai/gpt-5-mini", + "name": "GPT-5 Mini", + "family": "gpt-mini", + "attachment": true, "reasoning": true, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": false, - "knowledge": "2024-04", - "release_date": "2025-01-31", - "last_updated": "2025-01-31", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.025 + } }, - "openai/o1-mini": { - "id": "openai/o1-mini", - "name": "OpenAI o1-mini", - "family": "o-mini", + "amazon/titan-embed-text-v2": { + "id": "amazon/titan-embed-text-v2", + "name": "Titan Text Embeddings V2", + "family": "titan-embed", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": false, "temperature": false, - "knowledge": "2023-10", - "release_date": "2024-09-12", - "last_updated": "2024-12-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-04", + "last_updated": "2024-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 65536 } + "limit": { + "context": 8192, + "output": 1536 + }, + "cost": { + "input": 0.02, + "output": 0 + } }, - "microsoft/phi-3-mini-128k-instruct": { - "id": "microsoft/phi-3-mini-128k-instruct", - "name": "Phi-3-mini instruct (128k)", - "family": "phi", - "attachment": false, + "amazon/nova-2-lite": { + "id": "amazon/nova-2-lite", + "name": "Nova 2 Lite", + "family": "nova", + "attachment": true, "reasoning": true, + "tool_call": false, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2024-12-01", + "last_updated": "2024-12-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 1000000 + }, + "cost": { + "input": 0.3, + "output": 2.5 + } + }, + "amazon/nova-pro": { + "id": "amazon/nova-pro", + "name": "Nova Pro", + "family": "nova-pro", + "attachment": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-04-23", - "last_updated": "2024-04-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "knowledge": "2024-10", + "release_date": "2024-12-03", + "last_updated": "2024-12-03", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 300000, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 3.2, + "cache_read": 0.2 + } }, - "microsoft/phi-3-small-8k-instruct": { - "id": "microsoft/phi-3-small-8k-instruct", - "name": "Phi-3-small instruct (8k)", - "family": "phi", - "attachment": false, - "reasoning": true, + "amazon/nova-lite": { + "id": "amazon/nova-lite", + "name": "Nova Lite", + "family": "nova-lite", + "attachment": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-04-23", - "last_updated": "2024-04-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 8192, "output": 2048 } + "knowledge": "2024-10", + "release_date": "2024-12-03", + "last_updated": "2024-12-03", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 300000, + "output": 8192 + }, + "cost": { + "input": 0.06, + "output": 0.24, + "cache_read": 0.015 + } }, - "microsoft/phi-4-reasoning": { - "id": "microsoft/phi-4-reasoning", - "name": "Phi-4-Reasoning", - "family": "phi", + "amazon/nova-micro": { + "id": "amazon/nova-micro", + "name": "Nova Micro", + "family": "nova-micro", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "knowledge": "2024-10", + "release_date": "2024-12-03", + "last_updated": "2024-12-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.035, + "output": 0.14, + "cache_read": 0.00875 + } }, - "microsoft/phi-4-mini-reasoning": { - "id": "microsoft/phi-4-mini-reasoning", - "name": "Phi-4-mini-reasoning", - "family": "phi", + "mistral/mistral-nemo": { + "id": "mistral/mistral-nemo", + "name": "Mistral Nemo", + "family": "mistral-nemo", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "knowledge": "2024-04", + "release_date": "2024-07-01", + "last_updated": "2024-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 60288, + "output": 16000 + }, + "cost": { + "input": 0.04, + "output": 0.17 + } }, - "microsoft/phi-3-mini-4k-instruct": { - "id": "microsoft/phi-3-mini-4k-instruct", - "name": "Phi-3-mini instruct (4k)", - "family": "phi", - "attachment": false, - "reasoning": true, - "tool_call": true, + "mistral/ministral-14b": { + "id": "mistral/ministral-14b", + "name": "Ministral 14B", + "family": "ministral", + "attachment": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-04-23", - "last_updated": "2024-04-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 4096, "output": 1024 } + "knowledge": "2024-10", + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.2, + "output": 0.2 + } }, - "microsoft/phi-3-medium-4k-instruct": { - "id": "microsoft/phi-3-medium-4k-instruct", - "name": "Phi-3-medium instruct (4k)", - "family": "phi", + "mistral/codestral-embed": { + "id": "mistral/codestral-embed", + "name": "Codestral Embed", + "family": "codestral-embed", "attachment": false, - "reasoning": true, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2025-05-28", + "last_updated": "2025-05-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8192, + "output": 1536 + }, + "cost": { + "input": 0.15, + "output": 0 + } + }, + "mistral/mistral-medium": { + "id": "mistral/mistral-medium", + "name": "Mistral Medium 3.1", + "family": "mistral-medium", + "attachment": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-04-23", - "last_updated": "2024-04-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 4096, "output": 1024 } + "knowledge": "2024-10", + "release_date": "2025-05-07", + "last_updated": "2025-05-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 64000 + }, + "cost": { + "input": 0.4, + "output": 2 + } }, - "microsoft/phi-3.5-vision-instruct": { - "id": "microsoft/phi-3.5-vision-instruct", - "name": "Phi-3.5-vision instruct (128k)", - "family": "phi", + "mistral/mistral-embed": { + "id": "mistral/mistral-embed", + "name": "Mistral Embed", + "family": "mistral-embed", "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-08-20", - "last_updated": "2024-08-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2023-12-11", + "last_updated": "2023-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 8192, + "output": 1536 + }, + "cost": { + "input": 0.1, + "output": 0 + } }, - "microsoft/mai-ds-r1": { - "id": "microsoft/mai-ds-r1", - "name": "MAI-DS-R1", - "family": "mai", + "mistral/devstral-2": { + "id": "mistral/devstral-2", + "name": "Devstral 2", + "family": "devstral", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-06", - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-12-09", + "last_updated": "2025-12-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 65536, "output": 8192 } + "limit": { + "context": 256000, + "output": 256000 + } }, - "microsoft/phi-3.5-mini-instruct": { - "id": "microsoft/phi-3.5-mini-instruct", - "name": "Phi-3.5-mini instruct (128k)", - "family": "phi", - "attachment": false, - "reasoning": true, - "tool_call": true, + "mistral/mistral-large-3": { + "id": "mistral/mistral-large-3", + "name": "Mistral Large 3", + "family": "mistral-large", + "attachment": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-08-20", - "last_updated": "2024-08-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "knowledge": "2024-10", + "release_date": "2025-12-02", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.5, + "output": 1.5 + } }, - "microsoft/phi-4": { - "id": "microsoft/phi-4", - "name": "Phi-4", - "family": "phi", + "mistral/devstral-small-2": { + "id": "mistral/devstral-small-2", + "name": "Devstral Small 2", + "family": "devstral", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 16000, "output": 4096 } + "knowledge": "2024-10", + "release_date": "2025-05-07", + "last_updated": "2025-05-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 256000, + "output": 256000 + } }, - "microsoft/phi-3-medium-128k-instruct": { - "id": "microsoft/phi-3-medium-128k-instruct", - "name": "Phi-3-medium instruct (128k)", - "family": "phi", + "mistral/devstral-small": { + "id": "mistral/devstral-small", + "name": "Devstral Small 1.1", + "family": "devstral", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-04-23", - "last_updated": "2024-04-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "knowledge": "2024-10", + "release_date": "2025-05-07", + "last_updated": "2025-05-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 64000 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } }, - "microsoft/phi-3.5-moe-instruct": { - "id": "microsoft/phi-3.5-moe-instruct", - "name": "Phi-3.5-MoE instruct (128k)", - "family": "phi", + "mistral/ministral-8b": { + "id": "mistral/ministral-8b", + "name": "Ministral 8B (latest)", + "family": "ministral", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-08-20", - "last_updated": "2024-08-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2024-10-01", + "last_updated": "2024-10-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "microsoft/phi-4-multimodal-instruct": { - "id": "microsoft/phi-4-multimodal-instruct", - "name": "Phi-4-multimodal-instruct", - "family": "phi", + "mistral/magistral-medium": { + "id": "mistral/magistral-medium", + "name": "Magistral Medium (latest)", + "family": "magistral-medium", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text", "image", "audio"], "output": ["text"] }, + "knowledge": "2025-06", + "release_date": "2025-03-17", + "last_updated": "2025-03-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2, + "output": 5 + } }, - "microsoft/phi-3-small-128k-instruct": { - "id": "microsoft/phi-3-small-128k-instruct", - "name": "Phi-3-small instruct (128k)", - "family": "phi", - "attachment": false, + "mistral/mistral-small": { + "id": "mistral/mistral-small", + "name": "Mistral Small (latest)", + "family": "mistral-small", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-04-23", - "last_updated": "2024-04-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-06", + "release_date": "2026-03-16", + "last_updated": "2026-03-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "microsoft/phi-4-mini-instruct": { - "id": "microsoft/phi-4-mini-instruct", - "name": "Phi-4-mini-instruct", - "family": "phi", + "mistral/magistral-small": { + "id": "mistral/magistral-small", + "name": "Magistral Small", + "family": "magistral-small", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-06", + "release_date": "2025-03-17", + "last_updated": "2025-03-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.5, + "output": 1.5 + } }, - "cohere/cohere-command-r-plus-08-2024": { - "id": "cohere/cohere-command-r-plus-08-2024", - "name": "Cohere Command R+ 08-2024", - "family": "command-r", - "attachment": false, + "mistral/pixtral-12b": { + "id": "mistral/pixtral-12b", + "name": "Pixtral 12B", + "family": "pixtral", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-03", - "release_date": "2024-08-01", - "last_updated": "2024-08-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } - }, - "cohere/cohere-command-r": { - "id": "cohere/cohere-command-r", - "name": "Cohere Command R", - "family": "command-r", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-03", - "release_date": "2024-03-11", - "last_updated": "2024-08-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "knowledge": "2024-09", + "release_date": "2024-09-01", + "last_updated": "2024-09-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.15, + "output": 0.15 + } }, - "cohere/cohere-command-r-08-2024": { - "id": "cohere/cohere-command-r-08-2024", - "name": "Cohere Command R 08-2024", - "family": "command-r", + "mistral/mixtral-8x22b-instruct": { + "id": "mistral/mixtral-8x22b-instruct", + "name": "Mixtral 8x22B", + "family": "mixtral", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-03", - "release_date": "2024-08-01", - "last_updated": "2024-08-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "knowledge": "2024-04", + "release_date": "2024-04-17", + "last_updated": "2024-04-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 64000, + "output": 64000 + }, + "cost": { + "input": 2, + "output": 6 + } }, - "cohere/cohere-command-r-plus": { - "id": "cohere/cohere-command-r-plus", - "name": "Cohere Command R+", - "family": "command-r", - "attachment": false, + "mistral/pixtral-large": { + "id": "mistral/pixtral-large", + "name": "Pixtral Large (latest)", + "family": "pixtral", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-03", - "release_date": "2024-04-04", - "last_updated": "2024-08-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "knowledge": "2024-11", + "release_date": "2024-11-01", + "last_updated": "2024-11-04", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 2, + "output": 6 + } }, - "cohere/cohere-command-a": { - "id": "cohere/cohere-command-a", - "name": "Cohere Command A", - "family": "command-a", + "mistral/ministral-3b": { + "id": "mistral/ministral-3b", + "name": "Ministral 3B (latest)", + "family": "ministral", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-03", - "release_date": "2024-11-01", - "last_updated": "2024-11-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 4096 } + "knowledge": "2024-10", + "release_date": "2024-10-01", + "last_updated": "2024-10-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.04, + "output": 0.04 + } }, - "deepseek/deepseek-v3-0324": { - "id": "deepseek/deepseek-v3-0324", - "name": "DeepSeek-V3-0324", - "family": "deepseek", + "mistral/codestral": { + "id": "mistral/codestral", + "name": "Codestral (latest)", + "family": "codestral", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-06", - "release_date": "2025-03-24", - "last_updated": "2025-03-24", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2024-05-29", + "last_updated": "2025-01-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 256000, + "output": 4096 + }, + "cost": { + "input": 0.3, + "output": 0.9 + } }, - "deepseek/deepseek-r1": { - "id": "deepseek/deepseek-r1", - "name": "DeepSeek-R1", - "family": "deepseek-thinking", + "meta/llama-3.2-1b": { + "id": "meta/llama-3.2-1b", + "name": "Llama 3.2 1B Instruct", + "family": "llama", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2024-06", - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 65536, "output": 8192 } + "knowledge": "2023-12", + "release_date": "2024-09-18", + "last_updated": "2024-09-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "deepseek/deepseek-r1-0528": { - "id": "deepseek/deepseek-r1-0528", - "name": "DeepSeek-R1-0528", - "family": "deepseek-thinking", + "meta/llama-3.1-8b": { + "id": "meta/llama-3.1-8b", + "name": "Llama 3.1 8B Instruct", + "family": "llama", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-06", - "release_date": "2025-05-28", - "last_updated": "2025-05-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 65536, "output": 8192 } + "knowledge": "2023-12", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.03, + "output": 0.05 + } }, - "xai/grok-3-mini": { - "id": "xai/grok-3-mini", - "name": "Grok 3 Mini", - "family": "grok", - "attachment": false, - "reasoning": true, + "meta/llama-3.2-90b": { + "id": "meta/llama-3.2-90b", + "name": "Llama 3.2 90B Vision Instruct", + "family": "llama", + "attachment": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-12-09", - "last_updated": "2024-12-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2024-09-25", + "last_updated": "2024-09-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.72, + "output": 0.72 + } }, - "xai/grok-3": { - "id": "xai/grok-3", - "name": "Grok 3", - "family": "grok", + "meta/llama-3.2-3b": { + "id": "meta/llama-3.2-3b", + "name": "Llama 3.2 3B Instruct", + "family": "llama", "attachment": false, - "reasoning": true, - "tool_call": true, + "reasoning": false, + "tool_call": false, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-12-09", - "last_updated": "2024-12-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-12", + "release_date": "2024-09-18", + "last_updated": "2024-09-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.15, + "output": 0.15 + } }, - "core42/jais-30b-chat": { - "id": "core42/jais-30b-chat", - "name": "JAIS 30b Chat", - "family": "jais", - "attachment": false, - "reasoning": true, + "meta/llama-3.2-11b": { + "id": "meta/llama-3.2-11b", + "name": "Llama 3.2 11B Vision Instruct", + "family": "llama", + "attachment": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-03", - "release_date": "2023-08-30", - "last_updated": "2023-08-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 8192, "output": 2048 } + "knowledge": "2023-12", + "release_date": "2024-09-25", + "last_updated": "2024-09-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.16, + "output": 0.16 + } }, - "meta/meta-llama-3.1-8b-instruct": { - "id": "meta/meta-llama-3.1-8b-instruct", - "name": "Meta-Llama-3.1-8B-Instruct", + "meta/llama-3.1-70b": { + "id": "meta/llama-3.1-70b", + "name": "Llama 3.1 70B Instruct", "family": "llama", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, "knowledge": "2023-12", "release_date": "2024-07-23", "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 32768 } + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.4, + "output": 0.4 + } }, - "meta/llama-3.3-70b-instruct": { - "id": "meta/llama-3.3-70b-instruct", + "meta/llama-3.3-70b": { + "id": "meta/llama-3.3-70b", "name": "Llama-3.3-70B-Instruct", "family": "llama", - "attachment": false, - "reasoning": true, + "attachment": true, + "reasoning": false, "tool_call": true, "temperature": true, "knowledge": "2023-12", "release_date": "2024-12-06", "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "meta/llama-4-scout-17b-16e-instruct": { - "id": "meta/llama-4-scout-17b-16e-instruct", - "name": "Llama 4 Scout 17B 16E Instruct", + "meta/llama-4-maverick": { + "id": "meta/llama-4-maverick", + "name": "Llama-4-Maverick-17B-128E-Instruct-FP8", "family": "llama", - "attachment": false, - "reasoning": true, + "attachment": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-01-31", - "last_updated": "2025-01-31", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-08", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "meta/llama-3.2-11b-vision-instruct": { - "id": "meta/llama-3.2-11b-vision-instruct", - "name": "Llama-3.2-11B-Vision-Instruct", + "meta/llama-4-scout": { + "id": "meta/llama-4-scout", + "name": "Llama-4-Scout-17B-16E-Instruct-FP8", "family": "llama", - "attachment": false, - "reasoning": true, + "attachment": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "modalities": { "input": ["text", "image", "audio"], "output": ["text"] }, + "knowledge": "2024-08", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "meta/meta-llama-3-70b-instruct": { - "id": "meta/meta-llama-3-70b-instruct", - "name": "Meta-Llama-3-70B-Instruct", - "family": "llama", - "attachment": false, + "vercel/v0-1.5-md": { + "id": "vercel/v0-1.5-md", + "name": "v0-1.5-md", + "family": "v0", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-04-18", - "last_updated": "2024-04-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 8192, "output": 2048 } + "release_date": "2025-06-09", + "last_updated": "2025-06-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 32000 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "meta/meta-llama-3-8b-instruct": { - "id": "meta/meta-llama-3-8b-instruct", - "name": "Meta-Llama-3-8B-Instruct", - "family": "llama", - "attachment": false, + "vercel/v0-1.0-md": { + "id": "vercel/v0-1.0-md", + "name": "v0-1.0-md", + "family": "v0", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-04-18", - "last_updated": "2024-04-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 8192, "output": 2048 } + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 32000 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "meta/llama-3.2-90b-vision-instruct": { - "id": "meta/llama-3.2-90b-vision-instruct", - "name": "Llama-3.2-90B-Vision-Instruct", - "family": "llama", - "attachment": false, + "minimax/minimax-m2.7": { + "id": "minimax/minimax-m2.7", + "name": "Minimax M2.7", + "family": "minimax", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "modalities": { "input": ["text", "image", "audio"], "output": ["text"] }, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 204800, + "output": 131000 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06, + "cache_write": 0.375 + } }, - "meta/meta-llama-3.1-405b-instruct": { - "id": "meta/meta-llama-3.1-405b-instruct", - "name": "Meta-Llama-3.1-405B-Instruct", - "family": "llama", - "attachment": false, + "minimax/minimax-m2.7-highspeed": { + "id": "minimax/minimax-m2.7-highspeed", + "name": "MiniMax M2.7 High Speed", + "family": "minimax", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 204800, + "output": 131100 + }, + "cost": { + "input": 0.6, + "output": 2.4, + "cache_read": 0.06, + "cache_write": 0.375 + } }, - "meta/llama-4-maverick-17b-128e-instruct-fp8": { - "id": "meta/llama-4-maverick-17b-128e-instruct-fp8", - "name": "Llama 4 Maverick 17B 128E Instruct FP8", - "family": "llama", + "minimax/minimax-m2": { + "id": "minimax/minimax-m2", + "name": "MiniMax M2", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": true, "temperature": true, - "knowledge": "2024-12", - "release_date": "2025-01-31", - "last_updated": "2025-01-31", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-10-27", + "last_updated": "2025-10-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 262114, + "output": 262114 + }, + "cost": { + "input": 0.27, + "output": 1.15, + "cache_read": 0.03, + "cache_write": 0.38 + } }, - "meta/meta-llama-3.1-70b-instruct": { - "id": "meta/meta-llama-3.1-70b-instruct", - "name": "Meta-Llama-3.1-70B-Instruct", - "family": "llama", + "minimax/minimax-m2.1": { + "id": "minimax/minimax-m2.1", + "name": "MiniMax M2.1", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 32768 } - } - } - }, - "nano-gpt": { - "id": "nano-gpt", - "env": ["NANO_GPT_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://nano-gpt.com/api/v1", - "name": "NanoGPT", - "doc": "https://docs.nano-gpt.com", - "models": { - "claude-opus-4-thinking": { - "id": "claude-opus-4-thinking", - "name": "Claude 4 Opus Thinking", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "release_date": "2025-07-15", - "last_updated": "2025-07-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-10-27", + "last_updated": "2025-10-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 14.994, "output": 75.004 }, - "limit": { "context": 200000, "input": 200000, "output": 32000 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03, + "cache_write": 0.38 + } }, - "qwen3-coder-30b-a3b-instruct": { - "id": "qwen3-coder-30b-a3b-instruct", - "name": "Qwen3 Coder 30B A3B Instruct", + "minimax/minimax-m2.1-lightning": { + "id": "minimax/minimax-m2.1-lightning", + "name": "MiniMax M2.1 Lightning", + "family": "minimax", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 128000, "input": 128000, "output": 65536 } - }, - "brave-research": { - "id": "brave-research", - "name": "Brave (Research)", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2023-03-02", - "last_updated": "2024-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-10-27", + "last_updated": "2025-10-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 5 }, - "limit": { "context": 16384, "input": 16384, "output": 16384 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 2.4, + "cache_read": 0.03, + "cache_write": 0.38 + } }, - "jamba-large-1.7": { - "id": "jamba-large-1.7", - "name": "Jamba Large 1.7", + "minimax/minimax-m2.5": { + "id": "minimax/minimax-m2.5", + "name": "MiniMax M2.5", + "family": "minimax", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.989, "output": 7.99 }, - "limit": { "context": 256000, "input": 256000, "output": 4096 } - }, - "gemini-2.5-flash-lite": { - "id": "gemini-2.5-flash-lite", - "name": "Gemini 2.5 Flash Lite", - "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-12", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 1048756, "input": 1048756, "output": 65536 } + "limit": { + "context": 204800, + "output": 131000 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03, + "cache_write": 0.375 + } }, - "azure-o3-mini": { - "id": "azure-o3-mini", - "name": "Azure o3-mini", + "minimax/minimax-m2.5-highspeed": { + "id": "minimax/minimax-m2.5-highspeed", + "name": "MiniMax M2.5 High Speed", + "family": "minimax", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-01-31", - "last_updated": "2025-01-31", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.088, "output": 4.3996 }, - "limit": { "context": 200000, "input": 200000, "output": 65536 } - }, - "claude-sonnet-4-thinking:8192": { - "id": "claude-sonnet-4-thinking:8192", - "name": "Claude 4 Sonnet Thinking (8K)", - "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.992, "output": 14.994 }, - "limit": { "context": 1000000, "input": 1000000, "output": 64000 } - }, - "ernie-x1-32k": { - "id": "ernie-x1-32k", - "name": "Ernie X1 32k", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-05-08", - "last_updated": "2025-05-08", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "release_date": "2026-02-12", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.33, "output": 1.32 }, - "limit": { "context": 32000, "input": 32000, "output": 16384 } + "limit": { + "context": 0, + "output": 0 + }, + "cost": { + "input": 0.6, + "output": 2.4, + "cache_read": 0.03, + "cache_write": 0.375 + } }, - "exa-answer": { - "id": "exa-answer", - "name": "Exa (Answer)", + "kwaipilot/kat-coder-pro-v1": { + "id": "kwaipilot/kat-coder-pro-v1", + "name": "KAT-Coder-Pro V1", + "family": "kat-coder", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": false, - "structured_output": false, - "release_date": "2025-06-04", - "last_updated": "2025-06-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-10-24", + "last_updated": "2025-10-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 2.5 }, - "limit": { "context": 4096, "input": 4096, "output": 4096 } + "limit": { + "context": 256000, + "output": 32000 + } }, - "KAT-Coder-Pro-V1": { - "id": "KAT-Coder-Pro-V1", - "name": "KAT Coder Pro V1", + "kwaipilot/kat-coder-pro-v2": { + "id": "kwaipilot/kat-coder-pro-v2", + "name": "Kat Coder Pro V2", + "family": "kat-coder", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-10-28", - "last_updated": "2025-10-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.5, "output": 6 }, - "limit": { "context": 256000, "input": 256000, "output": 32768 } - }, - "ernie-4.5-turbo-128k": { - "id": "ernie-4.5-turbo-128k", - "name": "Ernie 4.5 Turbo 128k", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-05-08", - "last_updated": "2025-05-08", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.132, "output": 0.55 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } - }, - "claude-opus-4-5-20251101": { - "id": "claude-opus-4-5-20251101", - "name": "Claude 4.5 Opus", - "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-11-01", - "last_updated": "2025-11-01", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 4.998, "output": 25.007 }, - "limit": { "context": 200000, "input": 200000, "output": 32000 } - }, - "deepclaude": { - "id": "deepclaude", - "name": "DeepClaude", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-02-01", - "last_updated": "2025-02-01", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 } - }, - "Llama-3.3-70B-Forgotten-Abomination-v5.0": { - "id": "Llama-3.3-70B-Forgotten-Abomination-v5.0", - "name": "Llama 3.3 70B Forgotten Abomination v5.0", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } - }, - "qwen-image": { - "id": "qwen-image", - "name": "Qwen Image", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, "temperature": true, - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["image"] }, - "open_weights": false, - "limit": { "context": 0, "output": 0 } - }, - "gemini-2.5-pro-exp-03-25": { - "id": "gemini-2.5-pro-exp-03-25", - "name": "Gemini 2.5 Pro Experimental 0325", - "attachment": true, - "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-03-25", - "last_updated": "2025-03-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 1048756, "input": 1048756, "output": 65536 } - }, - "mistral-small-31-24b-instruct": { - "id": "mistral-small-31-24b-instruct", - "name": "Mistral Small 31 24b Instruct", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-15", - "last_updated": "2025-04-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-03-27", + "last_updated": "2026-03-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 128000, "input": 128000, "output": 131072 } + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06 + } }, - "claude-sonnet-4-thinking:1024": { - "id": "claude-sonnet-4-thinking:1024", - "name": "Claude 4 Sonnet Thinking (1K)", + "google/gemini-2.5-flash-lite-preview-09-2025": { + "id": "google/gemini-2.5-flash-lite-preview-09-2025", + "name": "Gemini 2.5 Flash Lite Preview 09-25", + "family": "gemini-flash-lite", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.992, "output": 14.994 }, - "limit": { "context": 1000000, "input": 1000000, "output": 64000 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.01 + } }, - "claude-opus-4-1-thinking": { - "id": "claude-opus-4-1-thinking", - "name": "Claude 4.1 Opus Thinking", + "google/gemini-3.1-flash-lite-preview": { + "id": "google/gemini-3.1-flash-lite-preview", + "name": "Gemini 3.1 Flash Lite Preview", + "family": "gemini", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "release_date": "2026-03-03", + "last_updated": "2026-03-06", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 14.994, "output": 75.004 }, - "limit": { "context": 200000, "input": 200000, "output": 32000 } + "limit": { + "context": 1000000, + "output": 65000 + }, + "cost": { + "input": 0.25, + "output": 1.5, + "cache_read": 0.025, + "cache_write": 1 + } }, - "jamba-large-1.6": { - "id": "jamba-large-1.6", - "name": "Jamba Large 1.6", + "google/gemini-3-pro-image": { + "id": "google/gemini-3-pro-image", + "name": "Nano Banana Pro (Gemini 3 Pro Image)", + "family": "gemini-pro", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-03-12", - "last_updated": "2025-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-03", + "release_date": "2025-09", + "last_updated": "2025-09", + "modalities": { + "input": ["text"], + "output": ["text", "image"] + }, "open_weights": false, - "cost": { "input": 1.989, "output": 7.99 }, - "limit": { "context": 256000, "input": 256000, "output": 4096 } + "limit": { + "context": 65536, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 120 + } }, - "universal-summarizer": { - "id": "universal-summarizer", - "name": "Universal Summarizer", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2023-05-01", - "last_updated": "2024-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "google/gemini-3.1-pro-preview": { + "id": "google/gemini-3.1-pro-preview", + "name": "Gemini 3.1 Pro Preview", + "family": "gemini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-19", + "last_updated": "2026-02-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 30, "output": 30 }, - "limit": { "context": 32768, "input": 32768, "output": 32768 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2 + } }, - "Llama-3.3-70B-Bigger-Body": { - "id": "Llama-3.3-70B-Bigger-Body", - "name": "Llama 3.3 70B Bigger Body", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "google/gemini-3-pro-preview": { + "id": "google/gemini-3-pro-preview", + "name": "Gemini 3 Pro Preview", + "family": "gemini-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2, + "tiers": [ + { + "input": 4, + "output": 18, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 18, + "cache_read": 0.4 + } + } }, - "doubao-seed-2-0-pro-260215": { - "id": "doubao-seed-2-0-pro-260215", - "name": "Doubao Seed 2.0 Pro", + "google/imagen-4.0-ultra-generate-001": { + "id": "google/imagen-4.0-ultra-generate-001", + "name": "Imagen 4 Ultra", + "family": "imagen", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2026-02-14", - "last_updated": "2026-02-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-05-24", + "last_updated": "2025-05-24", + "modalities": { + "input": ["text"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 0.782, "output": 3.876 }, - "limit": { "context": 256000, "input": 256000, "output": 128000 } + "limit": { + "context": 480, + "output": 0 + } }, - "Llama-3.3-70B-Progenitor-V3.3": { - "id": "Llama-3.3-70B-Progenitor-V3.3", - "name": "Llama 3.3 70B Progenitor V3.3", + "google/gemini-embedding-001": { + "id": "google/gemini-embedding-001", + "name": "Gemini Embedding 001", + "family": "gemini-embedding", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-07-26", - "last_updated": "2025-07-26", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-05-20", + "last_updated": "2025-05-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 8192, + "output": 1536 + }, + "cost": { + "input": 0.15, + "output": 0 + } }, - "claude-opus-4-1-thinking:32768": { - "id": "claude-opus-4-1-thinking:32768", - "name": "Claude 4.1 Opus Thinking (32K)", + "google/gemma-4-31b-it": { + "id": "google/gemma-4-31b-it", + "name": "Gemma 4 31B IT", + "family": "gemma", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "release_date": "2026-04-02", + "last_updated": "2026-04-03", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 14.994, "output": 75.004 }, - "limit": { "context": 200000, "input": 200000, "output": 32000 } + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0.14, + "output": 0.39999999999999997 + } }, - "doubao-seed-1-6-thinking-250615": { - "id": "doubao-seed-1-6-thinking-250615", - "name": "Doubao Seed 1.6 Thinking", + "google/gemini-2.5-flash-image": { + "id": "google/gemini-2.5-flash-image", + "name": "Nano Banana (Gemini 2.5 Flash Image)", + "family": "gemini-flash", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-06-15", - "last_updated": "2025-06-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-03-20", + "modalities": { + "input": ["text"], + "output": ["text", "image"] + }, "open_weights": false, - "cost": { "input": 0.204, "output": 2.04 }, - "limit": { "context": 256000, "input": 256000, "output": 16384 } + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.3, + "output": 2.5 + } }, - "Llama-3.3-70B-Fallen-v1": { - "id": "Llama-3.3-70B-Fallen-v1", - "name": "Llama 3.3 70B Fallen v1", + "google/text-embedding-005": { + "id": "google/text-embedding-005", + "name": "Text Embedding 005", + "family": "text-embedding", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2024-08", + "last_updated": "2024-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 8192, + "output": 1536 + }, + "cost": { + "input": 0.03, + "output": 0 + } }, - "glm-zero-preview": { - "id": "glm-zero-preview", - "name": "GLM Zero Preview", + "google/text-multilingual-embedding-002": { + "id": "google/text-multilingual-embedding-002", + "name": "Text Multilingual Embedding 002", + "family": "text-embedding", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2024-03", + "last_updated": "2024-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.802, "output": 1.802 }, - "limit": { "context": 8000, "input": 8000, "output": 4096 } + "limit": { + "context": 8192, + "output": 1536 + }, + "cost": { + "input": 0.03, + "output": 0 + } }, - "Llama-3.3-70B-MS-Nevoria": { - "id": "Llama-3.3-70B-MS-Nevoria", - "name": "Llama 3.3 70B MS Nevoria", - "attachment": false, - "reasoning": false, + "google/gemini-3.1-flash-image-preview": { + "id": "google/gemini-3.1-flash-image-preview", + "name": "Gemini 3.1 Flash Image Preview (Nano Banana 2)", + "family": "gemini", + "attachment": true, + "reasoning": true, "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "release_date": "2026-02-26", + "last_updated": "2026-03-06", + "modalities": { + "input": ["text", "image"], + "output": ["text", "image"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.5, + "output": 3 + } }, - "qwen-turbo": { - "id": "qwen-turbo", - "name": "Qwen Turbo", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-11-01", - "last_updated": "2024-11-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "google/gemini-3.1-flash-lite": { + "id": "google/gemini-3.1-flash-lite", + "name": "Gemini 3.1 Flash Lite", + "family": "gemini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-07", + "last_updated": "2026-05-08", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.04998, "output": 0.2006 }, - "limit": { "context": 1000000, "input": 1000000, "output": 8192 } + "limit": { + "context": 1000000, + "output": 65000 + }, + "cost": { + "input": 0.25, + "output": 1.5, + "cache_read": 0.03 + } }, - "glm-z1-air": { - "id": "glm-z1-air", - "name": "GLM Z1 Air", - "attachment": false, - "reasoning": false, + "google/gemini-3-flash": { + "id": "google/gemini-3-flash", + "name": "Gemini 3 Flash", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-04-15", - "last_updated": "2025-04-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-03", + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.07, "output": 0.07 }, - "limit": { "context": 32000, "input": 32000, "output": 16384 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.05 + } }, - "deepseek-v3-0324": { - "id": "deepseek-v3-0324", - "name": "DeepSeek Chat 0324", + "google/imagen-4.0-generate-001": { + "id": "google/imagen-4.0-generate-001", + "name": "Imagen 4", + "family": "imagen", "attachment": false, "reasoning": false, - "tool_call": true, - "structured_output": true, - "release_date": "2025-03-24", - "last_updated": "2025-03-24", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": false, + "temperature": false, + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 0.7 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 } + "limit": { + "context": 480, + "output": 0 + } }, - "gemini-2.5-flash-lite-preview-09-2025": { - "id": "gemini-2.5-flash-lite-preview-09-2025", - "name": "Gemini 2.5 Flash Lite Preview (09/2025)", + "google/gemini-2.5-flash-preview-09-2025": { + "id": "google/gemini-2.5-flash-preview-09-2025", + "name": "Gemini 2.5 Flash Preview 09-25", + "family": "gemini-flash", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, + "temperature": true, + "knowledge": "2025-01", "release_date": "2025-09-25", "last_updated": "2025-09-25", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 1048756, "input": 1048756, "output": 65536 } - }, - "qwq-32b": { - "id": "qwq-32b", - "name": "Qwen: QwQ 32B", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-15", - "last_updated": "2025-04-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25599999, "output": 0.30499999 }, - "limit": { "context": 128000, "input": 128000, "output": 32768 } - }, - "command-a-reasoning-08-2025": { - "id": "command-a-reasoning-08-2025", - "name": "Cohere Command A (08/2025)", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-08-22", - "last_updated": "2025-08-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 256000, "input": 256000, "output": 8192 } - }, - "GLM-4.5-Air-Derestricted-Iceblink-ReExtract": { - "id": "GLM-4.5-Air-Derestricted-Iceblink-ReExtract", - "name": "GLM 4.5 Air Derestricted Iceblink ReExtract", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-12", - "last_updated": "2025-12-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 131072, "input": 131072, "output": 98304 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.03, + "cache_write": 0.383 + } }, - "Llama-3.3-70B-Mokume-Gane-R1": { - "id": "Llama-3.3-70B-Mokume-Gane-R1", - "name": "Llama 3.3 70B Mokume Gane R1", + "google/gemini-embedding-2": { + "id": "google/gemini-embedding-2", + "name": "Gemini Embedding 2", + "family": "gemini-embedding", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "release_date": "2026-03-10", + "last_updated": "2026-03-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 0, + "output": 0 + } }, - "doubao-1-5-thinking-vision-pro-250428": { - "id": "doubao-1-5-thinking-vision-pro-250428", - "name": "Doubao 1.5 Thinking Vision Pro", + "google/gemma-4-26b-a4b-it": { + "id": "google/gemma-4-26b-a4b-it", + "name": "Gemma 4 26B A4B IT", + "family": "gemma", "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-05-15", - "last_updated": "2025-05-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.55, "output": 1.43 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } - }, - "Llama-3.3+(3v3.3)-70B-TenyxChat-DaybreakStorywriter": { - "id": "Llama-3.3+(3v3.3)-70B-TenyxChat-DaybreakStorywriter", - "name": "Llama 3.3+ 70B TenyxChat DaybreakStorywriter", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } - }, - "deepseek-r1": { - "id": "deepseek-r1", - "name": "DeepSeek R1", - "attachment": false, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 1.7 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 } - }, - "QwQ-32B-ArliAI-RpR-v1": { - "id": "QwQ-32B-ArliAI-RpR-v1", - "name": "QwQ 32b Arli V1", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-02", + "last_updated": "2026-04-03", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 32768, "input": 32768, "output": 32768 } + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0.13, + "output": 0.39999999999999997 + } }, - "GLM-4.6-Derestricted-v5": { - "id": "GLM-4.6-Derestricted-v5", - "name": "GLM 4.6 Derestricted v5", + "google/imagen-4.0-fast-generate-001": { + "id": "google/imagen-4.0-fast-generate-001", + "name": "Imagen 4 Fast", + "family": "imagen", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-06", + "last_updated": "2025-06", + "modalities": { + "input": ["text"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 1.5 }, - "limit": { "context": 131072, "input": 131072, "output": 8192 } + "limit": { + "context": 480, + "output": 0 + } }, - "qwen-max": { - "id": "qwen-max", - "name": "Qwen 2.5 Max", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-04-03", - "last_updated": "2024-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "google/gemini-2.5-flash-lite": { + "id": "google/gemini-2.5-flash-lite", + "name": "Gemini 2.5 Flash Lite", + "family": "gemini-flash-lite", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.5997, "output": 6.392 }, - "limit": { "context": 32000, "input": 32000, "output": 8192 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.01 + } }, - "glm-4": { - "id": "glm-4", - "name": "GLM-4", + "google/gemini-2.5-flash-image-preview": { + "id": "google/gemini-2.5-flash-image-preview", + "name": "Nano Banana Preview (Gemini 2.5 Flash Image Preview)", + "family": "gemini-flash", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2024-01-16", - "last_updated": "2024-01-16", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 14.994, "output": 14.994 }, - "limit": { "context": 128000, "input": 128000, "output": 4096 } - }, - "qvq-max": { - "id": "qvq-max", - "name": "Qwen: QvQ Max", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-03-28", - "last_updated": "2025-03-28", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-03-20", + "modalities": { + "input": ["text"], + "output": ["text", "image"] + }, "open_weights": false, - "cost": { "input": 1.4, "output": 5.3 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 } + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.3, + "output": 2.5 + } }, - "claude-opus-4-20250514": { - "id": "claude-opus-4-20250514", - "name": "Claude 4 Opus", + "google/gemini-2.0-flash-lite": { + "id": "google/gemini-2.0-flash-lite", + "name": "Gemini 2.0 Flash Lite", + "family": "gemini-flash-lite", "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, - "release_date": "2025-05-14", - "last_updated": "2025-05-14", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-06", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 14.994, "output": 75.004 }, - "limit": { "context": 200000, "input": 200000, "output": 32000 } + "limit": { + "context": 1048576, + "output": 8192 + }, + "cost": { + "input": 0.075, + "output": 0.3 + } }, - "gemini-2.5-flash-preview-04-17": { - "id": "gemini-2.5-flash-preview-04-17", - "name": "Gemini 2.5 Flash Preview", + "google/gemini-2.5-flash": { + "id": "google/gemini-2.5-flash", + "name": "Gemini 2.5 Flash", + "family": "gemini-flash", "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-17", - "last_updated": "2025-04-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 1048756, "input": 1048756, "output": 65536 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.03, + "input_audio": 1 + } }, - "claude-opus-4-thinking:32000": { - "id": "claude-opus-4-thinking:32000", - "name": "Claude 4 Opus Thinking (32K)", + "google/gemini-2.5-pro": { + "id": "google/gemini-2.5-pro", + "name": "Gemini 2.5 Pro", + "family": "gemini-pro", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 14.994, "output": 75.004 }, - "limit": { "context": 200000, "input": 200000, "output": 32000 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125, + "tiers": [ + { + "input": 2.5, + "output": 15, + "cache_read": 0.25, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 2.5, + "output": 15, + "cache_read": 0.25 + } + } }, - "qwen-long": { - "id": "qwen-long", - "name": "Qwen Long 10M", + "google/gemini-2.0-flash": { + "id": "google/gemini-2.0-flash", + "name": "Gemini 2.0 Flash", + "family": "gemini-flash", "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-01-25", - "last_updated": "2025-01-25", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2024-06", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1003, "output": 0.408 }, - "limit": { "context": 10000000, "input": 10000000, "output": 8192 } + "limit": { + "context": 1048576, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.025 + } }, - "Llama-3.3-70B-GeneticLemonade-Opus": { - "id": "Llama-3.3-70B-GeneticLemonade-Opus", - "name": "Llama 3.3 70B GeneticLemonade Opus", + "moonshotai/kimi-k2-turbo": { + "id": "moonshotai/kimi-k2-turbo", + "name": "Kimi K2 Turbo", + "family": "kimi", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-08", + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 256000, + "output": 16384 + }, + "cost": { + "input": 2.4, + "output": 10 + } }, - "doubao-1.5-pro-32k": { - "id": "doubao-1.5-pro-32k", - "name": "Doubao 1.5 Pro 32k", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-01-22", - "last_updated": "2025-01-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1343, "output": 0.3349 }, - "limit": { "context": 32000, "input": 32000, "output": 8192 } + "moonshotai/kimi-k2.5": { + "id": "moonshotai/kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-01-26", + "last_updated": "2026-01-26", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 1.2 + } }, - "Llama-3.3-70B-Forgotten-Safeword-3.6": { - "id": "Llama-3.3-70B-Forgotten-Safeword-3.6", - "name": "Llama 3.3 70B Forgotten Safeword 3.6", + "moonshotai/kimi-k2-thinking-turbo": { + "id": "moonshotai/kimi-k2-thinking-turbo", + "name": "Kimi K2 Thinking Turbo", + "family": "kimi-thinking", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "interleaved": true, + "temperature": true, + "knowledge": "2024-08", + "release_date": "2025-11-06", + "last_updated": "2025-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 262114, + "output": 262114 + }, + "cost": { + "input": 1.15, + "output": 8, + "cache_read": 0.15 + } }, - "grok-3-mini-fast-beta": { - "id": "grok-3-mini-fast-beta", - "name": "Grok 3 Mini Fast Beta", + "moonshotai/kimi-k2-0905": { + "id": "moonshotai/kimi-k2-0905", + "name": "Kimi K2 0905", + "family": "kimi", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 4 }, - "limit": { "context": 131072, "input": 131072, "output": 131072 } + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.6, + "output": 2.5 + } }, - "claude-3-7-sonnet-20250219": { - "id": "claude-3-7-sonnet-20250219", - "name": "Claude 3.7 Sonnet", + "moonshotai/kimi-k2.6": { + "id": "moonshotai/kimi-k2.6", + "name": "Kimi K2.6", + "family": "kimi-k2.6", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.992, "output": 14.994 }, - "limit": { "context": 200000, "input": 200000, "output": 16000 } + "temperature": true, + "release_date": "2026-04-20", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262000, + "output": 262000 + }, + "cost": { + "input": 0.95, + "output": 4, + "cache_read": 0.16 + } }, - "Llama-3.3-70B-ArliAI-RPMax-v3": { - "id": "Llama-3.3-70B-ArliAI-RPMax-v3", - "name": "Llama 3.3 70B ArliAI RPMax v3", + "moonshotai/kimi-k2-thinking": { + "id": "moonshotai/kimi-k2-thinking", + "name": "Kimi K2 Thinking", + "family": "kimi-thinking", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "interleaved": true, + "temperature": true, + "knowledge": "2024-08", + "release_date": "2025-11-06", + "last_updated": "2025-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 216144, + "output": 216144 + }, + "cost": { + "input": 0.47, + "output": 2, + "cache_read": 0.14 + } }, - "gemini-2.0-flash-thinking-exp-1219": { - "id": "gemini-2.0-flash-thinking-exp-1219", - "name": "Gemini 2.0 Flash Thinking 1219", + "moonshotai/kimi-k2": { + "id": "moonshotai/kimi-k2", + "name": "Kimi K2 Instruct", + "family": "kimi", "attachment": false, "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-07-14", + "last_updated": "2025-07-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "status": "deprecated", + "cost": { + "input": 1, + "output": 3 + } + }, + "interfaze/interfaze-beta": { + "id": "interfaze/interfaze-beta", + "name": "Interfaze Beta", + "attachment": false, + "reasoning": true, "tool_call": false, - "structured_output": false, - "release_date": "2024-12-19", - "last_updated": "2024-12-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "release_date": "2025-10-07", + "last_updated": "2026-04-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1003, "output": 0.408 }, - "limit": { "context": 32767, "input": 32767, "output": 8192 } + "limit": { + "context": 1000000, + "output": 32000 + }, + "cost": { + "input": 1.5, + "output": 3.5 + } }, - "gemini-2.5-flash-preview-05-20": { - "id": "gemini-2.5-flash-preview-05-20", - "name": "Gemini 2.5 Flash 0520", + "anthropic/claude-3.5-sonnet-20240620": { + "id": "anthropic/claude-3.5-sonnet-20240620", + "name": "Claude 3.5 Sonnet (2024-06-20)", + "family": "claude-sonnet", "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-05-20", - "last_updated": "2025-05-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2024-06-20", + "last_updated": "2024-06-20", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 1048000, "input": 1048000, "output": 65536 } + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "gemini-3-pro-preview": { - "id": "gemini-3-pro-preview", - "name": "Gemini 3 Pro", + "anthropic/claude-opus-4.6": { + "id": "anthropic/claude-opus-4.6", + "name": "Claude Opus 4.6", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "interleaved": true, + "temperature": true, + "knowledge": "2025-05-31", + "release_date": "2026-02", + "last_updated": "2026-02", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 12 }, - "limit": { "context": 1048756, "input": 1048756, "output": 65536 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "MiniMax-M1": { - "id": "MiniMax-M1", - "name": "MiniMax M1", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-06-16", - "last_updated": "2025-06-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "anthropic/claude-opus-4.7": { + "id": "anthropic/claude-opus-4.7", + "name": "Claude Opus 4.7", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1394, "output": 1.3328 }, - "limit": { "context": 1000000, "input": 1000000, "output": 131072 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "chroma": { - "id": "chroma", - "name": "Chroma", + "anthropic/claude-opus-4.5": { + "id": "anthropic/claude-opus-4.5", + "name": "Claude Opus 4.5", + "family": "claude-opus", "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, + "reasoning": true, + "tool_call": true, + "interleaved": true, "temperature": true, - "release_date": "2025-08-12", - "last_updated": "2025-08-12", - "modalities": { "input": ["text"], "output": ["image"] }, + "knowledge": "2025-03-31", + "release_date": "2025-11-24", + "last_updated": "2025-11-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 0, "output": 0 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 18.75 + } }, - "azure-o1": { - "id": "azure-o1", - "name": "Azure o1", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-17", - "last_updated": "2024-12-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "anthropic/claude-haiku-4.5": { + "id": "anthropic/claude-haiku-4.5", + "name": "Claude Haiku 4.5", + "family": "claude-haiku", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": true, + "temperature": true, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 14.994, "output": 59.993 }, - "limit": { "context": 200000, "input": 200000, "output": 100000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } }, - "claude-3-7-sonnet-thinking:128000": { - "id": "claude-3-7-sonnet-thinking:128000", - "name": "Claude 3.7 Sonnet Thinking (128K)", + "anthropic/claude-sonnet-4.6": { + "id": "anthropic/claude-sonnet-4.6", + "name": "Claude Sonnet 4.6", + "family": "claude-sonnet", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-02-24", - "last_updated": "2025-02-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "interleaved": true, + "temperature": true, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-02-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.992, "output": 14.994 }, - "limit": { "context": 200000, "input": 200000, "output": 64000 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75, + "tiers": [ + { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5 + } + } }, - "venice-uncensored:web": { - "id": "venice-uncensored:web", - "name": "Venice Uncensored Web", - "attachment": false, + "anthropic/claude-3-opus": { + "id": "anthropic/claude-3-opus", + "name": "Claude Opus 3", + "family": "claude-opus", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-05-01", - "last_updated": "2024-05-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2023-08-31", + "release_date": "2024-02-29", + "last_updated": "2024-02-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 0.4 }, - "limit": { "context": 80000, "input": 80000, "output": 16384 } + "limit": { + "context": 200000, + "output": 4096 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "deepseek-r1-sambanova": { - "id": "deepseek-r1-sambanova", - "name": "DeepSeek R1 Fast", - "attachment": false, + "anthropic/claude-3.5-haiku": { + "id": "anthropic/claude-3.5-haiku", + "name": "Claude Haiku 3.5", + "family": "claude-haiku", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-02-20", - "last_updated": "2025-02-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-07-31", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 4.998, "output": 6.987 }, - "limit": { "context": 128000, "input": 128000, "output": 4096 } + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 4, + "cache_read": 0.08, + "cache_write": 1 + } }, - "qwen25-vl-72b-instruct": { - "id": "qwen25-vl-72b-instruct", - "name": "Qwen25 VL 72b", + "anthropic/claude-opus-4": { + "id": "anthropic/claude-opus-4", + "name": "Claude Opus 4", + "family": "claude-opus", "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-05-10", - "last_updated": "2025-05-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.69989, "output": 0.69989 }, - "limit": { "context": 32000, "input": 32000, "output": 32768 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "brave-pro": { - "id": "brave-pro", - "name": "Brave (Pro)", - "attachment": false, + "anthropic/claude-3-haiku": { + "id": "anthropic/claude-3-haiku", + "name": "Claude Haiku 3", + "family": "claude-haiku", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2023-03-02", - "last_updated": "2024-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2023-08-31", + "release_date": "2024-03-13", + "last_updated": "2024-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 5 }, - "limit": { "context": 8192, "input": 8192, "output": 8192 } + "limit": { + "context": 200000, + "output": 4096 + }, + "cost": { + "input": 0.25, + "output": 1.25, + "cache_read": 0.03, + "cache_write": 0.3 + } }, - "glm-4-airx": { - "id": "glm-4-airx", - "name": "GLM-4 AirX", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-06-05", - "last_updated": "2024-06-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "anthropic/claude-sonnet-4.5": { + "id": "anthropic/claude-sonnet-4.5", + "name": "Claude Sonnet 4.5", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.006, "output": 2.006 }, - "limit": { "context": 8000, "input": 8000, "output": 4096 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "deepseek-chat": { - "id": "deepseek-chat", - "name": "DeepSeek V3/Deepseek Chat", + "anthropic/claude-sonnet-4": { + "id": "anthropic/claude-sonnet-4", + "name": "Claude Sonnet 4", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + "anthropic/claude-3.5-sonnet": { + "id": "anthropic/claude-3.5-sonnet", + "name": "Claude Sonnet 3.5 v2", + "family": "claude-sonnet", "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "release_date": "2025-02-27", - "last_updated": "2025-02-27", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 0.7 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 } - }, - "Llama-3.3-70B-Cu-Mai-R1": { - "id": "Llama-3.3-70B-Cu-Mai-R1", - "name": "Llama 3.3 70B Cu Mai R1", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-04-30", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "deepseek-chat-cheaper": { - "id": "deepseek-chat-cheaper", - "name": "DeepSeek V3/Chat Cheaper", + "anthropic/claude-3.7-sonnet": { + "id": "anthropic/claude-3.7-sonnet", + "name": "Claude Sonnet 3.7", + "family": "claude-sonnet", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-04-15", - "last_updated": "2025-04-15", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-10-31", + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 0.7 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "ernie-5.0-thinking-latest": { - "id": "ernie-5.0-thinking-latest", - "name": "Ernie 5.0 Thinking", + "anthropic/claude-opus-4.1": { + "id": "anthropic/claude-opus-4.1", + "name": "Claude Opus 4", + "family": "claude-opus", "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 2 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "claude-sonnet-4-5-20250929": { - "id": "claude-sonnet-4-5-20250929", - "name": "Claude Sonnet 4.5", + "xiaomi/mimo-v2.5-pro": { + "id": "xiaomi/mimo-v2.5-pro", + "name": "MiMo V2.5 Pro", + "family": "mimo-v2.5-pro", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "release_date": "2026-04-22", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.992, "output": 14.994 }, - "limit": { "context": 1000000, "input": 1000000, "output": 64000 } + "limit": { + "context": 1050000, + "output": 131000 + }, + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.19999999999999998 + } }, - "claude-opus-4-1-thinking:1024": { - "id": "claude-opus-4-1-thinking:1024", - "name": "Claude 4.1 Opus Thinking (1K)", + "xiaomi/mimo-v2.5": { + "id": "xiaomi/mimo-v2.5", + "name": "MiMo M2.5", + "family": "mimo-v2.5", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "release_date": "2026-04-22", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 14.994, "output": 75.004 }, - "limit": { "context": 200000, "input": 200000, "output": 32000 } + "limit": { + "context": 1050000, + "output": 131100 + }, + "cost": { + "input": 0.39999999999999997, + "output": 2, + "cache_read": 0.08 + } }, - "qwen-plus": { - "id": "qwen-plus", - "name": "Qwen Plus", + "xiaomi/mimo-v2-pro": { + "id": "xiaomi/mimo-v2-pro", + "name": "MiMo V2 Pro", + "family": "mimo", "attachment": false, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2024-01-25", - "last_updated": "2024-01-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-18", + "last_updated": "2026-03-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3995, "output": 1.2002 }, - "limit": { "context": 995904, "input": 995904, "output": 32768 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.19999999999999998 + } }, - "ernie-x1-32k-preview": { - "id": "ernie-x1-32k-preview", - "name": "Ernie X1 32k", + "xiaomi/mimo-v2-flash": { + "id": "xiaomi/mimo-v2-flash", + "name": "MiMo V2 Flash", + "family": "mimo", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.33, "output": 1.32 }, - "limit": { "context": 32000, "input": 32000, "output": 16384 } + "limit": { + "context": 262144, + "output": 32000 + }, + "cost": { + "input": 0.1, + "output": 0.29 + } }, - "Llama-3.3-70B-The-Omega-Directive-Unslop-v2.1": { - "id": "Llama-3.3-70B-The-Omega-Directive-Unslop-v2.1", - "name": "Llama 3.3 70B Omega Directive Unslop v2.1", + "bytedance/seed-1.6": { + "id": "bytedance/seed-1.6", + "name": "Seed 1.6", + "family": "seed", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } - }, - "gemini-2.5-pro-preview-06-05": { - "id": "gemini-2.5-pro-preview-06-05", - "name": "Gemini 2.5 Pro Preview 0605", - "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-06-05", - "last_updated": "2025-06-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-09", + "last_updated": "2025-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 1048756, "input": 1048756, "output": 65536 } + "limit": { + "context": 256000, + "output": 32000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.05 + } }, - "claude-sonnet-4-thinking:64000": { - "id": "claude-sonnet-4-thinking:64000", - "name": "Claude 4 Sonnet Thinking (64K)", - "attachment": true, + "bytedance/seed-1.8": { + "id": "bytedance/seed-1.8", + "name": "Seed 1.8", + "family": "seed", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-10", + "last_updated": "2025-10", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.992, "output": 14.994 }, - "limit": { "context": 1000000, "input": 1000000, "output": 64000 } + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.05 + } }, - "glm-z1-airx": { - "id": "glm-z1-airx", - "name": "GLM Z1 AirX", + "meituan/longcat-flash-chat": { + "id": "meituan/longcat-flash-chat", + "name": "LongCat Flash Chat", + "family": "longcat", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, - "release_date": "2025-04-15", - "last_updated": "2025-04-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-08-30", + "last_updated": "2025-08-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.7, "output": 0.7 }, - "limit": { "context": 32000, "input": 32000, "output": 16384 } + "limit": { + "context": 128000, + "output": 8192 + } }, - "qwen3-vl-235b-a22b-instruct-original": { - "id": "qwen3-vl-235b-a22b-instruct-original", - "name": "Qwen3 VL 235B A22B Instruct Original", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "meituan/longcat-flash-thinking": { + "id": "meituan/longcat-flash-thinking", + "name": "LongCat Flash Thinking", + "family": "longcat", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-09-23", + "last_updated": "2025-09-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 1.2 }, - "limit": { "context": 32768, "input": 32768, "output": 32768 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.15, + "output": 1.5 + } }, - "yi-lightning": { - "id": "yi-lightning", - "name": "Yi Lightning", + "meituan/longcat-flash-thinking-2601": { + "id": "meituan/longcat-flash-thinking-2601", + "name": "LongCat Flash Thinking 2601", + "family": "longcat", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": false, - "structured_output": false, - "release_date": "2024-10-16", - "last_updated": "2024-10-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "release_date": "2026-03-13", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2006, "output": 0.2006 }, - "limit": { "context": 12000, "input": 12000, "output": 4096 } + "limit": { + "context": 32768, + "output": 32768 + } }, - "sonar-deep-research": { - "id": "sonar-deep-research", - "name": "Perplexity Deep Research", + "bfl/flux-pro-1.0-fill": { + "id": "bfl/flux-pro-1.0-fill", + "name": "FLUX.1 Fill [pro]", + "family": "flux", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-02-25", - "last_updated": "2025-02-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2024-10", + "last_updated": "2024-10", + "modalities": { + "input": ["text"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 3.4, "output": 13.6 }, - "limit": { "context": 60000, "input": 60000, "output": 128000 } + "limit": { + "context": 512, + "output": 0 + } }, - "Llama-3.3-70B-Ignition-v0.1": { - "id": "Llama-3.3-70B-Ignition-v0.1", - "name": "Llama 3.3 70B Ignition v0.1", + "bfl/flux-pro-1.1": { + "id": "bfl/flux-pro-1.1", + "name": "FLUX1.1 [pro]", + "family": "flux", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2024-10", + "last_updated": "2024-10", + "modalities": { + "input": ["text"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 512, + "output": 0 + } }, - "kimi-k2-instruct-fast": { - "id": "kimi-k2-instruct-fast", - "name": "Kimi K2 0711 Fast", - "attachment": true, + "bfl/flux-kontext-pro": { + "id": "bfl/flux-kontext-pro", + "name": "FLUX.1 Kontext Pro", + "family": "flux", + "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-07-15", - "last_updated": "2025-07-15", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 2 }, - "limit": { "context": 131072, "input": 131072, "output": 16384 } - }, - "gemini-2.5-pro-preview-05-06": { - "id": "gemini-2.5-pro-preview-05-06", - "name": "Gemini 2.5 Pro Preview 0506", - "attachment": true, - "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-05-06", - "last_updated": "2025-05-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 1048756, "input": 1048756, "output": 65536 } - }, - "gemini-2.5-flash-preview-05-20:thinking": { - "id": "gemini-2.5-flash-preview-05-20:thinking", - "name": "Gemini 2.5 Flash 0520 Thinking", - "attachment": true, - "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-05-20", - "last_updated": "2025-05-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-06", + "last_updated": "2025-06", + "modalities": { + "input": ["text"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 3.5 }, - "limit": { "context": 1048000, "input": 1048000, "output": 65536 } + "limit": { + "context": 512, + "output": 0 + } }, - "auto-model-premium": { - "id": "auto-model-premium", - "name": "Auto model (Premium)", + "bfl/flux-kontext-max": { + "id": "bfl/flux-kontext-max", + "name": "FLUX.1 Kontext Max", + "family": "flux", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2024-06-01", - "last_updated": "2024-06-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-06", + "last_updated": "2025-06", + "modalities": { + "input": ["text"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 9.996, "output": 19.992 }, - "limit": { "context": 1000000, "input": 1000000, "output": 1000000 } + "limit": { + "context": 512, + "output": 0 + } }, - "glm-4.1v-thinking-flash": { - "id": "glm-4.1v-thinking-flash", - "name": "GLM 4.1V Thinking Flash", - "attachment": true, + "bfl/flux-pro-1.1-ultra": { + "id": "bfl/flux-pro-1.1-ultra", + "name": "FLUX1.1 [pro] Ultra", + "family": "flux", + "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "release_date": "2024-11", + "last_updated": "2024-11", + "modalities": { + "input": ["text"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 0.3 }, - "limit": { "context": 64000, "input": 64000, "output": 8192 } + "limit": { + "context": 512, + "output": 0 + } + } + } + }, + "minimax": { + "id": "minimax", + "env": ["MINIMAX_API_KEY"], + "npm": "@ai-sdk/anthropic", + "api": "https://api.minimax.io/anthropic/v1", + "name": "MiniMax (minimax.io)", + "doc": "https://platform.minimax.io/docs/guides/quickstart", + "models": { + "MiniMax-M2": { + "id": "MiniMax-M2", + "name": "MiniMax-M2", + "family": "minimax", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-10-27", + "last_updated": "2025-10-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 196608, + "output": 128000 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "claude-3-5-haiku-20241022": { - "id": "claude-3-5-haiku-20241022", - "name": "Claude 3.5 Haiku", - "attachment": true, - "reasoning": false, + "MiniMax-M2.5": { + "id": "MiniMax-M2.5", + "name": "MiniMax-M2.5", + "family": "minimax", + "attachment": false, + "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.8, "output": 4 }, - "limit": { "context": 200000, "input": 200000, "output": 8192 } + "temperature": true, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03, + "cache_write": 0.375 + } }, - "sonar": { - "id": "sonar", - "name": "Perplexity Simple", + "MiniMax-M2.7": { + "id": "MiniMax-M2.7", + "name": "MiniMax-M2.7", + "family": "minimax", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.003, "output": 1.003 }, - "limit": { "context": 127000, "input": 127000, "output": 128000 } + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06, + "cache_write": 0.375 + } }, - "auto-model": { - "id": "auto-model", - "name": "Auto model", + "MiniMax-M2.7-highspeed": { + "id": "MiniMax-M2.7-highspeed", + "name": "MiniMax-M2.7-highspeed", + "family": "minimax", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 2.4, + "cache_read": 0.06, + "cache_write": 0.375 + } + }, + "MiniMax-M2.1": { + "id": "MiniMax-M2.1", + "name": "MiniMax-M2.1", + "family": "minimax", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } + }, + "MiniMax-M2.5-highspeed": { + "id": "MiniMax-M2.5-highspeed", + "name": "MiniMax-M2.5-highspeed", + "family": "minimax", "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-13", + "last_updated": "2026-02-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 2.4, + "cache_read": 0.06, + "cache_write": 0.375 + } + } + } + }, + "llmgateway": { + "id": "llmgateway", + "env": ["LLMGATEWAY_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.llmgateway.io/v1", + "name": "LLM Gateway", + "doc": "https://llmgateway.io/docs", + "models": { + "gpt-4o-mini-search-preview": { + "id": "gpt-4o-mini-search-preview", + "name": "GPT-4o Mini Search Preview", + "family": "gpt", + "attachment": true, "reasoning": false, "tool_call": false, "structured_output": false, - "release_date": "2024-06-01", - "last_updated": "2024-06-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "release_date": "2024-10-01", + "last_updated": "2024-10-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 1000000, "input": 1000000, "output": 1000000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "gemini-2.0-flash-001": { - "id": "gemini-2.0-flash-001", - "name": "Gemini 2.0 Flash", + "grok-4-1-fast-reasoning": { + "id": "grok-4-1-fast-reasoning", + "name": "Grok 4.1 Fast Reasoning", + "family": "grok", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "release_date": "2025-11-19", + "last_updated": "2025-11-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1003, "output": 0.408 }, - "limit": { "context": 1000000, "input": 1000000, "output": 8192 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "Llama-3.3-70B-Mhnnn-x1": { - "id": "Llama-3.3-70B-Mhnnn-x1", - "name": "Llama 3.3 70B Mhnnn x1", + "qwen3-235b-a22b-instruct-2507": { + "id": "qwen3-235b-a22b-instruct-2507", + "name": "Qwen3 235B A22B Instruct (2507)", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-07-08", + "last_updated": "2025-07-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 2.4 + } }, - "step-2-16k-exp": { - "id": "step-2-16k-exp", - "name": "Step-2 16k Exp", + "llama-4-scout": { + "id": "llama-4-scout", + "name": "Llama 4 Scout", + "family": "llama", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "structured_output": false, - "release_date": "2024-07-05", - "last_updated": "2024-07-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 7.004, "output": 19.992 }, - "limit": { "context": 16000, "input": 16000, "output": 8192 } + "temperature": true, + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 16384 + }, + "status": "beta", + "cost": { + "input": 0.18, + "output": 0.59 + } }, - "Llama-3.3-70B-Legion-V2.1": { - "id": "Llama-3.3-70B-Legion-V2.1", - "name": "Llama 3.3 70B Legion V2.1", + "hermes-2-pro-llama-3-8b": { + "id": "hermes-2-pro-llama-3-8b", + "name": "Hermes 2 Pro Llama 3 8B", + "family": "hermes", "attachment": false, "reasoning": false, "tool_call": false, "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "temperature": true, + "release_date": "2024-05-27", + "last_updated": "2024-05-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 0.14, + "output": 0.14 + } }, - "Llama-3.3+(3.1v3.3)-70B-New-Dawn-v1.1": { - "id": "Llama-3.3+(3.1v3.3)-70B-New-Dawn-v1.1", - "name": "Llama 3.3+ 70B New Dawn v1.1", + "qwen-coder-plus": { + "id": "qwen-coder-plus", + "name": "Qwen Coder Plus", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } - }, - "doubao-1-5-thinking-pro-vision-250415": { - "id": "doubao-1-5-thinking-pro-vision-250415", - "name": "Doubao 1.5 Thinking Pro Vision", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-15", - "last_updated": "2025-04-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2024-09-18", + "last_updated": "2024-09-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 2.4 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.5, + "output": 1 + } }, - "gemini-2.0-flash-lite": { - "id": "gemini-2.0-flash-lite", - "name": "Gemini 2.0 Flash Lite", + "auto": { + "id": "auto", + "name": "Auto Route", + "family": "auto", "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-11", - "last_updated": "2024-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.0748, "output": 0.306 }, - "limit": { "context": 1000000, "input": 1000000, "output": 8192 } - }, - "claude-sonnet-4-5-20250929-thinking": { - "id": "claude-sonnet-4-5-20250929-thinking", - "name": "Claude Sonnet 4.5 Thinking", - "attachment": true, - "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.992, "output": 14.994 }, - "limit": { "context": 1000000, "input": 1000000, "output": 64000 } - }, - "yi-large": { - "id": "yi-large", - "name": "Yi Large", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-05-13", - "last_updated": "2024-05-13", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3.196, "output": 3.196 }, - "limit": { "context": 32000, "input": 32000, "output": 4096 } - }, - "Gemma-3-27B-Nidum-Uncensored": { - "id": "Gemma-3-27B-Nidum-Uncensored", - "name": "Gemma 3 27B Nidum Uncensored", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-08-08", - "last_updated": "2025-08-08", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "release_date": "2024-01-01", + "last_updated": "2024-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 96000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "claude-opus-4-thinking:32768": { - "id": "claude-opus-4-thinking:32768", - "name": "Claude 4 Opus Thinking (32K)", + "glm-4.6v-flashx": { + "id": "glm-4.6v-flashx", + "name": "GLM-4.6V FlashX", + "family": "glm", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "release_date": "2025-12-08", + "last_updated": "2025-12-08", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 14.994, "output": 75.004 }, - "limit": { "context": 200000, "input": 200000, "output": 32000 } + "limit": { + "context": 128000, + "output": 16000 + }, + "cost": { + "input": 0.04, + "output": 0.4, + "cache_read": 0 + } }, - "Llama-3.3-70B-Cirrus-x1": { - "id": "Llama-3.3-70B-Cirrus-x1", - "name": "Llama 3.3 70B Cirrus x1", + "gemma-2-27b-it-together": { + "id": "gemma-2-27b-it-together", + "name": "Gemma 2 27B IT", + "family": "gemma", "attachment": false, "reasoning": false, "tool_call": false, "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "temperature": true, + "release_date": "2024-06-27", + "last_updated": "2024-06-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 16384 + }, + "cost": { + "input": 0.08, + "output": 0.08 + } }, - "Gemma-3-27B-CardProjector-v4": { - "id": "Gemma-3-27B-CardProjector-v4", - "name": "Gemma 3 27B CardProjector v4", + "codestral-2508": { + "id": "codestral-2508", + "name": "Codestral", + "family": "mistral", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-03-10", - "last_updated": "2025-03-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "structured_output": true, + "temperature": true, + "release_date": "2025-07-30", + "last_updated": "2025-07-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 16384 + }, + "cost": { + "input": 0.3, + "output": 0.9 + } }, - "Qwen2.5-32B-EVA-v0.2": { - "id": "Qwen2.5-32B-EVA-v0.2", - "name": "Qwen 2.5 32b EVA", + "gemma-3-1b-it": { + "id": "gemma-3-1b-it", + "name": "Gemma 3 1B IT", + "family": "gemma", "attachment": false, "reasoning": false, "tool_call": false, "structured_output": false, - "release_date": "2024-09-01", - "last_updated": "2024-09-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.493, "output": 0.493 }, - "limit": { "context": 24576, "input": 24576, "output": 8192 } - }, - "sonar-reasoning-pro": { - "id": "sonar-reasoning-pro", - "name": "Perplexity Reasoning Pro", - "attachment": false, - "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.006, "output": 7.9985 }, - "limit": { "context": 127000, "input": 127000, "output": 128000 } + "temperature": true, + "release_date": "2025-03-12", + "last_updated": "2025-03-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 16384 + }, + "cost": { + "input": 0.08, + "output": 0.3 + } }, - "v0-1.5-lg": { - "id": "v0-1.5-lg", - "name": "v0 1.5 LG", + "glm-4-32b-0414-128k": { + "id": "glm-4-32b-0414-128k", + "name": "GLM-4 32B (0414-128k)", + "family": "glm", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-07-04", - "last_updated": "2025-07-04", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75 }, - "limit": { "context": 1000000, "input": 1000000, "output": 64000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "gemini-2.5-flash-preview-09-2025-thinking": { - "id": "gemini-2.5-flash-preview-09-2025-thinking", - "name": "Gemini 2.5 Flash Preview (09/2025) – Thinking", + "seed-1-6-flash-250715": { + "id": "seed-1-6-flash-250715", + "name": "Seed 1.6 Flash (250715)", + "family": "seed", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.5 }, - "limit": { "context": 1048756, "input": 1048756, "output": 65536 } - }, - "azure-gpt-4-turbo": { - "id": "azure-gpt-4-turbo", - "name": "Azure gpt-4-turbo", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2023-11-06", - "last_updated": "2024-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 9.996, "output": 30.005 }, - "limit": { "context": 128000, "input": 128000, "output": 4096 } + "temperature": true, + "release_date": "2025-07-26", + "last_updated": "2025-07-26", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 8192 + }, + "cost": { + "input": 0.07, + "output": 0.3, + "cache_read": 0.01 + } }, - "deepseek-reasoner": { - "id": "deepseek-reasoner", - "name": "DeepSeek Reasoner", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 1.7 }, - "limit": { "context": 64000, "input": 64000, "output": 65536 } + "seed-1-6-250615": { + "id": "seed-1-6-250615", + "name": "Seed 1.6 (250615)", + "family": "seed", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-06-25", + "last_updated": "2025-06-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 8192 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.05 + } }, - "gemini-2.5-flash-nothinking": { - "id": "gemini-2.5-flash-nothinking", - "name": "Gemini 2.5 Flash (No Thinking)", + "qwen3-vl-235b-a22b-thinking": { + "id": "qwen3-vl-235b-a22b-thinking", + "name": "Qwen3 VL 235B A22B Thinking", + "family": "qwen", "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-06-05", - "last_updated": "2025-06-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.5 }, - "limit": { "context": 1048756, "input": 1048756, "output": 65536 } + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-09-15", + "last_updated": "2025-09-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 2.4 + } }, - "Gemma-3-27B-ArliAI-RPMax-v3": { - "id": "Gemma-3-27B-ArliAI-RPMax-v3", - "name": "Gemma 3 27B RPMax v3", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-07-03", - "last_updated": "2025-07-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "qwen3-vl-30b-a3b-thinking": { + "id": "qwen3-vl-30b-a3b-thinking", + "name": "Qwen3 VL 30B A3B Thinking", + "family": "qwen", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-10-02", + "last_updated": "2025-10-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "v0-1.0-md": { - "id": "v0-1.0-md", - "name": "v0 1.0 MD", - "attachment": false, + "qwen2-5-vl-32b-instruct": { + "id": "qwen2-5-vl-32b-instruct", + "name": "Qwen2.5 VL 32B Instruct", + "family": "qwen", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-07-04", - "last_updated": "2025-07-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 200000, "input": 200000, "output": 64000 } + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-03-15", + "last_updated": "2025-03-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 0.3 + } }, - "step-3": { - "id": "step-3", - "name": "Step-3", + "qwen3-vl-8b-instruct": { + "id": "qwen3-vl-8b-instruct", + "name": "Qwen3 VL 8B Instruct", + "family": "qwen", "attachment": true, "reasoning": false, "tool_call": false, "structured_output": false, - "release_date": "2025-07-31", - "last_updated": "2025-07-31", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2499, "output": 0.6494 }, - "limit": { "context": 65536, "input": 65536, "output": 8192 } + "temperature": true, + "release_date": "2025-08-19", + "last_updated": "2025-08-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "brave": { - "id": "brave", - "name": "Brave (Answers)", + "claude-3-7-sonnet": { + "id": "claude-3-7-sonnet", + "name": "Claude 3.7 Sonnet", + "family": "claude", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "structured_output": false, - "release_date": "2023-03-02", - "last_updated": "2024-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "release_date": "2025-02-24", + "last_updated": "2025-02-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 5 }, - "limit": { "context": 8192, "input": 8192, "output": 8192 } + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3 + } }, - "ernie-x1-turbo-32k": { - "id": "ernie-x1-turbo-32k", - "name": "Ernie X1 Turbo 32k", + "gemini-pro-latest": { + "id": "gemini-pro-latest", + "name": "Gemini Pro Latest", + "family": "gemini", "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-05-08", - "last_updated": "2025-05-08", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.165, "output": 0.66 }, - "limit": { "context": 32000, "input": 32000, "output": 16384 } - }, - "glm-4-air": { - "id": "glm-4-air", - "name": "GLM-4 Air", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-06-05", - "last_updated": "2024-06-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2026-02-27", + "last_updated": "2026-02-27", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2006, "output": 0.2006 }, - "limit": { "context": 128000, "input": 128000, "output": 4096 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2 + } }, - "GLM-4.5-Air-Derestricted": { - "id": "GLM-4.5-Air-Derestricted", - "name": "GLM 4.5 Air Derestricted", + "claude-3-5-haiku": { + "id": "claude-3-5-haiku", + "name": "Claude 3.5 Haiku", + "family": "claude", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 202600, "input": 202600, "output": 98304 } - }, - "grok-3-fast-beta": { - "id": "grok-3-fast-beta", - "name": "Grok 3 Fast Beta", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, + "tool_call": true, + "structured_output": false, + "temperature": true, + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25 }, - "limit": { "context": 131072, "input": 131072, "output": 131072 } + "limit": { + "context": 200000, + "output": 8192 + }, + "status": "deprecated", + "cost": { + "input": 0.8, + "output": 4, + "cache_read": 0.08 + } }, - "claude-sonnet-4-20250514": { - "id": "claude-sonnet-4-20250514", - "name": "Claude 4 Sonnet", + "qwen-max-latest": { + "id": "qwen-max-latest", + "name": "Qwen Max Latest", + "family": "qwen", "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "release_date": "2025-01-25", + "last_updated": "2025-01-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.992, "output": 14.994 }, - "limit": { "context": 200000, "input": 200000, "output": 64000 } + "limit": { + "context": 32768, + "output": 8192 + }, + "cost": { + "input": 1.6, + "output": 6.4 + } }, - "claude-3-7-sonnet-thinking:1024": { - "id": "claude-3-7-sonnet-thinking:1024", - "name": "Claude 3.7 Sonnet Thinking (1K)", + "glm-4.6v-flash": { + "id": "glm-4.6v-flash", + "name": "GLM-4.6V Flash", + "family": "glm", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-02-24", - "last_updated": "2025-02-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.992, "output": 14.994 }, - "limit": { "context": 200000, "input": 200000, "output": 64000 } + "temperature": true, + "release_date": "2025-12-08", + "last_updated": "2025-12-08", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 16000 + }, + "status": "beta", + "cost": { + "input": 0, + "output": 0 + } }, - "doubao-seed-code-preview-latest": { - "id": "doubao-seed-code-preview-latest", - "name": "Doubao Seed Code Preview", + "qwen3-30b-a3b-instruct-2507": { + "id": "qwen3-30b-a3b-instruct-2507", + "name": "Qwen3 30B A3B Instruct (2507)", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-07-08", + "last_updated": "2025-07-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } + }, + "minimax-text-01": { + "id": "minimax-text-01", + "name": "MiniMax Text 01", + "family": "minimax", "attachment": false, "reasoning": true, "tool_call": false, "structured_output": false, - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 256000, "input": 256000, "output": 16384 } + "temperature": true, + "release_date": "2025-01-15", + "last_updated": "2025-01-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 131072 + }, + "cost": { + "input": 0.2, + "output": 1.1 + } }, - "grok-3-beta": { - "id": "grok-3-beta", - "name": "Grok 3 Beta", + "qwen3-32b-fp8": { + "id": "qwen3-32b-fp8", + "name": "Qwen3 32B FP8", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 131072, "input": 131072, "output": 131072 } + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-04-28", + "last_updated": "2025-04-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "claude-3-7-sonnet-reasoner": { - "id": "claude-3-7-sonnet-reasoner", - "name": "Claude 3.7 Sonnet Reasoner", + "llama-4-scout-17b-instruct": { + "id": "llama-4-scout-17b-instruct", + "name": "Llama 4 Scout 17B Instruct", + "family": "llama", "attachment": true, "reasoning": false, "tool_call": false, "structured_output": false, - "release_date": "2025-03-29", - "last_updated": "2025-03-29", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 } + "temperature": true, + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 2048 + }, + "cost": { + "input": 0.17, + "output": 0.66 + } }, - "gemini-2.0-pro-exp-02-05": { - "id": "gemini-2.0-pro-exp-02-05", - "name": "Gemini 2.0 Pro 0205", + "qwen3-4b-fp8": { + "id": "qwen3-4b-fp8", + "name": "Qwen3 4B FP8", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-04-28", + "last_updated": "2025-04-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.03, + "output": 0.05 + } + }, + "ministral-8b-2512": { + "id": "ministral-8b-2512", + "name": "Ministral 8B", + "family": "mistral", "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-02-05", - "last_updated": "2025-02-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.989, "output": 7.956 }, - "limit": { "context": 2097152, "input": 2097152, "output": 8192 } + "structured_output": true, + "temperature": true, + "release_date": "2025-12-02", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 8192 + }, + "cost": { + "input": 0.15, + "output": 0.15 + } }, - "glm-4-long": { - "id": "glm-4-long", - "name": "GLM-4 Long", - "attachment": false, + "gemma-3-27b": { + "id": "gemma-3-27b", + "name": "Gemma 3 27B", + "family": "gemma", + "attachment": true, "reasoning": false, "tool_call": false, "structured_output": false, - "release_date": "2024-08-01", - "last_updated": "2024-08-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2006, "output": 0.2006 }, - "limit": { "context": 1000000, "input": 1000000, "output": 4096 } + "temperature": true, + "release_date": "2025-03-12", + "last_updated": "2025-03-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.27, + "output": 0.27 + } }, - "venice-uncensored": { - "id": "venice-uncensored", - "name": "Venice Uncensored", - "attachment": false, + "qwen3-vl-flash": { + "id": "qwen3-vl-flash", + "name": "Qwen3 VL Flash", + "family": "qwen", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-02-24", - "last_updated": "2025-02-24", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-10-09", + "last_updated": "2025-10-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 0.4 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "limit": { + "context": 1000000, + "output": 32000 + }, + "cost": { + "input": 0.05, + "output": 0.4, + "cache_read": 0.01 + } }, - "Gemma-3-27B-Big-Tiger-v3": { - "id": "Gemma-3-27B-Big-Tiger-v3", - "name": "Gemma 3 27B Big Tiger v3", + "llama-3.1-70b-instruct": { + "id": "llama-3.1-70b-instruct", + "name": "Llama 3.1 70B Instruct", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": false, "structured_output": false, - "release_date": "2025-08-08", - "last_updated": "2025-08-08", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "temperature": true, + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 2048 + }, + "status": "beta", + "cost": { + "input": 0.72, + "output": 0.72 + } }, - "doubao-seed-2-0-mini-260215": { - "id": "doubao-seed-2-0-mini-260215", - "name": "Doubao Seed 2.0 Mini", + "seed-1-8-251228": { + "id": "seed-1-8-251228", + "name": "Seed 1.8 (251228)", + "family": "seed", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-12-18", + "last_updated": "2025-12-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 8192 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.05 + } + }, + "qwen3-235b-a22b-thinking-2507": { + "id": "qwen3-235b-a22b-thinking-2507", + "name": "Qwen3 235B A22B Thinking (2507)", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2026-02-14", - "last_updated": "2026-02-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.0493, "output": 0.4845 }, - "limit": { "context": 256000, "input": 256000, "output": 32000 } + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-07-08", + "last_updated": "2025-07-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 2.4 + } }, - "gemini-2.0-flash-thinking-exp-01-21": { - "id": "gemini-2.0-flash-thinking-exp-01-21", - "name": "Gemini 2.0 Flash Thinking 0121", + "seed-1-6-250915": { + "id": "seed-1-6-250915", + "name": "Seed 1.6 (250915)", + "family": "seed", "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-01-21", - "last_updated": "2025-01-21", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-09-15", + "last_updated": "2025-09-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 8192 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.05 + } + }, + "glm-4.5-x": { + "id": "glm-4.5-x", + "name": "GLM-4.5 X", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 1.003 }, - "limit": { "context": 1000000, "input": 1000000, "output": 8192 } + "limit": { + "context": 128000, + "output": 16384 + }, + "status": "beta", + "cost": { + "input": 2.2, + "output": 8.9, + "cache_read": 0.45 + } }, - "Llama-3.3-70B-Damascus-R1": { - "id": "Llama-3.3-70B-Damascus-R1", - "name": "Damascus R1", + "qwen3-30b-a3b-thinking-2507": { + "id": "qwen3-30b-a3b-thinking-2507", + "name": "Qwen3 30B A3B Thinking (2507)", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-07-08", + "last_updated": "2025-07-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } + }, + "grok-4-fast-reasoning": { + "id": "grok-4-fast-reasoning", + "name": "Grok 4 Fast Reasoning", + "family": "grok", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "doubao-1-5-thinking-pro-250415": { - "id": "doubao-1-5-thinking-pro-250415", - "name": "Doubao 1.5 Thinking Pro", + "deepseek-v3.1": { + "id": "deepseek-v3.1", + "name": "DeepSeek V3.1", + "family": "deepseek", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-08-21", + "last_updated": "2025-08-21", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.56, + "output": 1.68, + "cache_read": 0.11 + } + }, + "ministral-3b-2512": { + "id": "ministral-3b-2512", + "name": "Ministral 3B", + "family": "mistral", "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-04-17", - "last_updated": "2025-04-17", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.6, "output": 2.4 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "structured_output": true, + "temperature": true, + "release_date": "2025-12-02", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "asi1-mini": { - "id": "asi1-mini", - "name": "ASI1 Mini", + "qwen-plus-latest": { + "id": "qwen-plus-latest", + "name": "Qwen Plus Latest", + "family": "qwen", "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-03-25", - "last_updated": "2025-03-25", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-01-25", + "last_updated": "2025-01-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 1 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 0.9 + } }, - "doubao-seed-2-0-code-preview-260215": { - "id": "doubao-seed-2-0-code-preview-260215", - "name": "Doubao Seed 2.0 Code Preview", + "llama-3.1-nemotron-ultra-253b": { + "id": "llama-3.1-nemotron-ultra-253b", + "name": "Llama 3.1 Nemotron Ultra 253B", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2026-02-14", - "last_updated": "2026-02-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.782, "output": 3.893 }, - "limit": { "context": 256000, "input": 256000, "output": 128000 } + "structured_output": true, + "temperature": true, + "release_date": "2025-04-07", + "last_updated": "2025-04-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.6, + "output": 1.8 + } }, - "exa-research-pro": { - "id": "exa-research-pro", - "name": "Exa (Research Pro)", - "attachment": false, + "llama-4-maverick-17b-instruct": { + "id": "llama-4-maverick-17b-instruct", + "name": "Llama 4 Maverick 17B Instruct", + "family": "llama", + "attachment": true, "reasoning": false, "tool_call": false, "structured_output": false, - "release_date": "2025-06-04", - "last_updated": "2025-06-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 2.5 }, - "limit": { "context": 16384, "input": 16384, "output": 16384 } + "temperature": true, + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 2048 + }, + "cost": { + "input": 0.24, + "output": 0.97 + } }, - "GLM-4.5-Air-Derestricted-Iceblink-v2": { - "id": "GLM-4.5-Air-Derestricted-Iceblink-v2", - "name": "GLM 4.5 Air Derestricted Iceblink v2", + "grok-4-0709": { + "id": "grok-4-0709", + "name": "Grok 4 (0709)", + "family": "grok", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 158600, "input": 158600, "output": 65536 } + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "gemini-2.5-pro": { - "id": "gemini-2.5-pro", - "name": "Gemini 2.5 Pro", - "attachment": true, + "qwen3-30b-a3b-fp8": { + "id": "qwen3-30b-a3b-fp8", + "name": "Qwen3 30B A3B FP8", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-04-28", + "last_updated": "2025-04-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } + }, + "minimax-m2.1-lightning": { + "id": "minimax-m2.1-lightning", + "name": "MiniMax M2.1 Lightning", + "family": "minimax", + "attachment": false, "reasoning": true, "tool_call": false, "structured_output": false, - "release_date": "2025-06-05", - "last_updated": "2025-06-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 196608, + "output": 131072 + }, + "cost": { + "input": 0.12, + "output": 0.48 + } + }, + "qwen3-max-2026-01-23": { + "id": "qwen3-max-2026-01-23", + "name": "Qwen3 Max (2026-01-23)", + "family": "qwen", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2026-01-23", + "last_updated": "2026-01-23", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 1048756, "input": 1048756, "output": 65536 } + "limit": { + "context": 256000, + "output": 32800 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.6 + } }, - "Llama-3.3-70B-Nova": { - "id": "Llama-3.3-70B-Nova", - "name": "Llama 3.3 70B Nova", + "llama-3.2-3b-instruct": { + "id": "llama-3.2-3b-instruct", + "name": "Llama 3.2 3B Instruct", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "structured_output": true, + "temperature": true, + "release_date": "2024-09-18", + "last_updated": "2024-09-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 32000 + }, + "cost": { + "input": 0.03, + "output": 0.05 + } }, - "Gemma-3-27B-it": { - "id": "Gemma-3-27B-it", - "name": "Gemma 3 27B IT", + "qwen3-coder-next": { + "id": "qwen3-coder-next", + "name": "Qwen3 Coder Next", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-03-10", - "last_updated": "2025-03-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } - }, - "claude-opus-4-thinking:8192": { - "id": "claude-opus-4-thinking:8192", - "name": "Claude 4 Opus Thinking (8K)", - "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 14.994, "output": 75.004 }, - "limit": { "context": 200000, "input": 200000, "output": 32000 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.8, + "output": 4 + } }, - "claude-3-5-sonnet-20240620": { - "id": "claude-3-5-sonnet-20240620", - "name": "Claude 3.5 Sonnet Old", + "gpt-4o-search-preview": { + "id": "gpt-4o-search-preview", + "name": "GPT-4o Search Preview", + "family": "gpt", + "attachment": true, + "reasoning": false, + "tool_call": false, + "structured_output": false, + "temperature": true, + "release_date": "2024-10-01", + "last_updated": "2024-10-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10 + } + }, + "custom": { + "id": "custom", + "name": "Custom Model", + "family": "auto", "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, - "release_date": "2024-06-20", - "last_updated": "2024-06-20", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "release_date": "2024-01-01", + "last_updated": "2024-01-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.992, "output": 14.994 }, - "limit": { "context": 200000, "input": 200000, "output": 8192 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "claude-opus-4-1-20250805": { - "id": "claude-opus-4-1-20250805", - "name": "Claude 4.1 Opus", + "qwen3-vl-30b-a3b-instruct": { + "id": "qwen3-vl-30b-a3b-instruct", + "name": "Qwen3 VL 30B A3B Instruct", + "family": "qwen", "attachment": true, "reasoning": false, "tool_call": true, "structured_output": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 14.994, "output": 75.004 }, - "limit": { "context": 200000, "input": 200000, "output": 32000 } + "temperature": true, + "release_date": "2025-10-02", + "last_updated": "2025-10-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "gemini-2.5-flash": { - "id": "gemini-2.5-flash", - "name": "Gemini 2.5 Flash", + "deepseek-v3.2": { + "id": "deepseek-v3.2", + "name": "DeepSeek V3.2", + "family": "deepseek", "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-06-05", - "last_updated": "2025-06-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.5 }, - "limit": { "context": 1048756, "input": 1048756, "output": 65536 } + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 16384 + }, + "cost": { + "input": 0.28, + "output": 0.42, + "cache_read": 0.03 + } }, - "sonar-pro": { - "id": "sonar-pro", - "name": "Perplexity Pro", + "qwen3-235b-a22b-fp8": { + "id": "qwen3-235b-a22b-fp8", + "name": "Qwen3 235B A22B FP8", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.992, "output": 14.994 }, - "limit": { "context": 200000, "input": 200000, "output": 128000 } + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-04-28", + "last_updated": "2025-04-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.5, + "output": 2.5 + } }, - "sarvan-medium": { - "id": "sarvan-medium", - "name": "Sarvam Medium", + "gpt-oss-20b": { + "id": "gpt-oss-20b", + "name": "GPT OSS 20B", + "family": "gpt-oss", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 0.75 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "limit": { + "context": 131072, + "output": 32766 + }, + "cost": { + "input": 0.1, + "output": 0.5 + } }, - "hunyuan-t1-latest": { - "id": "hunyuan-t1-latest", - "name": "Hunyuan T1", + "kimi-k2": { + "id": "kimi-k2", + "name": "Kimi K2", + "family": "kimi", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-03-22", - "last_updated": "2025-03-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.17, "output": 0.66 }, - "limit": { "context": 256000, "input": 256000, "output": 16384 } + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-07-11", + "last_updated": "2025-07-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.5 + } }, - "Llama-3.3-70B-RAWMAW": { - "id": "Llama-3.3-70B-RAWMAW", - "name": "Llama 3.3 70B RAWMAW", + "llama-3-8b-instruct": { + "id": "llama-3-8b-instruct", + "name": "Llama 3 8B Instruct", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "structured_output": true, + "temperature": true, + "release_date": "2025-04-03", + "last_updated": "2025-04-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 0.04, + "output": 0.04 + } }, - "kimi-thinking-preview": { - "id": "kimi-thinking-preview", - "name": "Kimi Thinking Preview", + "qwen3-vl-235b-a22b-instruct": { + "id": "qwen3-vl-235b-a22b-instruct", + "name": "Qwen3 VL 235B A22B Instruct", + "family": "qwen", "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-05-07", - "last_updated": "2025-05-07", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 31.46, "output": 31.46 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-09-15", + "last_updated": "2025-09-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 2.4 + } }, - "claude-sonnet-4-thinking:32768": { - "id": "claude-sonnet-4-thinking:32768", - "name": "Claude 4 Sonnet Thinking (32K)", - "attachment": true, + "gpt-oss-120b": { + "id": "gpt-oss-120b", + "name": "GPT OSS 120B", + "family": "gpt-oss", + "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.992, "output": 14.994 }, - "limit": { "context": 1000000, "input": 1000000, "output": 64000 } + "limit": { + "context": 131072, + "output": 32766 + }, + "cost": { + "input": 0.15, + "output": 0.75 + } }, - "gemini-2.5-pro-preview-03-25": { - "id": "gemini-2.5-pro-preview-03-25", - "name": "Gemini 2.5 Pro Preview 0325", - "attachment": true, - "reasoning": true, + "qwen25-coder-7b": { + "id": "qwen25-coder-7b", + "name": "Qwen2.5 Coder 7B", + "family": "qwen", + "attachment": false, + "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-03-25", - "last_updated": "2025-03-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 1048756, "input": 1048756, "output": 65536 } + "structured_output": true, + "temperature": true, + "release_date": "2024-09-19", + "last_updated": "2024-09-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.05, + "output": 0.05 + } }, - "gemini-2.5-flash-preview-09-2025": { - "id": "gemini-2.5-flash-preview-09-2025", - "name": "Gemini 2.5 Flash Preview (09/2025)", - "attachment": true, - "reasoning": true, + "llama-3.1-8b-instruct": { + "id": "llama-3.1-8b-instruct", + "name": "Llama 3.1 8B Instruct", + "family": "llama", + "attachment": false, + "reasoning": false, "tool_call": true, "structured_output": true, - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 2.5 }, - "limit": { "context": 1048756, "input": 1048756, "output": 65536 } + "temperature": true, + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 2048 + }, + "status": "beta", + "cost": { + "input": 0.22, + "output": 0.22 + } }, - "Llama-3.3-70B-Electra-R1": { - "id": "Llama-3.3-70B-Electra-R1", - "name": "Llama 3.3 70B Electra R1", + "llama-3-70b-instruct": { + "id": "llama-3-70b-instruct", + "name": "Llama 3 70B Instruct", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "structured_output": true, + "temperature": true, + "release_date": "2024-04-18", + "last_updated": "2024-04-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 8000 + }, + "cost": { + "input": 0.51, + "output": 0.74 + } }, - "Llama-3.3-70B-ArliAI-RPMax-v2": { - "id": "Llama-3.3-70B-ArliAI-RPMax-v2", - "name": "Llama 3.3 70B ArliAI RPMax v2", + "deepseek-r1-0528": { + "id": "deepseek-r1-0528", + "name": "DeepSeek R1 (0528)", + "family": "deepseek", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": false, "structured_output": false, - "release_date": "2025-08-08", - "last_updated": "2025-08-08", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "temperature": true, + "release_date": "2025-05-28", + "last_updated": "2025-05-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 64000, + "output": 16384 + }, + "status": "beta", + "cost": { + "input": 0.8, + "output": 2.4 + } }, - "GLM-4.5-Air-Derestricted-Steam": { - "id": "GLM-4.5-Air-Derestricted-Steam", - "name": "GLM 4.5 Air Derestricted Steam", + "glm-4.5-airx": { + "id": "glm-4.5-airx", + "name": "GLM-4.5 AirX", + "family": "glm", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, + "tool_call": true, + "structured_output": true, + "temperature": true, "release_date": "2025-07-28", "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 220600, "input": 220600, "output": 65536 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.1, + "output": 4.5, + "cache_read": 0.22 + } }, - "doubao-seed-1-8-251215": { - "id": "doubao-seed-1-8-251215", - "name": "Doubao Seed 1.8", + "ministral-14b-2512": { + "id": "ministral-14b-2512", + "name": "Ministral 14B", + "family": "mistral", + "attachment": true, + "reasoning": false, + "tool_call": false, + "structured_output": true, + "temperature": true, + "release_date": "2025-12-02", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 8192 + }, + "cost": { + "input": 0.2, + "output": 0.2 + } + }, + "llama-3.2-11b-instruct": { + "id": "llama-3.2-11b-instruct", + "name": "Llama 3.2 11B Instruct", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": false, + "structured_output": true, + "temperature": true, + "release_date": "2024-09-25", + "last_updated": "2024-09-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.07, + "output": 0.33 + } + }, + "claude-3-opus": { + "id": "claude-3-opus", + "name": "Claude 3 Opus", + "family": "claude", + "attachment": true, + "reasoning": false, + "tool_call": true, "structured_output": false, - "release_date": "2025-12-15", - "last_updated": "2025-12-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "release_date": "2024-03-04", + "last_updated": "2024-03-04", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.612, "output": 6.12 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 } + "limit": { + "context": 200000, + "output": 4096 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5 + } }, - "Llama-3.3-70B-The-Omega-Directive-Unslop-v2.0": { - "id": "Llama-3.3-70B-The-Omega-Directive-Unslop-v2.0", - "name": "Llama 3.3 70B Omega Directive Unslop v2.0", + "minimax-m2.7": { + "id": "minimax-m2.7", + "name": "MiniMax-M2.7", + "family": "minimax", "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06, + "cache_write": 0.375 + } + }, + "grok-4-20-beta-0309-non-reasoning": { + "id": "grok-4-20-beta-0309-non-reasoning", + "name": "Grok 4.20 (Non-Reasoning)", + "family": "grok", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-09", + "last_updated": "2026-03-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.2, + "context_over_200k": { + "input": 4, + "output": 12, + "cache_read": 0.4 + }, + "tiers": [ + { + "input": 4, + "output": 12, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ] + } }, - "GLM-4.5-Air-Derestricted-Steam-ReExtract": { - "id": "GLM-4.5-Air-Derestricted-Steam-ReExtract", - "name": "GLM 4.5 Air Derestricted Steam ReExtract", + "qwen3-coder-plus": { + "id": "qwen3-coder-plus", + "name": "Qwen3 Coder Plus", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-12", - "last_updated": "2025-12-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1, + "output": 5 + } + }, + "claude-haiku-4-5": { + "id": "claude-haiku-4-5", + "name": "Claude Haiku 4.5 (latest)", + "family": "claude-haiku", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } + }, + "claude-opus-4-5-20251101": { + "id": "claude-opus-4-5-20251101", + "name": "Claude Opus 4.5", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-11-01", + "last_updated": "2025-11-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } + }, + "gemini-2.5-flash-lite-preview-09-2025": { + "id": "gemini-2.5-flash-lite-preview-09-2025", + "name": "Gemini 2.5 Flash Lite Preview 09-25", + "family": "gemini-flash-lite", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 131072, "input": 131072, "output": 65536 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.025 + } }, - "exa-research": { - "id": "exa-research", - "name": "Exa (Research)", + "kimi-k2.5": { + "id": "kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi-k2.5", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-06-04", - "last_updated": "2025-06-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 2.5 }, - "limit": { "context": 8192, "input": 8192, "output": 8192 } + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 3, + "cache_read": 0.1 + } }, - "azure-gpt-4o": { - "id": "azure-gpt-4o", - "name": "Azure gpt-4o", + "llama-3.3-70b-instruct": { + "id": "llama-3.3-70b-instruct", + "name": "Llama-3.3-70B-Instruct", + "family": "llama", "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "release_date": "2024-05-13", - "last_updated": "2024-05-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.499, "output": 9.996 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "temperature": true, + "knowledge": "2023-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "study_gpt-chatgpt-4o-latest": { - "id": "study_gpt-chatgpt-4o-latest", - "name": "Study Mode", + "mistral-large-2512": { + "id": "mistral-large-2512", + "name": "Mistral Large 3", + "family": "mistral-large", "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-05-13", - "last_updated": "2024-05-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 4.998, "output": 14.994 }, - "limit": { "context": 200000, "input": 200000, "output": 16384 } + "tool_call": true, + "temperature": true, + "knowledge": "2024-11", + "release_date": "2024-11-01", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.5, + "output": 1.5 + } }, - "Llama-3.3-70B-Aurora-Borealis": { - "id": "Llama-3.3-70B-Aurora-Borealis", - "name": "Llama 3.3 70B Aurora Borealis", + "glm-4.7": { + "id": "glm-4.7", + "name": "GLM-4.7", + "family": "glm", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.11, + "cache_write": 0 + } }, - "Baichuan4-Turbo": { - "id": "Baichuan4-Turbo", - "name": "Baichuan 4 Turbo", + "minimax-m2.7-highspeed": { + "id": "minimax-m2.7-highspeed", + "name": "MiniMax-M2.7-highspeed", + "family": "minimax", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-08-19", - "last_updated": "2025-08-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.42, "output": 2.42 }, - "limit": { "context": 128000, "input": 128000, "output": 32768 } + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 2.4, + "cache_read": 0.06, + "cache_write": 0.375 + } }, - "Baichuan4-Air": { - "id": "Baichuan4-Air", - "name": "Baichuan 4 Air", + "mimo-v2.5-pro": { + "id": "mimo-v2.5-pro", + "name": "MiMo-V2.5-Pro", + "family": "mimo", "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.2, + "context_over_200k": { + "input": 2, + "output": 6, + "cache_read": 0.4 + }, + "tiers": [ + { + "input": 2, + "output": 6, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 256000 + } + } + ] + } + }, + "gemma-3n-e4b-it": { + "id": "gemma-3n-e4b-it", + "name": "Gemma 3n 4B", + "family": "gemma", + "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-08-19", - "last_updated": "2025-08-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.157, "output": 0.157 }, - "limit": { "context": 32768, "input": 32768, "output": 32768 } + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-05-20", + "last_updated": "2025-05-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 2000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "KAT-Coder-Exp-72B-1010": { - "id": "KAT-Coder-Exp-72B-1010", - "name": "KAT Coder Exp 72B 1010", - "attachment": false, + "claude-3-5-sonnet-20241022": { + "id": "claude-3-5-sonnet-20241022", + "name": "Claude Sonnet 3.5 v2", + "family": "claude-sonnet", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-10-28", - "last_updated": "2025-10-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04-30", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.2 }, - "limit": { "context": 128000, "input": 128000, "output": 32768 } + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "gemini-2.0-flash-exp-image-generation": { - "id": "gemini-2.0-flash-exp-image-generation", - "name": "Gemini Text + Image", - "attachment": false, - "reasoning": false, - "tool_call": false, + "gpt-5.2-pro": { + "id": "gpt-5.2-pro", + "name": "GPT-5.2 Pro", + "family": "gpt-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, "structured_output": false, - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 32767, "input": 32767, "output": 8192 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 21, + "output": 168 + } }, - "Llama-3.3-70B-Dark-Ages-v0.1": { - "id": "Llama-3.3-70B-Dark-Ages-v0.1", - "name": "Llama 3.3 70B Dark Ages v0.1", + "qwq-plus": { + "id": "qwq-plus", + "name": "QwQ Plus", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2025-03-05", + "last_updated": "2025-03-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 2.4 + } }, - "claude-opus-4-1-thinking:8192": { - "id": "claude-opus-4-1-thinking:8192", - "name": "Claude 4.1 Opus Thinking (8K)", + "gemini-3.1-flash-lite-preview": { + "id": "gemini-3.1-flash-lite-preview", + "name": "Gemini 3.1 Flash Lite Preview", + "family": "gemini-flash-lite", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-03-03", + "last_updated": "2026-03-03", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 14.994, "output": 75.004 }, - "limit": { "context": 200000, "input": 200000, "output": 32000 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.25, + "output": 1.5, + "cache_read": 0.025, + "input_audio": 0.5 + } }, - "Magistral-Small-2506": { - "id": "Magistral-Small-2506", - "name": "Magistral Small 2506", + "qwen-vl-plus": { + "id": "qwen-vl-plus", + "name": "Qwen-VL Plus", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2024-01-25", + "last_updated": "2025-08-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 1.4 }, - "limit": { "context": 32768, "input": 32768, "output": 32768 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.21, + "output": 0.63 + } }, - "Llama-3.3-70B-MiraiFanfare": { - "id": "Llama-3.3-70B-MiraiFanfare", - "name": "Llama 3.3 70b Mirai Fanfare", + "glm-5": { + "id": "glm-5", + "name": "GLM-5", + "family": "glm", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-07-26", - "last_updated": "2025-07-26", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.493, "output": 0.493 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3.2, + "cache_read": 0.2, + "cache_write": 0 + } }, - "glm-4-flash": { - "id": "glm-4-flash", - "name": "GLM-4 Flash", + "devstral-2512": { + "id": "devstral-2512", + "name": "Devstral 2", + "family": "devstral", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-08-01", - "last_updated": "2024-08-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1003, "output": 0.1003 }, - "limit": { "context": 128000, "input": 128000, "output": 4096 } + "tool_call": true, + "temperature": true, + "knowledge": "2025-12", + "release_date": "2025-12-09", + "last_updated": "2025-12-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.4, + "output": 2 + } }, - "Llama-3.3-70B-Shakudo": { - "id": "Llama-3.3-70B-Shakudo", - "name": "Llama 3.3 70B Shakudo", + "qwen3-32b": { + "id": "qwen3-32b", + "name": "Qwen3 32B", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.7, + "output": 2.8, + "reasoning": 8.4 + } + }, + "claude-sonnet-4-6": { + "id": "claude-sonnet-4-6", + "name": "Claude Sonnet 4.6", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "gemini-2.0-pro-reasoner": { - "id": "gemini-2.0-pro-reasoner", - "name": "Gemini 2.0 Pro Reasoner", + "glm-4.7-flashx": { + "id": "glm-4.7-flashx", + "name": "GLM-4.7-FlashX", + "family": "glm-flash", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-02-05", - "last_updated": "2025-02-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-01-19", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 0.07, + "output": 0.4, + "cache_read": 0.01, + "cache_write": 0 + } + }, + "gemini-3.1-pro-preview": { + "id": "gemini-3.1-pro-preview", + "name": "Gemini 3.1 Pro Preview", + "family": "gemini-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-02-19", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.292, "output": 4.998 }, - "limit": { "context": 128000, "input": 128000, "output": 65536 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2, + "context_over_200k": { + "input": 4, + "output": 18, + "cache_read": 0.4 + }, + "tiers": [ + { + "input": 4, + "output": 18, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ] + } }, - "Llama-3.3-70B-Fallen-R1-v1": { - "id": "Llama-3.3-70B-Fallen-R1-v1", - "name": "Llama 3.3 70B Fallen R1 v1", + "qwen35-397b-a17b": { + "id": "qwen35-397b-a17b", + "name": "Qwen3.5 397B-A17B", + "family": "qwen", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2026-02-15", + "last_updated": "2026-02-15", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.6, + "output": 3.6 + } + }, + "qwen-max": { + "id": "qwen-max", + "name": "Qwen Max", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2024-04-03", + "last_updated": "2025-01-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 32768, + "output": 8192 + }, + "cost": { + "input": 1.6, + "output": 6.4 + } }, - "gemini-2.5-flash-lite-preview-06-17": { - "id": "gemini-2.5-flash-lite-preview-06-17", - "name": "Gemini 2.5 Flash Lite Preview", + "gpt-5.3-chat-latest": { + "id": "gpt-5.3-chat-latest", + "name": "GPT-5.3 Chat (latest)", + "family": "gpt", "attachment": true, - "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-08-31", + "release_date": "2026-03-03", + "last_updated": "2026-03-03", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 1048756, "input": 1048756, "output": 65536 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "doubao-seed-1-6-flash-250615": { - "id": "doubao-seed-1-6-flash-250615", - "name": "Doubao Seed 1.6 Flash", - "attachment": false, + "gemini-2.0-flash": { + "id": "gemini-2.0-flash", + "name": "Gemini 2.0 Flash", + "family": "gemini-flash", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-06-15", - "last_updated": "2025-06-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2024-06", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.0374, "output": 0.374 }, - "limit": { "context": 256000, "input": 256000, "output": 16384 } + "limit": { + "context": 1048576, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.025 + } }, - "claude-opus-4-5-20251101:thinking": { - "id": "claude-opus-4-5-20251101:thinking", - "name": "Claude 4.5 Opus Thinking", + "gemini-3-flash-preview": { + "id": "gemini-3-flash-preview", + "name": "Gemini 3 Flash Preview", + "family": "gemini-flash", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-11-01", - "last_updated": "2025-11-01", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 4.998, "output": 25.007 }, - "limit": { "context": 200000, "input": 200000, "output": 32000 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.05, + "input_audio": 1 + } }, - "Llama-3.3-70B-Strawberrylemonade-v1.2": { - "id": "Llama-3.3-70B-Strawberrylemonade-v1.2", - "name": "Llama 3.3 70B StrawberryLemonade v1.2", + "qwen-plus": { + "id": "qwen-plus", + "name": "Qwen Plus", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2024-01-25", + "last_updated": "2025-09-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 1000000, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 1.2, + "reasoning": 4 + } }, - "Llama-3.3-70B-Magnum-v4-SE": { - "id": "Llama-3.3-70B-Magnum-v4-SE", - "name": "Llama 3.3 70B Magnum v4 SE", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "gpt-5.5": { + "id": "gpt-5.5", + "name": "GPT-5.5", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-12-01", + "release_date": "2026-04-23", + "last_updated": "2026-04-23", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "experimental": { + "modes": { + "fast": { + "cost": { + "input": 12.5, + "output": 75, + "cache_read": 1.25 + }, + "provider": { + "body": { + "service_tier": "priority" + } + } + } + } + }, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5, + "context_over_200k": { + "input": 10, + "output": 45, + "cache_read": 1 + }, + "tiers": [ + { + "input": 10, + "output": 45, + "cache_read": 1, + "tier": { + "type": "context", + "size": 272000 + } + } + ] + } }, - "MiniMax-M2": { - "id": "MiniMax-M2", - "name": "MiniMax M2", - "attachment": false, + "qwen3.6-35b-a3b": { + "id": "qwen3.6-35b-a3b", + "name": "Qwen3.6 35B-A3B", + "family": "qwen", + "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-10-25", - "last_updated": "2025-10-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.17, "output": 1.53 }, - "limit": { "context": 200000, "input": 200000, "output": 131072 } + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2026-04-17", + "last_updated": "2026-04-17", + "modalities": { + "input": ["text", "image", "video", "audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.248, + "output": 1.485 + } }, - "doubao-seed-1-6-250615": { - "id": "doubao-seed-1-6-250615", - "name": "Doubao Seed 1.6", + "qwen-omni-turbo": { + "id": "qwen-omni-turbo", + "name": "Qwen-Omni Turbo", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-06-15", - "last_updated": "2025-06-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2025-01-19", + "last_updated": "2025-03-26", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text", "audio"] + }, "open_weights": false, - "cost": { "input": 0.204, "output": 0.51 }, - "limit": { "context": 256000, "input": 256000, "output": 16384 } + "limit": { + "context": 32768, + "output": 2048 + }, + "cost": { + "input": 0.07, + "output": 0.27, + "input_audio": 4.44, + "output_audio": 8.89 + } }, - "Llama-3.3-70B-StrawberryLemonade-v1.0": { - "id": "Llama-3.3-70B-StrawberryLemonade-v1.0", - "name": "Llama 3.3 70B StrawberryLemonade v1.0", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "claude-opus-4-7": { + "id": "claude-opus-4-7", + "name": "Claude Opus 4.7", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "Meta-Llama-3-1-8B-Instruct-FP8": { - "id": "Meta-Llama-3-1-8B-Instruct-FP8", - "name": "Llama 3.1 8B (decentralized)", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "gpt-5-mini": { + "id": "gpt-5-mini", + "name": "GPT-5 Mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.02, "output": 0.03 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.025 + } }, - "claude-opus-4-1-thinking:32000": { - "id": "claude-opus-4-1-thinking:32000", - "name": "Claude 4.1 Opus Thinking (32K)", + "gpt-5-nano": { + "id": "gpt-5-nano", + "name": "GPT-5 Nano", + "family": "gpt-nano", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 14.994, "output": 75.004 }, - "limit": { "context": 200000, "input": 200000, "output": 32000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.05, + "output": 0.4, + "cache_read": 0.005 + } }, - "GLM-4.5-Air-Derestricted-Iceblink-v2-ReExtract": { - "id": "GLM-4.5-Air-Derestricted-Iceblink-v2-ReExtract", - "name": "GLM 4.5 Air Derestricted Iceblink v2 ReExtract", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-12", - "last_updated": "2025-12-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "mimo-v2-omni": { + "id": "mimo-v2-omni", + "name": "MiMo-V2-Omni", + "family": "mimo", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 131072, "input": 131072, "output": 65536 } + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0.4, + "output": 2, + "cache_read": 0.08 + } }, - "gemini-3-pro-preview-thinking": { - "id": "gemini-3-pro-preview-thinking", - "name": "Gemini 3 Pro Thinking", + "gpt-5.3-codex": { + "id": "gpt-5.3-codex", + "name": "GPT-5.3 Codex", + "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 12 }, - "limit": { "context": 1048756, "input": 1048756, "output": 65536 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "gemini-2.5-flash-lite-preview-09-2025-thinking": { - "id": "gemini-2.5-flash-lite-preview-09-2025-thinking", - "name": "Gemini 2.5 Flash Lite Preview (09/2025) – Thinking", + "minimax-m2": { + "id": "minimax-m2", + "name": "MiniMax-M2", + "family": "minimax", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-10-27", + "last_updated": "2025-10-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 196608, + "output": 128000 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } + }, + "claude-sonnet-4-5-20250929": { + "id": "claude-sonnet-4-5-20250929", + "name": "Claude Sonnet 4.5", + "family": "claude-sonnet", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 1048756, "input": 1048756, "output": 65536 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "Llama-3.3-70B-Vulpecula-R1": { - "id": "Llama-3.3-70B-Vulpecula-R1", - "name": "Llama 3.3 70B Vulpecula R1", + "qwen-flash": { + "id": "qwen-flash", + "name": "Qwen Flash", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 1000000, + "output": 32768 + }, + "cost": { + "input": 0.05, + "output": 0.4 + } }, - "doubao-1.5-vision-pro-32k": { - "id": "doubao-1.5-vision-pro-32k", - "name": "Doubao 1.5 Vision Pro 32k", + "gpt-4-turbo": { + "id": "gpt-4-turbo", + "name": "GPT-4 Turbo", + "family": "gpt", "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, "structured_output": false, - "release_date": "2025-01-22", - "last_updated": "2025-01-22", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2023-11-06", + "last_updated": "2024-04-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.459, "output": 1.377 }, - "limit": { "context": 32000, "input": 32000, "output": 8192 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 10, + "output": 30 + } }, - "deepseek-reasoner-cheaper": { - "id": "deepseek-reasoner-cheaper", - "name": "Deepseek R1 Cheaper", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "gemini-2.5-pro": { + "id": "gemini-2.5-pro", + "name": "Gemini 2.5 Pro", + "family": "gemini-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 1.7 }, - "limit": { "context": 128000, "input": 128000, "output": 65536 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125, + "context_over_200k": { + "input": 2.5, + "output": 15, + "cache_read": 0.25 + }, + "tiers": [ + { + "input": 2.5, + "output": 15, + "cache_read": 0.25, + "tier": { + "type": "context", + "size": 200000 + } + } + ] + } }, - "ernie-x1.1-preview": { - "id": "ernie-x1.1-preview", - "name": "ERNIE X1.1", + "mimo-v2.5": { + "id": "mimo-v2.5", + "name": "MiMo-V2.5", + "family": "mimo", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 0.4, + "output": 2, + "cache_read": 0.08, + "context_over_200k": { + "input": 0.8, + "output": 4, + "cache_read": 0.16 + }, + "tiers": [ + { + "input": 0.8, + "output": 4, + "cache_read": 0.16, + "tier": { + "type": "context", + "size": 256000 + } + } + ] + } + }, + "grok-4-1-fast-non-reasoning": { + "id": "grok-4-1-fast-non-reasoning", + "name": "Grok 4.1 Fast (Non-Reasoning)", + "family": "grok", "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-09-10", - "last_updated": "2025-09-10", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2025-07", + "release_date": "2025-11-19", + "last_updated": "2025-11-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 64000, "input": 64000, "output": 8192 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "KAT-Coder-Air-V1": { - "id": "KAT-Coder-Air-V1", - "name": "KAT Coder Air V1", - "attachment": false, + "sonar-pro": { + "id": "sonar-pro", + "name": "Sonar Pro", + "family": "sonar-pro", + "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-10-28", - "last_updated": "2025-10-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-09-01", + "release_date": "2024-01-01", + "last_updated": "2025-09-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.2 }, - "limit": { "context": 128000, "input": 128000, "output": 32768 } + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15 + } }, - "mercury-coder-small": { - "id": "mercury-coder-small", - "name": "Mercury Coder Small", - "attachment": false, + "pixtral-large-latest": { + "id": "pixtral-large-latest", + "name": "Pixtral Large (latest)", + "family": "pixtral", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-02-26", - "last_updated": "2025-02-26", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-11", + "release_date": "2024-11-01", + "last_updated": "2024-11-04", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 2, + "output": 6 + } + }, + "gpt-5.2": { + "id": "gpt-5.2", + "name": "GPT-5.2", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 1 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "doubao-1.5-pro-256k": { - "id": "doubao-1.5-pro-256k", - "name": "Doubao 1.5 Pro 256k", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-03-12", - "last_updated": "2025-03-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "grok-4-20-beta-0309-reasoning": { + "id": "grok-4-20-beta-0309-reasoning", + "name": "Grok 4.20 (Reasoning)", + "family": "grok", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-09", + "last_updated": "2026-03-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.799, "output": 1.445 }, - "limit": { "context": 256000, "input": 256000, "output": 16384 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.2, + "context_over_200k": { + "input": 4, + "output": 12, + "cache_read": 0.4 + }, + "tiers": [ + { + "input": 4, + "output": 12, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ] + } }, - "glm-4-plus": { - "id": "glm-4-plus", - "name": "GLM-4 Plus", - "attachment": false, + "gpt-4o-mini": { + "id": "gpt-4o-mini", + "name": "GPT-4o mini", + "family": "gpt-mini", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-08-01", - "last_updated": "2024-08-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2023-09", + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 7.497, "output": 7.497 }, - "limit": { "context": 128000, "input": 128000, "output": 4096 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.08 + } }, - "Baichuan-M2": { - "id": "Baichuan-M2", - "name": "Baichuan M2 32B Medical", + "qwen3.6-plus": { + "id": "qwen3.6-plus", + "name": "Qwen3.6 Plus", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-08-19", - "last_updated": "2025-08-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.05, + "cache_write": 0.625, + "context_over_200k": { + "input": 2, + "output": 6, + "cache_read": 0.2, + "cache_write": 2.5 + }, + "tiers": [ + { + "input": 2, + "output": 6, + "cache_read": 0.2, + "cache_write": 2.5, + "tier": { + "type": "context", + "size": 256000 + } + } + ] + } + }, + "gpt-5.4-mini": { + "id": "gpt-5.4-mini", + "name": "GPT-5.4 mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15.73, "output": 15.73 }, - "limit": { "context": 32768, "input": 32768, "output": 32768 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.75, + "output": 4.5, + "cache_read": 0.075 + } }, - "auto-model-standard": { - "id": "auto-model-standard", - "name": "Auto model (Standard)", + "qwen3-max": { + "id": "qwen3-max", + "name": "Qwen3 Max", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-06-01", - "last_updated": "2024-06-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-09-23", + "last_updated": "2025-09-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 9.996, "output": 19.992 }, - "limit": { "context": 1000000, "input": 1000000, "output": 1000000 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 1.2, + "output": 6 + } }, - "Gemma-3-27B-it-Abliterated": { - "id": "Gemma-3-27B-it-Abliterated", - "name": "Gemma 3 27B IT Abliterated", + "minimax-m2.1": { + "id": "minimax-m2.1", + "name": "MiniMax-M2.1", + "family": "minimax", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-07-03", - "last_updated": "2025-07-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.42, "output": 0.42 }, - "limit": { "context": 32768, "input": 32768, "output": 96000 } + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "learnlm-1.5-pro-experimental": { - "id": "learnlm-1.5-pro-experimental", - "name": "Gemini LearnLM Experimental", + "glm-5.1": { + "id": "glm-5.1", + "name": "GLM-5.1", + "family": "glm", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-05-14", - "last_updated": "2024-05-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "release_date": "2026-03-27", + "last_updated": "2026-03-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3.502, "output": 10.506 }, - "limit": { "context": 32767, "input": 32767, "output": 8192 } + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 6, + "output": 24, + "cache_read": 1.3, + "cache_write": 0 + } }, - "claude-opus-4-thinking:1024": { - "id": "claude-opus-4-thinking:1024", - "name": "Claude 4 Opus Thinking (1K)", + "o4-mini": { + "id": "o4-mini", + "name": "o4-mini", + "family": "o-mini", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 14.994, "output": 75.004 }, - "limit": { "context": 200000, "input": 200000, "output": 32000 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.28 + } }, - "gemini-2.5-flash-preview-04-17:thinking": { - "id": "gemini-2.5-flash-preview-04-17:thinking", - "name": "Gemini 2.5 Flash Preview Thinking", + "gpt-5.4-nano": { + "id": "gpt-5.4-nano", + "name": "GPT-5.4 nano", + "family": "gpt-nano", "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-17", - "last_updated": "2025-04-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 3.5 }, - "limit": { "context": 1048756, "input": 1048756, "output": 65536 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.2, + "output": 1.25, + "cache_read": 0.02 + } }, - "v0-1.5-md": { - "id": "v0-1.5-md", - "name": "v0 1.5 MD", + "glm-4.5": { + "id": "glm-4.5", + "name": "GLM-4.5", + "family": "glm", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-07-04", - "last_updated": "2025-07-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 200000, "input": 200000, "output": 64000 } + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 98304 + }, + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.11, + "cache_write": 0 + } }, - "Llama-3.3-70B-Electranova-v1.0": { - "id": "Llama-3.3-70B-Electranova-v1.0", - "name": "Llama 3.3 70B Electranova v1.0", - "attachment": false, + "mistral-large-latest": { + "id": "mistral-large-latest", + "name": "Mistral Large (latest)", + "family": "mistral-large", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "tool_call": true, + "temperature": true, + "knowledge": "2024-11", + "release_date": "2024-11-01", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.5, + "output": 1.5 + } }, - "Llama-3.3+(3.1v3.3)-70B-Hanami-x1": { - "id": "Llama-3.3+(3.1v3.3)-70B-Hanami-x1", - "name": "Llama 3.3+ 70B Hanami x1", + "mistral-small-2506": { + "id": "mistral-small-2506", + "name": "Mistral Small 3.2", + "family": "mistral-small", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "tool_call": true, + "temperature": true, + "knowledge": "2025-03", + "release_date": "2025-06-20", + "last_updated": "2025-06-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } }, - "phi-4-multimodal-instruct": { - "id": "phi-4-multimodal-instruct", - "name": "Phi 4 Multimodal", - "attachment": false, + "gemma-3-12b-it": { + "id": "gemma-3-12b-it", + "name": "Gemma 3 12B", + "family": "gemma", + "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-07-26", - "last_updated": "2025-07-26", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.07, "output": 0.11 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "structured_output": true, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-03-13", + "last_updated": "2025-03-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "claude-haiku-4-5-20251001": { - "id": "claude-haiku-4-5-20251001", - "name": "Claude Haiku 4.5", + "gpt-5.2-codex": { + "id": "gpt-5.2-codex", + "name": "GPT-5.2 Codex", + "family": "gpt-codex", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1, "output": 5 }, - "limit": { "context": 200000, "input": 200000, "output": 64000 } - }, - "ernie-4.5-8k-preview": { - "id": "ernie-4.5-8k-preview", - "name": "Ernie 4.5 8k Preview", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-03-25", - "last_updated": "2025-03-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.66, "output": 2.6 }, - "limit": { "context": 8000, "input": 8000, "output": 16384 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "claude-3-7-sonnet-thinking:32768": { - "id": "claude-3-7-sonnet-thinking:32768", - "name": "Claude 3.7 Sonnet Thinking (32K)", + "gemini-2.5-flash": { + "id": "gemini-2.5-flash", + "name": "Gemini 2.5 Flash", + "family": "gemini-flash", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-07-15", - "last_updated": "2025-07-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.992, "output": 14.994 }, - "limit": { "context": 200000, "input": 200000, "output": 64000 } - }, - "qwen3-max-2026-01-23": { - "id": "qwen3-max-2026-01-23", - "name": "Qwen3 Max 2026-01-23", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2026-01-26", - "last_updated": "2026-01-26", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.2002, "output": 6.001 }, - "limit": { "context": 256000, "input": 256000, "output": 32768 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.03, + "input_audio": 1 + } }, - "claude-3-7-sonnet-thinking:8192": { - "id": "claude-3-7-sonnet-thinking:8192", - "name": "Claude 3.7 Sonnet Thinking (8K)", + "gpt-5.2-chat-latest": { + "id": "gpt-5.2-chat-latest", + "name": "GPT-5.2 Chat", + "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-02-24", - "last_updated": "2025-02-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.992, "output": 14.994 }, - "limit": { "context": 200000, "input": 200000, "output": 64000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "Llama-3.3-70B-Incandescent-Malevolence": { - "id": "Llama-3.3-70B-Incandescent-Malevolence", - "name": "Llama 3.3 70B Incandescent Malevolence", - "attachment": false, + "gemma-3n-e2b-it": { + "id": "gemma-3n-e2b-it", + "name": "Gemma 3n 2B", + "family": "gemma", + "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 2000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "step-2-mini": { - "id": "step-2-mini", - "name": "Step-2 Mini", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-07-05", - "last_updated": "2024-07-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "gpt-5.1-codex-mini": { + "id": "gpt-5.1-codex-mini", + "name": "GPT-5.1 Codex mini", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2006, "output": 0.408 }, - "limit": { "context": 8000, "input": 8000, "output": 4096 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.025 + } }, - "auto-model-basic": { - "id": "auto-model-basic", - "name": "Auto model (Basic)", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-06-01", - "last_updated": "2024-06-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "grok-4-fast": { + "id": "grok-4-fast", + "name": "Grok 4 Fast", + "family": "grok", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-07", + "release_date": "2025-09-19", + "last_updated": "2025-09-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 9.996, "output": 19.992 }, - "limit": { "context": 1000000, "input": 1000000, "output": 1000000 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "claude-sonnet-4-thinking": { - "id": "claude-sonnet-4-thinking", - "name": "Claude 4 Sonnet Thinking", + "gemini-3.1-flash-lite": { + "id": "gemini-3.1-flash-lite", + "name": "Gemini 3.1 Flash Lite", + "family": "gemini-flash-lite", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-02-24", - "last_updated": "2025-02-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-05-07", + "last_updated": "2026-05-07", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.992, "output": 14.994 }, - "limit": { "context": 1000000, "input": 1000000, "output": 64000 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.25, + "output": 1.5, + "cache_read": 0.025, + "input_audio": 0.5 + } }, - "Llama-3.3-70B-GeneticLemonade-Unleashed-v3": { - "id": "Llama-3.3-70B-GeneticLemonade-Unleashed-v3", - "name": "Llama 3.3 70B GeneticLemonade Unleashed v3", + "qwen3-next-80b-a3b-thinking": { + "id": "qwen3-next-80b-a3b-thinking", + "name": "Qwen3-Next 80B-A3B (Thinking)", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-09", + "last_updated": "2025-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.5, + "output": 6 + } }, - "step-r1-v-mini": { - "id": "step-r1-v-mini", - "name": "Step R1 V Mini", + "grok-code-fast-1": { + "id": "grok-code-fast-1", + "name": "Grok Code Fast 1", + "family": "grok", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-08", - "last_updated": "2025-04-08", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2023-10", + "release_date": "2025-08-28", + "last_updated": "2025-08-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 11 }, - "limit": { "context": 128000, "input": 128000, "output": 65536 } + "limit": { + "context": 256000, + "output": 10000 + }, + "cost": { + "input": 0.2, + "output": 1.5, + "cache_read": 0.02 + } }, - "glm-4-plus-0111": { - "id": "glm-4-plus-0111", - "name": "GLM 4 Plus 0111", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "gpt-5.1": { + "id": "gpt-5.1", + "name": "GPT-5.1", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 9.996, "output": 9.996 }, - "limit": { "context": 128000, "input": 128000, "output": 4096 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.13 + } }, - "z-image-turbo": { - "id": "z-image-turbo", - "name": "Z Image Turbo", + "gemma-3-4b-it": { + "id": "gemma-3-4b-it", + "name": "Gemma 3 4B", + "family": "gemma", "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": false, "temperature": true, - "release_date": "2025-11-27", - "last_updated": "2025-11-27", - "modalities": { "input": ["text"], "output": ["image"] }, - "open_weights": false, - "limit": { "context": 0, "output": 0 } - }, - "Llama-3.3-70B-Sapphira-0.2": { - "id": "Llama-3.3-70B-Sapphira-0.2", - "name": "Llama 3.3 70B Sapphira 0.2", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "knowledge": "2024-10", + "release_date": "2025-03-13", + "last_updated": "2025-03-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "deepseek-math-v2": { - "id": "deepseek-math-v2", - "name": "DeepSeek Math V2", + "kimi-k2-thinking-turbo": { + "id": "kimi-k2-thinking-turbo", + "name": "Kimi K2 Thinking Turbo", + "family": "kimi-thinking", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-03", - "last_updated": "2025-12-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.6, "output": 2.2 }, - "limit": { "context": 128000, "input": 128000, "output": 65536 } + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-08", + "release_date": "2025-11-06", + "last_updated": "2025-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 1.15, + "output": 8, + "cache_read": 0.15 + } }, - "azure-gpt-4o-mini": { - "id": "azure-gpt-4o-mini", - "name": "Azure gpt-4o-mini", + "o1": { + "id": "o1", + "name": "o1", + "family": "o", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1496, "output": 0.595 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } - }, - "Mistral-Nemo-12B-Instruct-2407": { - "id": "Mistral-Nemo-12B-Instruct-2407", - "name": "Mistral Nemo 12B Instruct 2407", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, + "temperature": false, + "knowledge": "2023-09", + "release_date": "2024-12-05", + "last_updated": "2024-12-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.01, "output": 0.01 }, - "limit": { "context": 16384, "input": 16384, "output": 16384 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 15, + "output": 60, + "cache_read": 7.5 + } }, - "jamba-mini-1.6": { - "id": "jamba-mini-1.6", - "name": "Jamba Mini 1.6", + "glm-4.5-air": { + "id": "glm-4.5-air", + "name": "GLM-4.5-Air", + "family": "glm-air", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-03-01", - "last_updated": "2025-03-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1989, "output": 0.408 }, - "limit": { "context": 256000, "input": 256000, "output": 4096 } + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 98304 + }, + "cost": { + "input": 0.2, + "output": 1.1, + "cache_read": 0.03, + "cache_write": 0 + } }, - "qwen3-vl-235b-a22b-thinking": { - "id": "qwen3-vl-235b-a22b-thinking", - "name": "Qwen3 VL 235B A22B Thinking", + "gpt-5.4-pro": { + "id": "gpt-5.4-pro", + "name": "GPT-5.4 Pro", + "family": "gpt-pro", "attachment": true, "reasoning": true, - "tool_call": false, + "tool_call": true, "structured_output": false, - "release_date": "2025-08-26", - "last_updated": "2025-08-26", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 6 }, - "limit": { "context": 32768, "input": 32768, "output": 32768 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 30, + "output": 180 + } }, - "Llama-3.3-70B-ArliAI-RPMax-v1.4": { - "id": "Llama-3.3-70B-ArliAI-RPMax-v1.4", - "name": "Llama 3.3 70B RPMax v1.4", + "gpt-3.5-turbo": { + "id": "gpt-3.5-turbo", + "name": "GPT-3.5-turbo", + "family": "gpt", "attachment": false, "reasoning": false, "tool_call": false, "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2021-09-01", + "release_date": "2023-03-01", + "last_updated": "2023-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 16385, + "output": 4096 + }, + "cost": { + "input": 0.5, + "output": 1.5, + "cache_read": 1.25 + } }, - "Llama-3.3-70B-Anthrobomination": { - "id": "Llama-3.3-70B-Anthrobomination", - "name": "Llama 3.3 70B Anthrobomination", + "o3-mini": { + "id": "o3-mini", + "name": "o3-mini", + "family": "o-mini", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2024-12-20", + "last_updated": "2025-01-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.55 + } }, - "glm-4-air-0111": { - "id": "glm-4-air-0111", - "name": "GLM 4 Air 0111", + "qwen-vl-max": { + "id": "qwen-vl-max", + "name": "Qwen-VL Max", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-01-11", - "last_updated": "2025-01-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2024-04-08", + "last_updated": "2025-08-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1394, "output": 0.1394 }, - "limit": { "context": 128000, "input": 128000, "output": 4096 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 3.2 + } }, - "jamba-mini": { - "id": "jamba-mini", - "name": "Jamba Mini", + "sonar": { + "id": "sonar", + "name": "Sonar", + "family": "sonar", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1989, "output": 0.408 }, - "limit": { "context": 256000, "input": 256000, "output": 4096 } - }, - "ernie-5.0-thinking-preview": { - "id": "ernie-5.0-thinking-preview", - "name": "Ernie 5.0 Thinking Preview", - "attachment": true, - "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-09-01", + "release_date": "2024-01-01", + "last_updated": "2025-09-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 2 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 1, + "output": 1 + } }, - "Gemma-3-27B-Glitter": { - "id": "Gemma-3-27B-Glitter", - "name": "Gemma 3 27B Glitter", + "qwen3-coder-flash": { + "id": "qwen3-coder-flash", + "name": "Qwen3 Coder Flash", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-03-10", - "last_updated": "2025-03-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 1000000, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 1.5 + } }, - "hidream": { - "id": "hidream", - "name": "Hidream", + "grok-4-3": { + "id": "grok-4-3", + "name": "Grok 4.3", + "family": "grok", "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2024-01-01", - "last_updated": "2024-01-01", - "modalities": { "input": ["text"], "output": ["image"] }, + "release_date": "2026-05-01", + "last_updated": "2026-05-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "limit": { "context": 0, "output": 0 } + "limit": { + "context": 1000000, + "output": 30000 + }, + "cost": { + "input": 1.25, + "output": 2.5, + "cache_read": 0.2, + "context_over_200k": { + "input": 2.5, + "output": 5, + "cache_read": 0.4 + }, + "tiers": [ + { + "input": 2.5, + "output": 5, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ] + } }, - "glm-4.1v-thinking-flashx": { - "id": "glm-4.1v-thinking-flashx", - "name": "GLM 4.1V Thinking FlashX", + "glm-4.5v": { + "id": "glm-4.5v", + "name": "GLM-4.5V", + "family": "glm", "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 0.3 }, - "limit": { "context": 64000, "input": 64000, "output": 8192 } - }, - "phi-4-mini-instruct": { - "id": "phi-4-mini-instruct", - "name": "Phi 4 Mini", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-07-26", - "last_updated": "2025-07-26", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.17, "output": 0.68 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } - }, - "Llama-3.3-70B-Sapphira-0.1": { - "id": "Llama-3.3-70B-Sapphira-0.1", - "name": "Llama 3.3 70B Sapphira 0.1", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-08-11", + "last_updated": "2025-08-11", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 64000, + "output": 16384 + }, + "cost": { + "input": 0.6, + "output": 1.8 + } }, - "yi-medium-200k": { - "id": "yi-medium-200k", - "name": "Yi Medium 200k", + "deepseek-v4-flash": { + "id": "deepseek-v4-flash", + "name": "DeepSeek V4 Flash", + "family": "deepseek-flash", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-03-01", - "last_updated": "2024-03-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.499, "output": 2.499 }, - "limit": { "context": 200000, "input": 200000, "output": 4096 } + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 0.14, + "output": 0.28, + "cache_read": 0.028 + } }, - "jamba-mini-1.7": { - "id": "jamba-mini-1.7", - "name": "Jamba Mini 1.7", + "grok-4": { + "id": "grok-4", + "name": "Grok 4", + "family": "grok", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-07", "release_date": "2025-07-09", "last_updated": "2025-07-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1989, "output": 0.408 }, - "limit": { "context": 256000, "input": 256000, "output": 4096 } - }, - "gemini-3-pro-image-preview": { - "id": "gemini-3-pro-image-preview", - "name": "Gemini 3 Pro Image", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 12 }, - "limit": { "context": 1048756, "input": 1048756, "output": 65536 } + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "reasoning": 15, + "cache_read": 0.75 + } }, - "fastgpt": { - "id": "fastgpt", - "name": "Web Answer", + "qwen3-next-80b-a3b-instruct": { + "id": "qwen3-next-80b-a3b-instruct", + "name": "Qwen3-Next 80B-A3B Instruct", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2023-08-01", - "last_updated": "2024-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 7.5, "output": 7.5 }, - "limit": { "context": 32768, "input": 32768, "output": 32768 } + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-09", + "last_updated": "2025-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.5, + "output": 2 + } }, - "GLM-4.5-Air-Derestricted-Iceblink": { - "id": "GLM-4.5-Air-Derestricted-Iceblink", - "name": "GLM 4.5 Air Derestricted Iceblink", - "attachment": false, + "gpt-4": { + "id": "gpt-4", + "name": "GPT-4", + "family": "gpt", + "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, "structured_output": false, - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2023-11", + "release_date": "2023-11-06", + "last_updated": "2024-04-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 131072, "input": 131072, "output": 98304 } + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 30, + "output": 60 + } }, - "Llama-3.3-70B-Predatorial-Extasy": { - "id": "Llama-3.3-70B-Predatorial-Extasy", - "name": "Llama 3.3 70B Predatorial Extasy", + "glm-4.6": { + "id": "glm-4.6", + "name": "GLM-4.6", + "family": "glm", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.11, + "cache_write": 0 + } }, - "claude-3-7-sonnet-thinking": { - "id": "claude-3-7-sonnet-thinking", - "name": "Claude 3.7 Sonnet Thinking", + "kimi-k2.6": { + "id": "kimi-k2.6", + "name": "Kimi K2.6", + "family": "kimi-k2.6", "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, - "release_date": "2025-02-24", - "last_updated": "2025-02-24", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.992, "output": 14.994 }, - "limit": { "context": 200000, "input": 200000, "output": 16000 } - }, - "Llama-3.3-70B-Magnum-v4-SE-Cirrus-x1-SLERP": { - "id": "Llama-3.3-70B-Magnum-v4-SE-Cirrus-x1-SLERP", - "name": "Llama 3.3 70B Magnum v4 SE Cirrus x1 SLERP", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-07-26", - "last_updated": "2025-07-26", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.95, + "output": 4, + "cache_read": 0.16 + } }, - "claude-3-5-sonnet-20241022": { - "id": "claude-3-5-sonnet-20241022", - "name": "Claude 3.5 Sonnet", + "glm-4.6v": { + "id": "glm-4.6v", + "name": "GLM-4.6V", + "family": "glm", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-08-26", - "last_updated": "2025-08-26", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.992, "output": 14.994 }, - "limit": { "context": 200000, "input": 200000, "output": 8192 } + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-12-08", + "last_updated": "2025-12-08", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.3, + "output": 0.9 + } }, - "gemini-exp-1206": { - "id": "gemini-exp-1206", - "name": "Gemini 2.0 Pro 1206", + "claude-opus-4-1-20250805": { + "id": "claude-opus-4-1-20250805", + "name": "Claude Opus 4.1", + "family": "claude-opus", "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.258, "output": 4.998 }, - "limit": { "context": 2097152, "input": 2097152, "output": 8192 } - }, - "doubao-seed-2-0-lite-260215": { - "id": "doubao-seed-2-0-lite-260215", - "name": "Doubao Seed 2.0 Lite", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2026-02-14", - "last_updated": "2026-02-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1462, "output": 0.8738 }, - "limit": { "context": 256000, "input": 256000, "output": 32000 } - }, - "jamba-large": { - "id": "jamba-large", - "name": "Jamba Large", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.989, "output": 7.99 }, - "limit": { "context": 256000, "input": 256000, "output": 4096 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "hunyuan-turbos-20250226": { - "id": "hunyuan-turbos-20250226", - "name": "Hunyuan Turbo S", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-02-27", - "last_updated": "2025-02-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "gpt-5.4": { + "id": "gpt-5.4", + "name": "GPT-5.4", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.187, "output": 0.374 }, - "limit": { "context": 24000, "input": 24000, "output": 8192 } - }, - "ernie-4.5-turbo-vl-32k": { - "id": "ernie-4.5-turbo-vl-32k", - "name": "Ernie 4.5 Turbo VL 32k", + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 2.5, + "output": 15, + "cache_read": 0.25 + } + }, + "claude-haiku-4-5-20251001": { + "id": "claude-haiku-4-5-20251001", + "name": "Claude Haiku 4.5", + "family": "claude-haiku", "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-05-08", - "last_updated": "2025-05-08", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.495, "output": 1.43 }, - "limit": { "context": 32000, "input": 32000, "output": 16384 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } }, - "grok-3-mini-beta": { - "id": "grok-3-mini-beta", - "name": "Grok 3 Mini Beta", + "glm-4.5-flash": { + "id": "glm-4.5-flash", + "name": "GLM-4.5-Flash", + "family": "glm-flash", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 0.5 }, - "limit": { "context": 131072, "input": 131072, "output": 131072 } + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 98304 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "qwen3-30b-a3b-instruct-2507": { - "id": "qwen3-30b-a3b-instruct-2507", - "name": "Qwen3 30B A3B Instruct 2507", + "qwen3-vl-plus": { + "id": "qwen3-vl-plus", + "name": "Qwen3-VL Plus", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-02-20", - "last_updated": "2025-02-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-09-23", + "last_updated": "2025-09-23", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5 }, - "limit": { "context": 256000, "input": 256000, "output": 32768 } + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0.2, + "output": 1.6, + "reasoning": 4.8 + } }, - "Llama-3.3-70B-Argunaut-1-SFT": { - "id": "Llama-3.3-70B-Argunaut-1-SFT", - "name": "Llama 3.3 70B Argunaut 1 SFT", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "grok-4-1-fast": { + "id": "grok-4-1-fast", + "name": "Grok 4.1 Fast", + "family": "grok", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-07", + "release_date": "2025-11-19", + "last_updated": "2025-11-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "abacusai/Dracarys-72B-Instruct": { - "id": "abacusai/Dracarys-72B-Instruct", - "name": "Llama 3.1 70B Dracarys 2", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-08-02", - "last_updated": "2025-08-02", - "modalities": { "input": ["text"], "output": ["text"] }, + "claude-sonnet-4-20250514": { + "id": "claude-sonnet-4-20250514", + "name": "Claude Sonnet 4", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.49299999999999994, "output": 0.49299999999999994 }, - "limit": { "context": 16384, "input": 16384, "output": 8192 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "THUDM/GLM-Z1-32B-0414": { - "id": "THUDM/GLM-Z1-32B-0414", - "name": "GLM Z1 32B 0414", - "family": "glm-z", + "qwen3-coder-480b-a35b-instruct": { + "id": "qwen3-coder-480b-a35b-instruct", + "name": "Qwen3-Coder 480B-A35B Instruct", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-15", - "last_updated": "2025-04-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 1.5, + "output": 7.5 + } + }, + "claude-opus-4-6": { + "id": "claude-opus-4-6", + "name": "Claude Opus 4.6", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 128000, "input": 128000, "output": 65536 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "THUDM/GLM-Z1-9B-0414": { - "id": "THUDM/GLM-Z1-9B-0414", - "name": "GLM Z1 9B 0414", - "family": "glm-z", + "deepseek-v4-pro": { + "id": "deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 384000 + }, + "cost": { + "input": 1.74, + "output": 3.48, + "cache_read": 0.145 + } + }, + "gpt-4.1-nano": { + "id": "gpt-4.1-nano", + "name": "GPT-4.1 nano", + "family": "gpt-nano", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2024-04", "release_date": "2025-04-14", "last_updated": "2025-04-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 32000, "input": 32000, "output": 8000 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.03 + } }, - "THUDM/GLM-4-32B-0414": { - "id": "THUDM/GLM-4-32B-0414", - "name": "GLM 4 32B 0414", - "family": "glm", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "claude-3-7-sonnet-20250219": { + "id": "claude-3-7-sonnet-20250219", + "name": "Claude Sonnet 3.7", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-10-31", + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 128000, "input": 128000, "output": 65536 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "THUDM/GLM-Z1-Rumination-32B-0414": { - "id": "THUDM/GLM-Z1-Rumination-32B-0414", - "name": "GLM Z1 Rumination 32B 0414", - "family": "glm-z", + "qwen3-coder-30b-a3b-instruct": { + "id": "qwen3-coder-30b-a3b-instruct", + "name": "Qwen3-Coder 30B-A3B Instruct", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-15", - "last_updated": "2025-04-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 32000, "input": 32000, "output": 65536 } + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.45, + "output": 2.25 + } }, - "THUDM/GLM-4-9B-0414": { - "id": "THUDM/GLM-4-9B-0414", - "name": "GLM 4 9B 0414", - "family": "glm", + "minimax-m2.5": { + "id": "minimax-m2.5", + "name": "MiniMax-M2.5", + "family": "minimax", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 32000, "input": 32000, "output": 8000 } + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03, + "cache_write": 0.375 + } }, - "unsloth/gemma-3-1b-it": { - "id": "unsloth/gemma-3-1b-it", - "name": "Gemma 3 1B IT", - "family": "unsloth", + "mimo-v2-pro": { + "id": "mimo-v2-pro", + "name": "MiMo-V2-Pro", + "family": "mimo", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-03-10", - "last_updated": "2025-03-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1003, "output": 0.1003 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 } + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.2, + "context_over_200k": { + "input": 2, + "output": 6, + "cache_read": 0.4 + }, + "tiers": [ + { + "input": 2, + "output": 6, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 256000 + } + } + ] + } }, - "unsloth/gemma-3-27b-it": { - "id": "unsloth/gemma-3-27b-it", - "name": "Gemma 3 27B IT", - "family": "unsloth", + "o3": { + "id": "o3", + "name": "o3", + "family": "o", "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-03-10", - "last_updated": "2025-03-10", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2992, "output": 0.2992 }, - "limit": { "context": 128000, "input": 128000, "output": 96000 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } }, - "unsloth/gemma-3-4b-it": { - "id": "unsloth/gemma-3-4b-it", - "name": "Gemma 3 4B IT", - "family": "unsloth", + "gpt-5-pro": { + "id": "gpt-5-pro", + "name": "GPT-5 Pro", + "family": "gpt-pro", "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-03-10", - "last_updated": "2025-03-10", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-10-06", + "last_updated": "2025-10-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2006, "output": 0.2006 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 } + "limit": { + "context": 400000, + "input": 272000, + "output": 272000 + }, + "cost": { + "input": 15, + "output": 120 + } }, - "unsloth/gemma-3-12b-it": { - "id": "unsloth/gemma-3-12b-it", - "name": "Gemma 3 12B IT", - "family": "unsloth", + "gpt-4o": { + "id": "gpt-4o", + "name": "GPT-4o", + "family": "gpt", "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-03-10", - "last_updated": "2025-03-10", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2023-09", + "release_date": "2024-05-13", + "last_updated": "2024-08-06", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.272, "output": 0.272 }, - "limit": { "context": 128000, "input": 128000, "output": 131072 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10, + "cache_read": 1.25 + } }, - "shisa-ai/shisa-v2-llama3.3-70b": { - "id": "shisa-ai/shisa-v2-llama3.3-70b", - "name": "Shisa V2 Llama 3.3 70B", - "family": "llama", + "minimax-m2.5-highspeed": { + "id": "minimax-m2.5-highspeed", + "name": "MiniMax-M2.5-highspeed", + "family": "minimax", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-07-26", - "last_updated": "2025-07-26", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 0.5 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-13", + "last_updated": "2026-02-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 2.4, + "cache_read": 0.06, + "cache_write": 0.375 + } }, - "shisa-ai/shisa-v2.1-llama3.3-70b": { - "id": "shisa-ai/shisa-v2.1-llama3.3-70b", - "name": "Shisa V2.1 Llama 3.3 70B", - "family": "llama", + "qwen-turbo": { + "id": "qwen-turbo", + "name": "Qwen Turbo", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2024-11-01", + "last_updated": "2025-04-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 0.5 }, - "limit": { "context": 32768, "input": 32768, "output": 4096 } + "limit": { + "context": 1000000, + "output": 16384 + }, + "cost": { + "input": 0.05, + "output": 0.2, + "reasoning": 0.5 + } }, - "openai/gpt-5.2-codex": { - "id": "openai/gpt-5.2-codex", - "name": "GPT 5.2 Codex", - "family": "gpt-codex", + "claude-sonnet-4-5": { + "id": "claude-sonnet-4-5", + "name": "Claude Sonnet 4.5 (latest)", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + "gemini-2.5-flash-lite": { + "id": "gemini-2.5-flash-lite", + "name": "Gemini 2.5 Flash Lite", + "family": "gemini-flash-lite", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2026-01-14", - "last_updated": "2026-01-14", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14 }, - "limit": { "context": 400000, "input": 400000, "output": 128000 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.01, + "input_audio": 0.3 + } }, - "openai/o1-pro": { - "id": "openai/o1-pro", - "name": "OpenAI o1 Pro", - "family": "o-pro", + "gpt-5": { + "id": "gpt-5", + "name": "GPT-5", + "family": "gpt", "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-01-25", - "last_updated": "2025-01-25", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 150, "output": 600 }, - "limit": { "context": 200000, "input": 200000, "output": 100000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "openai/o3-mini-low": { - "id": "openai/o3-mini-low", - "name": "OpenAI o3-mini (Low)", - "family": "o-mini", + "glm-4.7-flash": { + "id": "glm-4.7-flash", + "name": "GLM-4.7-Flash", + "family": "glm-flash", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-01-19", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } + }, + "mimo-v2-flash": { + "id": "mimo-v2-flash", + "name": "MiMo-V2-Flash", + "family": "mimo", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-01-31", - "last_updated": "2025-01-31", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 9.996, "output": 19.992 }, - "limit": { "context": 200000, "input": 200000, "output": 100000 } - }, - "openai/gpt-5.1-codex-mini": { - "id": "openai/gpt-5.1-codex-mini", - "name": "GPT 5.1 Codex Mini", - "family": "gpt-codex-mini", - "attachment": true, - "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 2 }, - "limit": { "context": 400000, "input": 400000, "output": 128000 } + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12-01", + "release_date": "2025-12-16", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.3, + "cache_read": 0.01 + } }, - "openai/o3-mini": { - "id": "openai/o3-mini", - "name": "OpenAI o3-mini", - "family": "o-mini", + "qwen3.6-max-preview": { + "id": "qwen3.6-max-preview", + "name": "Qwen3.6 Max Preview", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-01-31", - "last_updated": "2025-01-31", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-04-20", + "last_updated": "2026-04-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4 }, - "limit": { "context": 200000, "input": 200000, "output": 100000 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 1.3, + "output": 7.8, + "cache_read": 0.13, + "cache_write": 1.625 + } }, - "openai/gpt-5-pro": { - "id": "openai/gpt-5-pro", - "name": "GPT 5 Pro", - "family": "gpt-pro", + "gpt-5-chat-latest": { + "id": "gpt-5-chat-latest", + "name": "GPT-5 Chat (latest)", + "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": false, - "structured_output": false, + "structured_output": true, + "temperature": true, + "knowledge": "2024-09-30", "release_date": "2025-08-07", "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 120 }, - "limit": { "context": 400000, "input": 400000, "output": 128000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10 + } }, - "openai/gpt-5": { - "id": "openai/gpt-5", - "name": "GPT 5", - "family": "gpt", + "claude-opus-4-20250514": { + "id": "claude-opus-4-20250514", + "name": "Claude Opus 4", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 400000, "input": 400000, "output": 128000 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "openai/chatgpt-4o-latest": { - "id": "openai/chatgpt-4o-latest", - "name": "ChatGPT 4o", - "family": "gpt", - "attachment": true, + "qwen2-5-vl-72b-instruct": { + "id": "qwen2-5-vl-72b-instruct", + "name": "Qwen2.5-VL 72B Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2024-09", + "last_updated": "2024-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 2.8, + "output": 8.4 + } + }, + "gpt-5.5-pro": { + "id": "gpt-5.5-pro", + "name": "GPT-5.5 Pro", + "family": "gpt-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, "structured_output": true, - "release_date": "2024-05-13", - "last_updated": "2024-05-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-12-01", + "release_date": "2026-04-23", + "last_updated": "2026-04-23", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 4.998, "output": 14.993999999999998 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 30, + "output": 180, + "context_over_200k": { + "input": 60, + "output": 270 + }, + "tiers": [ + { + "input": 60, + "output": 270, + "tier": { + "type": "context", + "size": 272000 + } + } + ] + } }, - "openai/gpt-4-turbo": { - "id": "openai/gpt-4-turbo", - "name": "GPT-4 Turbo", + "gpt-4.1": { + "id": "gpt-4.1", + "name": "GPT-4.1", "family": "gpt", "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2023-11-06", - "last_updated": "2024-01-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 10, "output": 30 }, - "limit": { "context": 128000, "input": 128000, "output": 4096 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } }, - "openai/gpt-4o": { - "id": "openai/gpt-4o", - "name": "GPT-4o", - "family": "gpt", - "attachment": true, + "devstral-small-2507": { + "id": "devstral-small-2507", + "name": "Devstral Small", + "family": "devstral", + "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-05-13", - "last_updated": "2024-05-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.499, "output": 9.996 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "tool_call": true, + "temperature": true, + "knowledge": "2025-05", + "release_date": "2025-07-10", + "last_updated": "2025-07-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } }, - "openai/o3-mini-high": { - "id": "openai/o3-mini-high", - "name": "OpenAI o3-mini (High)", - "family": "o-mini", + "kimi-k2-thinking": { + "id": "kimi-k2-thinking", + "name": "Kimi K2 Thinking", + "family": "kimi-thinking", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-01-31", - "last_updated": "2025-01-31", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.64, "output": 2.588 }, - "limit": { "context": 200000, "input": 200000, "output": 100000 } + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-08", + "release_date": "2025-11-06", + "last_updated": "2025-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 2.5, + "cache_read": 0.15 + } }, - "openai/gpt-5-mini": { - "id": "openai/gpt-5-mini", - "name": "GPT 5 Mini", - "family": "gpt-mini", + "gemini-2.0-flash-lite": { + "id": "gemini-2.0-flash-lite", + "name": "Gemini 2.0 Flash Lite", + "family": "gemini-flash-lite", "attachment": true, - "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 2 }, - "limit": { "context": 400000, "input": 400000, "output": 128000 } - }, - "openai/gpt-4-turbo-preview": { - "id": "openai/gpt-4-turbo-preview", - "name": "GPT-4 Turbo Preview", - "family": "gpt", - "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2023-11-06", - "last_updated": "2024-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2024-06", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 9.996, "output": 30.004999999999995 }, - "limit": { "context": 128000, "input": 128000, "output": 4096 } + "limit": { + "context": 1048576, + "output": 8192 + }, + "cost": { + "input": 0.075, + "output": 0.3 + } }, - "openai/gpt-4o-mini": { - "id": "openai/gpt-4o-mini", - "name": "GPT-4o mini", + "gpt-4.1-mini": { + "id": "gpt-4.1-mini", + "name": "GPT-4.1 mini", "family": "gpt-mini", "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1496, "output": 0.595 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 1.6, + "cache_read": 0.1 + } }, - "openai/gpt-5.1-codex-max": { - "id": "openai/gpt-5.1-codex-max", - "name": "GPT 5.1 Codex Max", + "gpt-5.1-codex": { + "id": "gpt-5.1-codex", + "name": "GPT-5.1 Codex", "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", "release_date": "2025-11-13", "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 20 }, - "limit": { "context": 400000, "input": 400000, "output": 128000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "openai/gpt-4.1": { - "id": "openai/gpt-4.1", - "name": "GPT 4.1", - "family": "gpt", - "attachment": true, + "grok-3": { + "id": "grok-3", + "name": "Grok 3", + "family": "grok", + "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, - "release_date": "2025-09-10", - "last_updated": "2025-09-10", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8 }, - "limit": { "context": 1047576, "input": 1047576, "output": 32768 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75 + } }, - "openai/gpt-5.1-chat-latest": { - "id": "openai/gpt-5.1-chat-latest", - "name": "GPT 5.1 Chat (Latest)", - "family": "gpt", + "grok-4-fast-non-reasoning": { + "id": "grok-4-fast-non-reasoning", + "name": "Grok 4 Fast (Non-Reasoning)", + "family": "grok", "attachment": true, - "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 400000, "input": 400000, "output": 16384 } - }, - "openai/gpt-3.5-turbo": { - "id": "openai/gpt-3.5-turbo", - "name": "GPT-3.5 Turbo", - "family": "gpt", - "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2022-11-30", - "last_updated": "2024-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 1.5 }, - "limit": { "context": 16385, "input": 16385, "output": 4096 } - }, - "openai/gpt-oss-120b": { - "id": "openai/gpt-oss-120b", - "name": "GPT OSS 120B", - "family": "gpt-oss", - "attachment": false, - "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-07", + "release_date": "2025-09-19", + "last_updated": "2025-09-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.05, "output": 0.25 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "openai/gpt-5.1-chat": { - "id": "openai/gpt-5.1-chat", - "name": "GPT 5.1 Chat", - "family": "gpt", + "sonar-reasoning-pro": { + "id": "sonar-reasoning-pro", + "name": "Sonar Reasoning Pro", + "family": "sonar-reasoning", "attachment": true, "reasoning": true, "tool_call": false, - "structured_output": false, - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 400000, "input": 400000, "output": 128000 } - }, - "openai/o1": { - "id": "openai/o1", - "name": "OpenAI o1", - "family": "o", - "attachment": false, - "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-17", - "last_updated": "2024-12-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 14.993999999999998, "output": 59.993 }, - "limit": { "context": 200000, "input": 200000, "output": 100000 } - }, - "openai/o3": { - "id": "openai/o3", - "name": "OpenAI o3", - "family": "o", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-09-01", + "release_date": "2024-01-01", + "last_updated": "2025-09-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 8 }, - "limit": { "context": 200000, "input": 200000, "output": 100000 } - }, - "openai/gpt-4o-search-preview": { - "id": "openai/gpt-4o-search-preview", - "name": "GPT-4o Search Preview", - "family": "gpt", + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 2, + "output": 8 + } + } + } + }, + "google-vertex": { + "id": "google-vertex", + "env": ["GOOGLE_VERTEX_PROJECT", "GOOGLE_VERTEX_LOCATION", "GOOGLE_APPLICATION_CREDENTIALS"], + "npm": "@ai-sdk/google-vertex", + "name": "Vertex", + "doc": "https://cloud.google.com/vertex-ai/generative-ai/docs/models", + "models": { + "gemini-2.0-flash": { + "id": "gemini-2.0-flash", + "name": "Gemini 2.0 Flash", + "family": "gemini-flash", "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-05-13", - "last_updated": "2024-05-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-06", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.47, "output": 5.88 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "limit": { + "context": 1048576, + "output": 8192 + }, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.025 + } }, - "openai/o4-mini-high": { - "id": "openai/o4-mini-high", - "name": "OpenAI o4-mini high", - "family": "o-mini", - "attachment": false, + "gemini-3-pro-preview": { + "id": "gemini-3-pro-preview", + "name": "Gemini 3 Pro Preview", + "family": "gemini-pro", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.1, "output": 4.4 }, - "limit": { "context": 200000, "input": 200000, "output": 100000 } - }, - "openai/gpt-4o-2024-11-20": { - "id": "openai/gpt-4o-2024-11-20", - "name": "GPT-4o (2024-11-20)", - "family": "gpt", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-11-20", - "last_updated": "2024-11-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2, + "tiers": [ + { + "input": 4, + "output": 18, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 18, + "cache_read": 0.4 + } + } }, - "openai/gpt-5.2-chat": { - "id": "openai/gpt-5.2-chat", - "name": "GPT 5.2 Chat", - "family": "gpt", + "gemini-flash-latest": { + "id": "gemini-flash-latest", + "name": "Gemini Flash Latest", + "family": "gemini-flash", "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2026-01-01", - "last_updated": "2026-01-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14 }, - "limit": { "context": 400000, "input": 400000, "output": 16384 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.075, + "cache_write": 0.383 + } }, - "openai/gpt-5.2": { - "id": "openai/gpt-5.2", - "name": "GPT 5.2", - "family": "gpt", + "gemini-2.5-flash-lite-preview-06-17": { + "id": "gemini-2.5-flash-lite-preview-06-17", + "name": "Gemini 2.5 Flash Lite Preview 06-17", + "family": "gemini-flash-lite", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2026-01-01", - "last_updated": "2026-01-01", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14 }, - "limit": { "context": 400000, "input": 400000, "output": 128000 } + "limit": { + "context": 65536, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.025 + } }, - "openai/o4-mini-deep-research": { - "id": "openai/o4-mini-deep-research", - "name": "OpenAI o4-mini Deep Research", - "family": "o-mini", - "attachment": false, + "gemini-2.5-flash": { + "id": "gemini-2.5-flash", + "name": "Gemini 2.5 Flash", + "family": "gemini-flash", + "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 9.996, "output": 19.992 }, - "limit": { "context": 200000, "input": 200000, "output": 100000 } - }, - "openai/gpt-5.1": { - "id": "openai/gpt-5.1", - "name": "GPT 5.1", - "family": "gpt", + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.075, + "cache_write": 0.383 + } + }, + "gemini-2.5-flash-preview-09-2025": { + "id": "gemini-2.5-flash-preview-09-2025", + "name": "Gemini 2.5 Flash Preview 09-25", + "family": "gemini-flash", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 400000, "input": 400000, "output": 128000 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.075, + "cache_write": 0.383 + } }, - "openai/gpt-4o-mini-search-preview": { - "id": "openai/gpt-4o-mini-search-preview", - "name": "GPT-4o mini Search Preview", - "family": "gpt-mini", + "zai-org/glm-5-maas": { + "id": "zai-org/glm-5-maas", + "name": "GLM-5", + "family": "glm", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.088, "output": 0.35 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } - }, - "openai/gpt-4.1-mini": { - "id": "openai/gpt-4.1-mini", - "name": "GPT 4.1 Mini", - "family": "gpt-mini", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 1.6 }, - "limit": { "context": 1047576, "input": 1047576, "output": 32768 } + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 131072 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible", + "api": "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi" + }, + "cost": { + "input": 1, + "output": 3.2, + "cache_read": 0.1 + } }, - "openai/o3-pro-2025-06-10": { - "id": "openai/o3-pro-2025-06-10", - "name": "OpenAI o3-pro (2025-06-10)", - "family": "o-pro", + "zai-org/glm-4.7-maas": { + "id": "zai-org/glm-4.7-maas", + "name": "GLM-4.7", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, - "release_date": "2025-06-10", - "last_updated": "2025-06-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 9.996, "output": 19.992 }, - "limit": { "context": 200000, "input": 200000, "output": 100000 } + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-01-06", + "last_updated": "2026-01-06", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 200000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible", + "api": "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi" + }, + "cost": { + "input": 0.6, + "output": 2.2 + } }, - "openai/gpt-5-chat-latest": { - "id": "openai/gpt-5-chat-latest", - "name": "GPT 5 Chat", - "family": "gpt", - "attachment": true, + "deepseek-ai/deepseek-v3.2-maas": { + "id": "deepseek-ai/deepseek-v3.2-maas", + "name": "DeepSeek V3.2", + "family": "deepseek", + "attachment": false, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 400000, "input": 400000, "output": 128000 } + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-12-17", + "last_updated": "2026-04-04", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 65536 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible", + "api": "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi" + }, + "cost": { + "input": 0.56, + "output": 1.68, + "cache_read": 0.056 + } }, - "openai/gpt-5-nano": { - "id": "openai/gpt-5-nano", - "name": "GPT 5 Nano", - "family": "gpt-nano", - "attachment": true, + "deepseek-ai/deepseek-v3.1-maas": { + "id": "deepseek-ai/deepseek-v3.1-maas", + "name": "DeepSeek V3.1", + "family": "deepseek", + "attachment": false, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.05, "output": 0.4 }, - "limit": { "context": 400000, "input": 400000, "output": 128000 } + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-08-28", + "last_updated": "2025-08-28", + "modalities": { + "input": ["text", "pdf"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 32768 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible", + "api": "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi" + }, + "cost": { + "input": 0.6, + "output": 1.7 + } }, - "openai/gpt-oss-safeguard-20b": { - "id": "openai/gpt-oss-safeguard-20b", - "name": "GPT OSS Safeguard 20B", + "openai/gpt-oss-120b-maas": { + "id": "openai/gpt-oss-120b-maas", + "name": "GPT OSS 120B", "family": "gpt-oss", "attachment": false, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-10-29", - "last_updated": "2025-10-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.075, "output": 0.3 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "tool_call": true, + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.09, + "output": 0.36 + } }, - "openai/gpt-oss-20b": { - "id": "openai/gpt-oss-20b", + "openai/gpt-oss-20b-maas": { + "id": "openai/gpt-oss-20b-maas", "name": "GPT OSS 20B", "family": "gpt-oss", "attachment": false, "reasoning": true, - "tool_call": false, - "structured_output": false, + "tool_call": true, + "temperature": true, "release_date": "2025-08-05", "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.04, "output": 0.15 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.07, + "output": 0.25 + } }, - "openai/gpt-5.1-2025-11-13": { - "id": "openai/gpt-5.1-2025-11-13", - "name": "GPT-5.1 (2025-11-13)", - "family": "gpt", + "meta/llama-3.3-70b-instruct-maas": { + "id": "meta/llama-3.3-70b-instruct-maas", + "name": "Llama 3.3 70B Instruct", + "family": "llama", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 1000000, "input": 1000000, "output": 32768 } - }, - "openai/gpt-4o-2024-08-06": { - "id": "openai/gpt-4o-2024-08-06", - "name": "GPT-4o (2024-08-06)", - "family": "gpt", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-08-06", - "last_updated": "2024-08-06", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.499, "output": 9.996 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2025-04-29", + "last_updated": "2025-04-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible", + "api": "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi" + }, + "cost": { + "input": 0.72, + "output": 0.72 + } }, - "openai/gpt-4.1-nano": { - "id": "openai/gpt-4.1-nano", - "name": "GPT 4.1 Nano", - "family": "gpt-nano", + "meta/llama-4-maverick-17b-128e-instruct-maas": { + "id": "meta/llama-4-maverick-17b-128e-instruct-maas", + "name": "Llama 4 Maverick 17B 128E Instruct", + "family": "llama", "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 1047576, "input": 1047576, "output": 32768 } + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2024-08", + "release_date": "2025-04-29", + "last_updated": "2025-04-29", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 524288, + "output": 8192 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible", + "api": "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi" + }, + "cost": { + "input": 0.35, + "output": 1.15 + } }, - "openai/o1-preview": { - "id": "openai/o1-preview", - "name": "OpenAI o1-preview", - "family": "o", + "qwen/qwen3-235b-a22b-instruct-2507-maas": { + "id": "qwen/qwen3-235b-a22b-instruct-2507-maas", + "name": "Qwen3 235B A22B Instruct", + "family": "qwen", "attachment": false, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2024-09-12", - "last_updated": "2024-09-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 14.993999999999998, "output": 59.993 }, - "limit": { "context": 128000, "input": 128000, "output": 32768 } - }, - "openai/gpt-5-codex": { - "id": "openai/gpt-5-codex", - "name": "GPT-5 Codex", - "family": "gpt-codex", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 9.996, "output": 19.992 }, - "limit": { "context": 256000, "input": 256000, "output": 32768 } + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-08-13", + "last_updated": "2025-08-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 16384 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible", + "api": "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi" + }, + "cost": { + "input": 0.22, + "output": 0.88 + } }, - "openai/o4-mini": { - "id": "openai/o4-mini", - "name": "OpenAI o4-mini", - "family": "o-mini", + "moonshotai/kimi-k2-thinking-maas": { + "id": "moonshotai/kimi-k2-thinking-maas", + "name": "Kimi K2 Thinking", + "family": "kimi-thinking", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-08", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible", + "api": "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}/endpoints/openapi" + }, + "cost": { + "input": 0.6, + "output": 2.5 + } + }, + "gemini-flash-lite-latest": { + "id": "gemini-flash-lite-latest", + "name": "Gemini Flash-Lite Latest", + "family": "gemini-flash-lite", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4 }, - "limit": { "context": 200000, "input": 200000, "output": 100000 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.025 + } }, - "openai/o3-deep-research": { - "id": "openai/o3-deep-research", - "name": "OpenAI o3 Deep Research", - "family": "o", - "attachment": false, + "gemini-2.5-pro-preview-05-06": { + "id": "gemini-2.5-pro-preview-05-06", + "name": "Gemini 2.5 Pro Preview 05-06", + "family": "gemini-pro", + "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-05-06", + "last_updated": "2025-05-06", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 9.996, "output": 19.992 }, - "limit": { "context": 200000, "input": 200000, "output": 100000 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.31 + } }, - "openai/gpt-5.1-codex": { - "id": "openai/gpt-5.1-codex", - "name": "GPT 5.1 Codex", - "family": "gpt-codex", + "claude-haiku-4-5@20251001": { + "id": "claude-haiku-4-5@20251001", + "name": "Claude Haiku 4.5", + "family": "claude-haiku", "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 400000, "input": 400000, "output": 128000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/google-vertex/anthropic" + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } }, - "openai/gpt-5.2-pro": { - "id": "openai/gpt-5.2-pro", - "name": "GPT 5.2 Pro", - "family": "gpt-pro", + "gemini-3.1-pro-preview-customtools": { + "id": "gemini-3.1-pro-preview-customtools", + "name": "Gemini 3.1 Pro Preview Custom Tools", + "family": "gemini-pro", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2026-01-01", - "last_updated": "2026-01-01", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-02-19", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 21, "output": 168 }, - "limit": { "context": 400000, "input": 400000, "output": 128000 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2, + "context_over_200k": { + "input": 4, + "output": 18, + "cache_read": 0.4 + }, + "tiers": [ + { + "input": 4, + "output": 18, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ] + } }, - "CrucibleLab/L3.3-70B-Loki-V2.0": { - "id": "CrucibleLab/L3.3-70B-Loki-V2.0", - "name": "L3.3 70B Loki v2.0", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2026-01-22", - "last_updated": "2026-01-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "claude-sonnet-4-6@default": { + "id": "claude-sonnet-4-6@default", + "name": "Claude Sonnet 4.6", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.49299999999999994, "output": 0.49299999999999994 }, - "limit": { "context": 16384, "input": 16384, "output": 16384 } + "limit": { + "context": 200000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/google-vertex/anthropic" + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75, + "context_over_200k": { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5 + }, + "tiers": [ + { + "input": 6, + "output": 22.5, + "cache_read": 0.6, + "cache_write": 7.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ] + } }, - "microsoft/MAI-DS-R1-FP8": { - "id": "microsoft/MAI-DS-R1-FP8", - "name": "Microsoft DeepSeek R1", - "family": "deepseek", + "gemini-2.5-flash-lite-preview-09-2025": { + "id": "gemini-2.5-flash-lite-preview-09-2025", + "name": "Gemini 2.5 Flash Lite Preview 09-25", + "family": "gemini-flash-lite", "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", "release_date": "2025-09-25", "last_updated": "2025-09-25", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 0.3 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.025 + } }, - "microsoft/wizardlm-2-8x22b": { - "id": "microsoft/wizardlm-2-8x22b", - "name": "WizardLM-2 8x22B", - "family": "gpt", + "claude-3-5-haiku@20241022": { + "id": "claude-3-5-haiku@20241022", + "name": "Claude Haiku 3.5", + "family": "claude-haiku", "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-15", - "last_updated": "2025-04-15", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-07-31", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.49299999999999994, "output": 0.49299999999999994 }, - "limit": { "context": 65536, "input": 65536, "output": 8192 } + "limit": { + "context": 200000, + "output": 8192 + }, + "provider": { + "npm": "@ai-sdk/google-vertex/anthropic" + }, + "cost": { + "input": 0.8, + "output": 4, + "cache_read": 0.08, + "cache_write": 1 + } }, - "cohere/command-r-plus-08-2024": { - "id": "cohere/command-r-plus-08-2024", - "name": "Cohere: Command R+", - "family": "command-r", - "attachment": false, - "reasoning": false, + "gemini-3.1-flash-lite-preview": { + "id": "gemini-3.1-flash-lite-preview", + "name": "Gemini 3.1 Flash Lite Preview", + "family": "gemini-flash-lite", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": false, - "release_date": "2024-08-30", - "last_updated": "2024-08-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-03-03", + "last_updated": "2026-03-03", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.856, "output": 14.246 }, - "limit": { "context": 128000, "input": 128000, "output": 4096 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.25, + "output": 1.5, + "cache_read": 0.025, + "input_audio": 0.5 + } }, - "cohere/command-r": { - "id": "cohere/command-r", - "name": "Cohere: Command R", - "family": "command-r", - "attachment": false, + "claude-3-5-sonnet@20241022": { + "id": "claude-3-5-sonnet@20241022", + "name": "Claude Sonnet 3.5 v2", + "family": "claude-sonnet", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-03-11", - "last_updated": "2024-03-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04-30", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.476, "output": 1.428 }, - "limit": { "context": 128000, "input": 128000, "output": 4096 } + "limit": { + "context": 200000, + "output": 8192 + }, + "provider": { + "npm": "@ai-sdk/google-vertex/anthropic" + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "x-ai/grok-4-fast": { - "id": "x-ai/grok-4-fast", - "name": "Grok 4 Fast", - "family": "grok", + "gemini-3.1-pro-preview": { + "id": "gemini-3.1-pro-preview", + "name": "Gemini 3.1 Pro Preview", + "family": "gemini-pro", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-09-20", - "last_updated": "2025-09-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-02-19", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5 }, - "limit": { "context": 2000000, "input": 2000000, "output": 131072 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2, + "context_over_200k": { + "input": 4, + "output": 18, + "cache_read": 0.4 + }, + "tiers": [ + { + "input": 4, + "output": 18, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ] + } }, - "x-ai/grok-4-fast:thinking": { - "id": "x-ai/grok-4-fast:thinking", - "name": "Grok 4 Fast Thinking", - "family": "grok", + "claude-opus-4-1@20250805": { + "id": "claude-opus-4-1@20250805", + "name": "Claude Opus 4.1", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5 }, - "limit": { "context": 2000000, "input": 2000000, "output": 131072 } + "limit": { + "context": 200000, + "output": 32000 + }, + "provider": { + "npm": "@ai-sdk/google-vertex/anthropic" + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "x-ai/grok-4.1-fast": { - "id": "x-ai/grok-4.1-fast", - "name": "Grok 4.1 Fast", - "family": "grok", + "gemini-3-flash-preview": { + "id": "gemini-3-flash-preview", + "name": "Gemini 3 Flash Preview", + "family": "gemini-flash", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-11-20", - "last_updated": "2025-11-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.5 }, - "limit": { "context": 2000000, "input": 2000000, "output": 131072 } - }, - "x-ai/grok-code-fast-1": { - "id": "x-ai/grok-code-fast-1", - "name": "Grok Code Fast 1", - "family": "grok", - "attachment": false, - "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-08-28", - "last_updated": "2025-08-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 1.5 }, - "limit": { "context": 256000, "input": 256000, "output": 131072 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.05, + "input_audio": 1 + } }, - "x-ai/grok-4.1-fast-reasoning": { - "id": "x-ai/grok-4.1-fast-reasoning", - "name": "Grok 4.1 Fast Reasoning", - "family": "grok", + "gemini-2.5-flash-preview-05-20": { + "id": "gemini-2.5-flash-preview-05-20", + "name": "Gemini 2.5 Flash Preview 05-20", + "family": "gemini-flash", "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-11-20", - "last_updated": "2025-11-20", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-05-20", + "last_updated": "2025-05-20", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5 }, - "limit": { "context": 2000000, "input": 2000000, "output": 131072 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.0375 + } }, - "x-ai/grok-4-07-09": { - "id": "x-ai/grok-4-07-09", - "name": "Grok 4", - "family": "grok", - "attachment": true, - "reasoning": true, + "gemini-embedding-001": { + "id": "gemini-embedding-001", + "name": "Gemini Embedding 001", + "family": "gemini", + "attachment": false, + "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-05", + "release_date": "2025-05-20", + "last_updated": "2025-05-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 256000, "input": 256000, "output": 131072 } + "limit": { + "context": 2048, + "output": 3072 + }, + "cost": { + "input": 0.15, + "output": 0 + } }, - "anthropic/claude-opus-4.6:thinking:max": { - "id": "anthropic/claude-opus-4.6:thinking:max", - "name": "Claude 4.6 Opus Thinking Max", - "family": "claude-opus", + "gemini-2.5-pro": { + "id": "gemini-2.5-pro", + "name": "Gemini 2.5 Pro", + "family": "gemini-pro", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 4.998, "output": 25.007 }, - "limit": { "context": 1000000, "input": 1000000, "output": 128000 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125, + "context_over_200k": { + "input": 2.5, + "output": 15, + "cache_read": 0.25 + }, + "tiers": [ + { + "input": 2.5, + "output": 15, + "cache_read": 0.25, + "tier": { + "type": "context", + "size": 200000 + } + } + ] + } }, - "anthropic/claude-opus-4.6": { - "id": "anthropic/claude-opus-4.6", - "name": "Claude 4.6 Opus", - "family": "claude-opus", + "gemini-2.5-pro-preview-06-05": { + "id": "gemini-2.5-pro-preview-06-05", + "name": "Gemini 2.5 Pro Preview 06-05", + "family": "gemini-pro", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-06-05", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 4.998, "output": 25.007 }, - "limit": { "context": 1000000, "input": 1000000, "output": 128000 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.31 + } }, - "anthropic/claude-sonnet-4.6:thinking": { - "id": "anthropic/claude-sonnet-4.6:thinking", - "name": "Claude Sonnet 4.6 Thinking", + "claude-sonnet-4@20250514": { + "id": "claude-sonnet-4@20250514", + "name": "Claude Sonnet 4", "family": "claude-sonnet", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2026-02-17", - "last_updated": "2026-02-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.992, "output": 14.993999999999998 }, - "limit": { "context": 1000000, "input": 1000000, "output": 128000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/google-vertex/anthropic" + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "anthropic/claude-opus-4.6:thinking:medium": { - "id": "anthropic/claude-opus-4.6:thinking:medium", - "name": "Claude 4.6 Opus Thinking Medium", - "family": "claude-opus", + "gemini-3.1-flash-lite": { + "id": "gemini-3.1-flash-lite", + "name": "Gemini 3.1 Flash Lite", + "family": "gemini-flash-lite", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-05-07", + "last_updated": "2026-05-07", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 4.998, "output": 25.007 }, - "limit": { "context": 1000000, "input": 1000000, "output": 128000 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.25, + "output": 1.5, + "cache_read": 0.025, + "input_audio": 0.5 + } }, - "anthropic/claude-sonnet-4.6": { - "id": "anthropic/claude-sonnet-4.6", - "name": "Claude Sonnet 4.6", + "claude-3-7-sonnet@20250219": { + "id": "claude-3-7-sonnet@20250219", + "name": "Claude Sonnet 3.7", "family": "claude-sonnet", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2026-02-17", - "last_updated": "2026-02-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-10-31", + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.992, "output": 14.993999999999998 }, - "limit": { "context": 1000000, "input": 1000000, "output": 128000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/google-vertex/anthropic" + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "anthropic/claude-opus-4.6:thinking": { - "id": "anthropic/claude-opus-4.6:thinking", - "name": "Claude 4.6 Opus Thinking", + "claude-opus-4@20250514": { + "id": "claude-opus-4@20250514", + "name": "Claude Opus 4", "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 4.998, "output": 25.007 }, - "limit": { "context": 1000000, "input": 1000000, "output": 128000 } + "limit": { + "context": 200000, + "output": 32000 + }, + "provider": { + "npm": "@ai-sdk/google-vertex/anthropic" + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "anthropic/claude-opus-4.6:thinking:low": { - "id": "anthropic/claude-opus-4.6:thinking:low", - "name": "Claude 4.6 Opus Thinking Low", + "claude-opus-4-5@20251101": { + "id": "claude-opus-4-5@20251101", + "name": "Claude Opus 4.5", "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-11-01", + "last_updated": "2025-11-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 4.998, "output": 25.007 }, - "limit": { "context": 1000000, "input": 1000000, "output": 128000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/google-vertex/anthropic" + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "raifle/sorcererlm-8x22b": { - "id": "raifle/sorcererlm-8x22b", - "name": "SorcererLM 8x22B", - "family": "mixtral", + "gemini-2.5-flash-preview-04-17": { + "id": "gemini-2.5-flash-preview-04-17", + "name": "Gemini 2.5 Flash Preview 04-17", + "family": "gemini-flash", "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 4.505, "output": 4.505 }, - "limit": { "context": 16000, "input": 16000, "output": 8192 } - }, - "nothingiisreal/L3.1-70B-Celeste-V0.1-BF16": { - "id": "nothingiisreal/L3.1-70B-Celeste-V0.1-BF16", - "name": "Llama 3.1 70B Celeste v0.1", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.49299999999999994, "output": 0.49299999999999994 }, - "limit": { "context": 16384, "input": 16384, "output": 16384 } - }, - "NousResearch 2/hermes-4-70b": { - "id": "NousResearch 2/hermes-4-70b", - "name": "Hermes 4 Medium", - "family": "nousresearch", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-07-03", - "last_updated": "2025-07-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2006, "output": 0.39949999999999997 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 } - }, - "NousResearch 2/hermes-4-405b:thinking": { - "id": "NousResearch 2/hermes-4-405b:thinking", - "name": "Hermes 4 Large (Thinking)", - "family": "nousresearch", - "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 } - }, - "NousResearch 2/hermes-3-llama-3.1-70b": { - "id": "NousResearch 2/hermes-3-llama-3.1-70b", - "name": "Hermes 3 70B", - "family": "nousresearch", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2026-01-07", - "last_updated": "2026-01-07", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.408, "output": 0.408 }, - "limit": { "context": 65536, "input": 65536, "output": 8192 } - }, - "NousResearch 2/Hermes-4-70B:thinking": { - "id": "NousResearch 2/Hermes-4-70B:thinking", - "name": "Hermes 4 (Thinking)", - "family": "nousresearch", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-09-17", - "last_updated": "2025-09-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-04-17", + "last_updated": "2025-04-17", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2006, "output": 0.39949999999999997 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.0375 + } }, - "NousResearch 2/hermes-4-405b": { - "id": "NousResearch 2/hermes-4-405b", - "name": "Hermes 4 Large", - "family": "nousresearch", - "attachment": false, - "reasoning": false, + "claude-sonnet-4-5@20250929": { + "id": "claude-sonnet-4-5@20250929", + "name": "Claude Sonnet 4.5", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-08-26", - "last_updated": "2025-08-26", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 } - }, - "NousResearch 2/DeepHermes-3-Mistral-24B-Preview": { - "id": "NousResearch 2/DeepHermes-3-Mistral-24B-Preview", - "name": "DeepHermes-3 Mistral 24B (Preview)", - "family": "nousresearch", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-05-10", - "last_updated": "2025-05-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 0.3 }, - "limit": { "context": 128000, "input": 128000, "output": 32768 } + "limit": { + "context": 200000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/google-vertex/anthropic" + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "deepseek/deepseek-v3.2:thinking": { - "id": "deepseek/deepseek-v3.2:thinking", - "name": "DeepSeek V3.2 Thinking", - "family": "deepseek", + "gemini-2.5-flash-lite": { + "id": "gemini-2.5-flash-lite", + "name": "Gemini 2.5 Flash Lite", + "family": "gemini-flash-lite", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.27999999999999997, "output": 0.42000000000000004 }, - "limit": { "context": 163000, "input": 163000, "output": 65536 } - }, - "deepseek/deepseek-prover-v2-671b": { - "id": "deepseek/deepseek-prover-v2-671b", - "name": "DeepSeek Prover v2 671B", - "family": "deepseek", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-30", - "last_updated": "2025-04-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 2.5 }, - "limit": { "context": 160000, "input": 160000, "output": 16384 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.01, + "input_audio": 0.3 + } }, - "deepseek/deepseek-v3.2-speciale": { - "id": "deepseek/deepseek-v3.2-speciale", - "name": "DeepSeek V3.2 Speciale", - "family": "deepseek", + "claude-opus-4-6@default": { + "id": "claude-opus-4-6@default", + "name": "Claude Opus 4.6", + "family": "claude-opus", "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-02", - "last_updated": "2025-12-02", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.27999999999999997, "output": 0.42000000000000004 }, - "limit": { "context": 163000, "input": 163000, "output": 65536 } + "limit": { + "context": 1000000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/google-vertex/anthropic" + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25, + "context_over_200k": { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5 + }, + "tiers": [ + { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ] + } }, - "deepseek/deepseek-v3.2": { - "id": "deepseek/deepseek-v3.2", - "name": "DeepSeek V3.2", - "family": "deepseek", + "gemini-2.0-flash-lite": { + "id": "gemini-2.0-flash-lite", + "name": "Gemini 2.0 Flash Lite", + "family": "gemini-flash-lite", "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-06", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.27999999999999997, "output": 0.42000000000000004 }, - "limit": { "context": 163000, "input": 163000, "output": 65536 } + "limit": { + "context": 1048576, + "output": 8192 + }, + "cost": { + "input": 0.075, + "output": 0.3 + } }, - "zai-org/glm-4.7-flash": { - "id": "zai-org/glm-4.7-flash", - "name": "GLM 4.7 Flash", + "claude-opus-4-7@default": { + "id": "claude-opus-4-7@default", + "name": "Claude Opus 4.7", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/google-vertex/anthropic" + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25, + "context_over_200k": { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5 + }, + "tiers": [ + { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ] + } + } + } + }, + "cloudflare-workers-ai": { + "id": "cloudflare-workers-ai", + "env": ["CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/ai/v1", + "name": "Cloudflare Workers AI", + "doc": "https://developers.cloudflare.com/workers-ai/models/", + "models": { + "@cf/zai-org/glm-4.7-flash": { + "id": "@cf/zai-org/glm-4.7-flash", + "name": "GLM-4.7-Flash", "family": "glm-flash", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "temperature": true, + "knowledge": "2025-04", "release_date": "2026-01-19", "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.07, "output": 0.4 }, - "limit": { "context": 200000, "input": 200000, "output": 128000 } + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.06, + "output": 0.4 + } }, - "zai-org/glm-5.1:thinking": { - "id": "zai-org/glm-5.1:thinking", - "name": "GLM 5.1 Thinking", - "family": "glm", + "@cf/nvidia/nemotron-3-120b-a12b": { + "id": "@cf/nvidia/nemotron-3-120b-a12b", + "name": "Nemotron 3 Super 120B", + "family": "nemotron", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2026-03-27", - "last_updated": "2026-03-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-03-11", + "last_updated": "2026-03-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 2.55 }, - "limit": { "context": 200000, "input": 200000, "output": 131072 } + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.5, + "output": 1.5 + } }, - "zai-org/glm-5:thinking": { - "id": "zai-org/glm-5:thinking", - "name": "GLM 5 Thinking", - "family": "glm", + "@cf/openai/gpt-oss-20b": { + "id": "@cf/openai/gpt-oss-20b", + "name": "GPT OSS 20B", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 2.55 }, - "limit": { "context": 200000, "input": 200000, "output": 128000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.2, + "output": 0.3 + } }, - "zai-org/glm-5": { - "id": "zai-org/glm-5", - "name": "GLM 5", - "family": "glm", + "@cf/openai/gpt-oss-120b": { + "id": "@cf/openai/gpt-oss-120b", + "name": "GPT OSS 120B", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 2.55 }, - "limit": { "context": 200000, "input": 200000, "output": 128000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.35, + "output": 0.75 + } }, - "zai-org/glm-5.1": { - "id": "zai-org/glm-5.1", - "name": "GLM 5.1", - "family": "glm", - "attachment": false, - "reasoning": true, + "@cf/meta/llama-4-scout-17b-16e-instruct": { + "id": "@cf/meta/llama-4-scout-17b-16e-instruct", + "name": "Llama 4 Scout 17B 16E Instruct", + "family": "llama", + "attachment": true, + "reasoning": false, "tool_call": true, - "structured_output": true, - "release_date": "2026-03-27", - "last_updated": "2026-03-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 2.55 }, - "limit": { "context": 200000, "input": 200000, "output": 131072 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.27, + "output": 0.85 + } }, - "zai-org/glm-4.7": { - "id": "zai-org/glm-4.7", - "name": "GLM 4.7", - "family": "glm", - "attachment": false, + "@cf/google/gemma-4-26b-a4b-it": { + "id": "@cf/google/gemma-4-26b-a4b-it", + "name": "Gemma 4 26B A4B IT", + "family": "gemma", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2026-01-29", - "last_updated": "2026-01-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.15, "output": 0.8 }, - "limit": { "context": 200000, "input": 200000, "output": 128000 } - }, - "NeverSleep/Llama-3-Lumimaid-70B-v0.1": { - "id": "NeverSleep/Llama-3-Lumimaid-70B-v0.1", - "name": "Lumimaid 70b", - "family": "llama", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-07-01", - "last_updated": "2024-07-01", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.006, "output": 2.006 }, - "limit": { "context": 16384, "input": 16384, "output": 8192 } - }, - "NeverSleep/Lumimaid-v0.2-70B": { - "id": "NeverSleep/Lumimaid-v0.2-70B", - "name": "Lumimaid v0.2", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-07-01", - "last_updated": "2024-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1, "output": 1.5 }, - "limit": { "context": 16384, "input": 16384, "output": 8192 } - }, - "nvidia/Llama-3.1-Nemotron-Ultra-253B-v1": { - "id": "nvidia/Llama-3.1-Nemotron-Ultra-253B-v1", - "name": "Nvidia Nemotron Ultra 253B", - "family": "nemotron", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-07-03", - "last_updated": "2025-07-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 0.8 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } - }, - "nvidia/nvidia-nemotron-nano-9b-v2": { - "id": "nvidia/nvidia-nemotron-nano-9b-v2", - "name": "Nvidia Nemotron Nano 9B v2", - "family": "nemotron", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-08-18", - "last_updated": "2025-08-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.17, "output": 0.68 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } - }, - "nvidia/Llama-3_3-Nemotron-Super-49B-v1_5": { - "id": "nvidia/Llama-3_3-Nemotron-Super-49B-v1_5", - "name": "Nvidia Nemotron Super 49B v1.5", - "family": "nemotron", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-08-08", - "last_updated": "2025-08-08", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.05, "output": 0.25 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } - }, - "nvidia/Llama-3.1-Nemotron-70B-Instruct-HF": { - "id": "nvidia/Llama-3.1-Nemotron-70B-Instruct-HF", - "name": "Nvidia Nemotron 70b", - "family": "nemotron", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-15", - "last_updated": "2025-04-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.357, "output": 0.408 }, - "limit": { "context": 16384, "input": 16384, "output": 8192 } - }, - "nvidia/Llama-3.3-Nemotron-Super-49B-v1": { - "id": "nvidia/Llama-3.3-Nemotron-Super-49B-v1", - "name": "Nvidia Nemotron Super 49B", - "family": "nemotron", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-08-08", - "last_updated": "2025-08-08", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.15 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } - }, - "nvidia/nemotron-3-nano-30b-a3b": { - "id": "nvidia/nemotron-3-nano-30b-a3b", - "name": "Nvidia Nemotron 3 Nano 30B", - "family": "nemotron", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, + "temperature": true, "release_date": "2025-12-15", "last_updated": "2025-12-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.17, "output": 0.68 }, - "limit": { "context": 256000, "input": 256000, "output": 262144 } + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 16384 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } }, - "z-ai/glm-4.6:thinking": { - "id": "z-ai/glm-4.6:thinking", - "name": "GLM 4.6 Thinking", - "family": "glm", - "attachment": false, + "@cf/moonshotai/kimi-k2.5": { + "id": "@cf/moonshotai/kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi", + "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 1.5 }, - "limit": { "context": 200000, "input": 200000, "output": 65535 } + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.6, + "output": 3, + "cache_read": 0.1 + } }, - "z-ai/glm-4.5v:thinking": { - "id": "z-ai/glm-4.5v:thinking", - "name": "GLM 4.5V Thinking", - "family": "glmv", + "@cf/moonshotai/kimi-k2.6": { + "id": "@cf/moonshotai/kimi-k2.6", + "name": "Kimi K2.6", + "family": "kimi", "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-11-22", - "last_updated": "2025-11-22", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.6, "output": 1.7999999999999998 }, - "limit": { "context": 64000, "input": 64000, "output": 96000 } - }, - "z-ai/glm-4.6": { - "id": "z-ai/glm-4.6", - "name": "GLM 4.6", - "family": "glm", - "attachment": false, - "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 1.5 }, - "limit": { "context": 200000, "input": 200000, "output": 65535 } - }, - "z-ai/glm-4.5v": { - "id": "z-ai/glm-4.5v", - "name": "GLM 4.5V", - "family": "glmv", - "attachment": true, - "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-11-22", - "last_updated": "2025-11-22", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.6, "output": 1.7999999999999998 }, - "limit": { "context": 64000, "input": 64000, "output": 96000 } + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-04-20", + "last_updated": "2026-04-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.95, + "output": 4, + "cache_read": 0.16 + } + } + } + }, + "groq": { + "id": "groq", + "env": ["GROQ_API_KEY"], + "npm": "@ai-sdk/groq", + "name": "Groq", + "doc": "https://console.groq.com/docs/models", + "models": { + "gemma2-9b-it": { + "id": "gemma2-9b-it", + "name": "Gemma 2 9B", + "family": "gemma", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-06", + "release_date": "2024-06-27", + "last_updated": "2024-06-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 8192 + }, + "status": "deprecated", + "cost": { + "input": 0.2, + "output": 0.2 + } }, - "nex-agi/deepseek-v3.1-nex-n1": { - "id": "nex-agi/deepseek-v3.1-nex-n1", - "name": "DeepSeek V3.1 Nex N1", - "family": "deepseek", + "mistral-saba-24b": { + "id": "mistral-saba-24b", + "name": "Mistral Saba 24B", + "family": "mistral", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-10", - "last_updated": "2025-12-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-08", + "release_date": "2025-02-06", + "last_updated": "2025-02-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.27999999999999997, "output": 0.42000000000000004 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 } + "limit": { + "context": 32768, + "output": 32768 + }, + "status": "deprecated", + "cost": { + "input": 0.79, + "output": 0.79 + } }, - "stepfun-ai/step-3.5-flash:thinking": { - "id": "stepfun-ai/step-3.5-flash:thinking", - "name": "Step 3.5 Flash Thinking", - "family": "step", + "deepseek-r1-distill-llama-70b": { + "id": "deepseek-r1-distill-llama-70b", + "name": "DeepSeek R1 Distill Llama 70B", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2026-02-02", - "last_updated": "2026-02-02", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.5 }, - "limit": { "context": 256000, "input": 256000, "output": 256000 } + "tool_call": true, + "temperature": true, + "knowledge": "2024-07", + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "status": "deprecated", + "cost": { + "input": 0.75, + "output": 0.99 + } }, - "stepfun-ai/step-3.5-flash": { - "id": "stepfun-ai/step-3.5-flash", - "name": "Step 3.5 Flash", - "family": "step", + "llama-guard-3-8b": { + "id": "llama-guard-3-8b", + "name": "Llama Guard 3 8B", + "family": "llama", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2026-02-02", - "last_updated": "2026-02-02", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.5 }, - "limit": { "context": 256000, "input": 256000, "output": 256000 } + "temperature": true, + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 8192 + }, + "status": "deprecated", + "cost": { + "input": 0.2, + "output": 0.2 + } }, - "cognitivecomputations/dolphin-2.9.2-qwen2-72b": { - "id": "cognitivecomputations/dolphin-2.9.2-qwen2-72b", - "name": "Dolphin 72b", - "family": "qwen", + "llama-3.3-70b-versatile": { + "id": "llama-3.3-70b-versatile", + "name": "Llama 3.3 70B Versatile", + "family": "llama", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-02-27", - "last_updated": "2025-02-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.306, "output": 0.306 }, - "limit": { "context": 8192, "input": 8192, "output": 4096 } + "tool_call": true, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.59, + "output": 0.79 + } }, - "allenai/olmo-3.1-32b-instruct": { - "id": "allenai/olmo-3.1-32b-instruct", - "name": "Olmo 3.1 32B Instruct", - "family": "allenai", + "allam-2-7b": { + "id": "allam-2-7b", + "name": "ALLaM-2-7b", + "family": "allam", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2026-01-25", - "last_updated": "2026-01-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-09", + "release_date": "2024-09", + "last_updated": "2024-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.6 }, - "limit": { "context": 65536, "input": 65536, "output": 8192 } + "limit": { + "context": 4096, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "allenai/olmo-3-32b-think": { - "id": "allenai/olmo-3-32b-think", - "name": "Olmo 3 32B Think", - "family": "allenai", + "whisper-large-v3": { + "id": "whisper-large-v3", + "name": "Whisper Large V3", + "family": "whisper", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-11-01", - "last_updated": "2025-11-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 0.44999999999999996 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 } + "temperature": true, + "knowledge": "2023-09", + "release_date": "2023-09-01", + "last_updated": "2025-09-05", + "modalities": { + "input": ["audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 448, + "output": 448 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "allenai/olmo-3.1-32b-think": { - "id": "allenai/olmo-3.1-32b-think", - "name": "Olmo 3.1 32B Think", - "family": "allenai", + "llama-3.1-8b-instant": { + "id": "llama-3.1-8b-instant", + "name": "Llama 3.1 8B Instant", + "family": "llama", "attachment": false, - "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2026-01-25", - "last_updated": "2026-01-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.5 }, - "limit": { "context": 65536, "input": 65536, "output": 8192 } + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.05, + "output": 0.08 + } }, - "allenai/molmo-2-8b": { - "id": "allenai/molmo-2-8b", - "name": "Molmo 2 8B", - "family": "allenai", - "attachment": true, + "llama3-70b-8192": { + "id": "llama3-70b-8192", + "name": "Llama 3 70B", + "family": "llama", + "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2026-02-14", - "last_updated": "2026-02-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 36864, "input": 36864, "output": 36864 } + "tool_call": true, + "temperature": true, + "knowledge": "2023-03", + "release_date": "2024-04-18", + "last_updated": "2024-04-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 8192 + }, + "status": "deprecated", + "cost": { + "input": 0.59, + "output": 0.79 + } }, - "TheDrummer 2/Cydonia-24B-v4.1": { - "id": "TheDrummer 2/Cydonia-24B-v4.1", - "name": "The Drummer Cydonia 24B v4.1", - "family": "llama", + "qwen-qwq-32b": { + "id": "qwen-qwq-32b", + "name": "Qwen QwQ 32B", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-08-19", - "last_updated": "2025-08-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1003, "output": 0.1207 }, - "limit": { "context": 16384, "input": 16384, "output": 32768 } + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-09", + "release_date": "2024-11-27", + "last_updated": "2024-11-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "status": "deprecated", + "cost": { + "input": 0.29, + "output": 0.39 + } }, - "TheDrummer 2/UnslopNemo-12B-v4.1": { - "id": "TheDrummer 2/UnslopNemo-12B-v4.1", - "name": "UnslopNemo 12b v4", - "family": "llama", - "attachment": true, + "whisper-large-v3-turbo": { + "id": "whisper-large-v3-turbo", + "name": "Whisper Large v3 Turbo", + "family": "whisper", + "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2024-07-01", - "last_updated": "2024-07-01", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.49299999999999994, "output": 0.49299999999999994 }, - "limit": { "context": 32768, "input": 32768, "output": 8192 } + "temperature": true, + "knowledge": "2024-10", + "release_date": "2024-10-01", + "last_updated": "2024-10-01", + "modalities": { + "input": ["audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 448, + "output": 448 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "TheDrummer 2/Cydonia-24B-v4.3": { - "id": "TheDrummer 2/Cydonia-24B-v4.3", - "name": "The Drummer Cydonia 24B v4.3", + "llama3-8b-8192": { + "id": "llama3-8b-8192", + "name": "Llama 3 8B", "family": "llama", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-25", - "last_updated": "2025-12-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1003, "output": 0.1207 }, - "limit": { "context": 32768, "input": 32768, "output": 32768 } + "tool_call": true, + "temperature": true, + "knowledge": "2023-03", + "release_date": "2024-04-18", + "last_updated": "2024-04-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 8192 + }, + "status": "deprecated", + "cost": { + "input": 0.05, + "output": 0.08 + } }, - "TheDrummer 2/skyfall-36b-v2": { - "id": "TheDrummer 2/skyfall-36b-v2", - "name": "TheDrummer Skyfall 36B V2", - "family": "llama", - "attachment": true, + "canopylabs/orpheus-arabic-saudi": { + "id": "canopylabs/orpheus-arabic-saudi", + "name": "Orpheus Arabic Saudi", + "family": "canopylabs", + "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-03-10", - "last_updated": "2025-03-10", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-12-16", + "release_date": "2025-12-16", + "last_updated": "2025-12-16", + "modalities": { + "input": ["text"], + "output": ["audio"] + }, "open_weights": false, - "cost": { "input": 0.49299999999999994, "output": 0.49299999999999994 }, - "limit": { "context": 64000, "input": 64000, "output": 32768 } + "limit": { + "context": 4000, + "output": 50000 + }, + "cost": { + "input": 40, + "output": 0 + } }, - "TheDrummer 2/Anubis-70B-v1": { - "id": "TheDrummer 2/Anubis-70B-v1", - "name": "Anubis 70B v1", - "family": "llama", + "canopylabs/orpheus-v1-english": { + "id": "canopylabs/orpheus-v1-english", + "name": "Orpheus V1 English", + "family": "canopylabs", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2024-07-01", - "last_updated": "2024-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-12-19", + "release_date": "2025-12-19", + "last_updated": "2025-12-19", + "modalities": { + "input": ["text"], + "output": ["audio"] + }, "open_weights": false, - "cost": { "input": 0.31, "output": 0.31 }, - "limit": { "context": 65536, "input": 65536, "output": 16384 } + "limit": { + "context": 4000, + "output": 50000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "TheDrummer 2/Cydonia-24B-v2": { - "id": "TheDrummer 2/Cydonia-24B-v2", - "name": "The Drummer Cydonia 24B v2", + "meta-llama/llama-4-scout-17b-16e-instruct": { + "id": "meta-llama/llama-4-scout-17b-16e-instruct", + "name": "Llama 4 Scout 17B", "family": "llama", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1003, "output": 0.1207 }, - "limit": { "context": 16384, "input": 16384, "output": 32768 } + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2024-08", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.11, + "output": 0.34 + } }, - "TheDrummer 2/Cydonia-24B-v4": { - "id": "TheDrummer 2/Cydonia-24B-v4", - "name": "The Drummer Cydonia 24B v4", + "meta-llama/llama-prompt-guard-2-22m": { + "id": "meta-llama/llama-prompt-guard-2-22m", + "name": "Llama Prompt Guard 2 22M", "family": "llama", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-07-22", - "last_updated": "2025-07-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2006, "output": 0.2414 }, - "limit": { "context": 16384, "input": 16384, "output": 32768 } + "temperature": true, + "knowledge": "2024-10", + "release_date": "2024-10-01", + "last_updated": "2024-10-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 512, + "output": 512 + }, + "cost": { + "input": 0.03, + "output": 0.03 + } }, - "TheDrummer 2/Anubis-70B-v1.1": { - "id": "TheDrummer 2/Anubis-70B-v1.1", - "name": "Anubis 70B v1.1", + "meta-llama/llama-guard-4-12b": { + "id": "meta-llama/llama-guard-4-12b", + "name": "Llama Guard 4 12B", "family": "llama", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2024-07-01", - "last_updated": "2024-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.31, "output": 0.31 }, - "limit": { "context": 131072, "input": 131072, "output": 16384 } + "temperature": true, + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 1024 + }, + "status": "deprecated", + "cost": { + "input": 0.2, + "output": 0.2 + } }, - "TheDrummer 2/Magidonia-24B-v4.3": { - "id": "TheDrummer 2/Magidonia-24B-v4.3", - "name": "The Drummer Magidonia 24B v4.3", + "meta-llama/llama-4-maverick-17b-128e-instruct": { + "id": "meta-llama/llama-4-maverick-17b-128e-instruct", + "name": "Llama 4 Maverick 17B", "family": "llama", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-25", - "last_updated": "2025-12-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1003, "output": 0.1207 }, - "limit": { "context": 32768, "input": 32768, "output": 32768 } + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2024-08", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "status": "deprecated", + "cost": { + "input": 0.2, + "output": 0.6 + } }, - "TheDrummer 2/Rocinante-12B-v1.1": { - "id": "TheDrummer 2/Rocinante-12B-v1.1", - "name": "Rocinante 12b", + "meta-llama/llama-prompt-guard-2-86m": { + "id": "meta-llama/llama-prompt-guard-2-86m", + "name": "Llama Prompt Guard 2 86M", "family": "llama", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2024-07-01", - "last_updated": "2024-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.408, "output": 0.595 }, - "limit": { "context": 16384, "input": 16384, "output": 8192 } + "temperature": true, + "knowledge": "2024-10", + "release_date": "2024-10-01", + "last_updated": "2024-10-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 512, + "output": 512 + }, + "cost": { + "input": 0.04, + "output": 0.04 + } }, - "google/gemini-3-flash-preview-thinking": { - "id": "google/gemini-3-flash-preview-thinking", - "name": "Gemini 3 Flash Thinking", - "family": "gemini-flash", - "attachment": true, + "openai/gpt-oss-20b": { + "id": "openai/gpt-oss-20b", + "name": "GPT OSS 20B", + "family": "gpt-oss", + "attachment": false, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 3 }, - "limit": { "context": 1048756, "input": 1048756, "output": 65536 } + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.075, + "output": 0.3 + } }, - "google/gemini-3-flash-preview": { - "id": "google/gemini-3-flash-preview", - "name": "Gemini 3 Flash (Preview)", - "family": "gemini-flash", - "attachment": true, + "openai/gpt-oss-safeguard-20b": { + "id": "openai/gpt-oss-safeguard-20b", + "name": "Safety GPT OSS 20B", + "family": "gpt-oss", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-03-05", + "last_updated": "2025-03-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.075, + "output": 0.3, + "cache_read": 0.037 + } + }, + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "name": "GPT OSS 120B", + "family": "gpt-oss", + "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 3 }, - "limit": { "context": 1048756, "input": 1048756, "output": 65536 } + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "google/gemini-flash-1.5": { - "id": "google/gemini-flash-1.5", - "name": "Gemini 1.5 Flash", - "family": "gemini-flash", + "qwen/qwen3-32b": { + "id": "qwen/qwen3-32b", + "name": "Qwen3 32B", + "family": "qwen", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-05-14", - "last_updated": "2024-05-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.0748, "output": 0.306 }, - "limit": { "context": 2000000, "input": 2000000, "output": 8192 } + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-11-08", + "release_date": "2024-12-23", + "last_updated": "2024-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 40960 + }, + "cost": { + "input": 0.29, + "output": 0.59 + } }, - "undi95/remm-slerp-l2-13b": { - "id": "undi95/remm-slerp-l2-13b", - "name": "ReMM SLERP 13B", - "family": "llama", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, + "groq/compound": { + "id": "groq/compound", + "name": "Compound", + "family": "groq", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-09-04", + "release_date": "2025-09-04", + "last_updated": "2025-09-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.7989999999999999, "output": 1.2069999999999999 }, - "limit": { "context": 6144, "input": 6144, "output": 4096 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "amazon/nova-lite-v1": { - "id": "amazon/nova-lite-v1", - "name": "Amazon Nova Lite 1.0", - "family": "nova-lite", + "groq/compound-mini": { + "id": "groq/compound-mini", + "name": "Compound Mini", + "family": "groq", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-03", - "last_updated": "2024-12-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-09-04", + "release_date": "2025-09-04", + "last_updated": "2025-09-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.0595, "output": 0.238 }, - "limit": { "context": 300000, "input": 300000, "output": 5120 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "amazon/nova-2-lite-v1": { - "id": "amazon/nova-2-lite-v1", - "name": "Amazon Nova 2 Lite", - "family": "nova", + "moonshotai/kimi-k2-instruct": { + "id": "moonshotai/kimi-k2-instruct", + "name": "Kimi K2 Instruct", + "family": "kimi", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-03", - "last_updated": "2024-12-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5099999999999999, "output": 4.25 }, - "limit": { "context": 1000000, "input": 1000000, "output": 65535 } + "tool_call": true, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-07-14", + "last_updated": "2025-07-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "status": "deprecated", + "cost": { + "input": 1, + "output": 3 + } }, - "amazon/nova-micro-v1": { - "id": "amazon/nova-micro-v1", - "name": "Amazon Nova Micro 1.0", - "family": "nova-micro", + "moonshotai/kimi-k2-instruct-0905": { + "id": "moonshotai/kimi-k2-instruct-0905", + "name": "Kimi K2 Instruct 0905", + "family": "kimi", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-03", - "last_updated": "2024-12-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.0357, "output": 0.1394 }, - "limit": { "context": 128000, "input": 128000, "output": 5120 } - }, - "amazon/nova-pro-v1": { - "id": "amazon/nova-pro-v1", - "name": "Amazon Nova Pro 1.0", - "family": "nova-pro", + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 16384 + }, + "cost": { + "input": 1, + "output": 3 + } + } + } + }, + "azure": { + "id": "azure", + "env": ["AZURE_RESOURCE_NAME", "AZURE_API_KEY"], + "npm": "@ai-sdk/azure", + "name": "Azure", + "doc": "https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models", + "models": { + "mistral-nemo": { + "id": "mistral-nemo", + "name": "Mistral Nemo", + "family": "mistral-nemo", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-03", - "last_updated": "2024-12-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-07", + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.15, + "output": 0.15 + } + }, + "gpt-5.2-chat": { + "id": "gpt-5.2-chat", + "name": "GPT-5.2 Chat", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.7989999999999999, "output": 3.1959999999999997 }, - "limit": { "context": 300000, "input": 300000, "output": 32000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "baidu/ernie-4.5-300b-a47b": { - "id": "baidu/ernie-4.5-300b-a47b", - "name": "ERNIE 4.5 300B", - "family": "ernie", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-06-30", - "last_updated": "2025-06-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "codex-mini": { + "id": "codex-mini", + "name": "Codex Mini", + "family": "gpt-codex-mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2024-04", + "release_date": "2025-05-16", + "last_updated": "2025-05-16", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.35, "output": 1.15 }, - "limit": { "context": 131072, "input": 131072, "output": 16384 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.5, + "output": 6, + "cache_read": 0.375 + } }, - "baidu/ernie-4.5-vl-28b-a3b": { - "id": "baidu/ernie-4.5-vl-28b-a3b", - "name": "ERNIE 4.5 VL 28B", - "family": "ernie", + "phi-4-multimodal": { + "id": "phi-4-multimodal", + "name": "Phi-4-multimodal", + "family": "phi", "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-06-30", - "last_updated": "2025-06-30", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.13999999999999999, "output": 0.5599999999999999 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "temperature": true, + "knowledge": "2023-10", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.08, + "output": 0.32, + "input_audio": 4 + } + }, + "phi-3.5-mini-instruct": { + "id": "phi-3.5-mini-instruct", + "name": "Phi-3.5-mini-instruct", + "family": "phi", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "knowledge": "2023-10", + "release_date": "2024-08-20", + "last_updated": "2024-08-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.13, + "output": 0.52 + } }, - "meta-llama/llama-3.3-70b-instruct": { - "id": "meta-llama/llama-3.3-70b-instruct", - "name": "Llama 3.3 70b Instruct", + "llama-4-scout-17b-16e-instruct": { + "id": "llama-4-scout-17b-16e-instruct", + "name": "Llama 4 Scout 17B 16E Instruct", "family": "llama", - "attachment": false, + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, - "release_date": "2025-02-27", - "last_updated": "2025-02-27", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-08", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.2, + "output": 0.78 + } + }, + "grok-4-1-fast-reasoning": { + "id": "grok-4-1-fast-reasoning", + "name": "Grok 4.1 Fast (Reasoning)", + "family": "grok", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-06-27", + "last_updated": "2025-06-27", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.05, "output": 0.23 }, - "limit": { "context": 131072, "input": 131072, "output": 16384 } + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "status": "beta", + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "meta-llama/llama-3.1-8b-instruct": { - "id": "meta-llama/llama-3.1-8b-instruct", - "name": "Llama 3.1 8b Instruct", - "family": "llama", + "phi-3-medium-4k-instruct": { + "id": "phi-3-medium-4k-instruct", + "name": "Phi-3-medium-instruct (4k)", + "family": "phi", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.0544, "output": 0.0544 }, - "limit": { "context": 131072, "input": 131072, "output": 16384 } + "temperature": true, + "knowledge": "2023-10", + "release_date": "2024-04-23", + "last_updated": "2024-04-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 4096, + "output": 1024 + }, + "cost": { + "input": 0.17, + "output": 0.68 + } }, - "meta-llama/llama-4-scout": { - "id": "meta-llama/llama-4-scout", - "name": "Llama 4 Scout", - "family": "llama", - "attachment": true, + "ministral-3b": { + "id": "ministral-3b", + "name": "Ministral 3B", + "family": "ministral", + "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.085, "output": 0.46 }, - "limit": { "context": 328000, "input": 328000, "output": 65536 } + "temperature": true, + "knowledge": "2024-03", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.04, + "output": 0.04 + } }, - "meta-llama/llama-4-maverick": { - "id": "meta-llama/llama-4-maverick", - "name": "Llama 4 Maverick", - "family": "llama", + "claude-haiku-4-5": { + "id": "claude-haiku-4-5", + "name": "Claude Haiku 4.5", + "family": "claude-haiku", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-02-31", + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.18000000000000002, "output": 0.8 }, - "limit": { "context": 1048576, "input": 1048576, "output": 65536 } + "limit": { + "context": 200000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } }, - "meta-llama/llama-3.2-90b-vision-instruct": { - "id": "meta-llama/llama-3.2-90b-vision-instruct", - "name": "Llama 3.2 Medium", + "meta-llama-3.1-8b-instruct": { + "id": "meta-llama-3.1-8b-instruct", + "name": "Meta-Llama-3.1-8B-Instruct", "family": "llama", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.9009999999999999, "output": 0.9009999999999999 }, - "limit": { "context": 131072, "input": 131072, "output": 16384 } - }, - "meta-llama/llama-3.2-3b-instruct": { - "id": "meta-llama/llama-3.2-3b-instruct", - "name": "Llama 3.2 3b Instruct", - "family": "llama", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.0306, "output": 0.0493 }, - "limit": { "context": 131072, "input": 131072, "output": 8192 } + "tool_call": true, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.3, + "output": 0.61 + } }, - "ReadyArt/MS3.2-The-Omega-Directive-24B-Unslop-v2.0": { - "id": "ReadyArt/MS3.2-The-Omega-Directive-24B-Unslop-v2.0", - "name": "Omega Directive 24B Unslop v2.0", - "family": "llama", + "kimi-k2.5": { + "id": "kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-08", - "last_updated": "2025-12-08", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 0.5 }, - "limit": { "context": 16384, "input": 16384, "output": 32768 } + "reasoning": true, + "tool_call": true, + "interleaved": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-02-06", + "last_updated": "2026-02-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible", + "api": "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/models", + "shape": "completions" + }, + "cost": { + "input": 0.6, + "output": 3 + } }, - "ReadyArt/The-Omega-Abomination-L-70B-v1.0": { - "id": "ReadyArt/The-Omega-Abomination-L-70B-v1.0", - "name": "The Omega Abomination V1", + "llama-3.3-70b-instruct": { + "id": "llama-3.3-70b-instruct", + "name": "Llama-3.3-70B-Instruct", "family": "llama", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.7, "output": 0.95 }, - "limit": { "context": 16384, "input": 16384, "output": 16384 } + "tool_call": true, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.71, + "output": 0.71 + } }, - "miromind-ai/mirothinker-v1.5-235b": { - "id": "miromind-ai/mirothinker-v1.5-235b", - "name": "MiroThinker v1.5 235B", - "family": "gpt", + "deepseek-v3-0324": { + "id": "deepseek-v3-0324", + "name": "DeepSeek-V3-0324", + "family": "deepseek", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2026-01-07", - "last_updated": "2026-01-07", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 32768, "input": 32768, "output": 4000 } + "tool_call": true, + "temperature": true, + "knowledge": "2024-07", + "release_date": "2025-03-24", + "last_updated": "2025-03-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 1.14, + "output": 4.56 + } }, - "Doctor-Shotgun/MS3.2-24B-Magnum-Diamond": { - "id": "Doctor-Shotgun/MS3.2-24B-Magnum-Diamond", - "name": "MS3.2 24B Magnum Diamond", - "family": "mistral", - "attachment": false, - "reasoning": false, + "gpt-5-chat": { + "id": "gpt-5-chat", + "name": "GPT-5 Chat", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, "tool_call": false, - "structured_output": false, - "release_date": "2025-11-24", - "last_updated": "2025-11-24", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-10-24", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.49299999999999994, "output": 0.49299999999999994 }, - "limit": { "context": 16384, "input": 16384, "output": 32768 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.13 + } }, - "LLM360/K2-Think": { - "id": "LLM360/K2-Think", - "name": "K2-Think", - "family": "kimi-thinking", + "phi-3.5-moe-instruct": { + "id": "phi-3.5-moe-instruct", + "name": "Phi-3.5-MoE-instruct", + "family": "phi", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-07-26", - "last_updated": "2025-07-26", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2023-10", + "release_date": "2024-08-20", + "last_updated": "2024-08-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.16, + "output": 0.64 + } + }, + "gpt-5.3-chat": { + "id": "gpt-5.3-chat", + "name": "GPT-5.3 Chat", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-03", + "last_updated": "2026-03-03", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.17, "output": 0.68 }, - "limit": { "context": 128000, "input": 128000, "output": 32768 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "MiniMaxAI/MiniMax-M1-80k": { - "id": "MiniMaxAI/MiniMax-M1-80k", - "name": "MiniMax M1 80K", - "family": "minimax", + "o1-mini": { + "id": "o1-mini", + "name": "o1-mini", + "family": "o-mini", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-06-16", - "last_updated": "2025-06-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2023-09", + "release_date": "2024-09-12", + "last_updated": "2024-09-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6052, "output": 2.4225000000000003 }, - "limit": { "context": 1000000, "input": 1000000, "output": 131072 } + "limit": { + "context": 128000, + "output": 65536 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.55 + } }, - "failspy/Meta-Llama-3-70B-Instruct-abliterated-v3.5": { - "id": "failspy/Meta-Llama-3-70B-Instruct-abliterated-v3.5", - "name": "Llama 3 70B abliterated", - "family": "llama", + "text-embedding-3-large": { + "id": "text-embedding-3-large", + "name": "text-embedding-3-large", + "family": "text-embedding", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-07-26", - "last_updated": "2025-07-26", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-01-25", + "last_updated": "2024-01-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.7, "output": 0.7 }, - "limit": { "context": 8192, "input": 8192, "output": 8192 } + "limit": { + "context": 8191, + "output": 3072 + }, + "cost": { + "input": 0.13, + "output": 0 + } }, - "essentialai/rnj-1-instruct": { - "id": "essentialai/rnj-1-instruct", - "name": "RNJ-1 Instruct 8B", - "family": "rnj", + "phi-3-mini-128k-instruct": { + "id": "phi-3-mini-128k-instruct", + "name": "Phi-3-mini-instruct (128k)", + "family": "phi", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-12-13", - "last_updated": "2025-12-13", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.15 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 } + "temperature": true, + "knowledge": "2023-10", + "release_date": "2024-04-23", + "last_updated": "2024-04-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.13, + "output": 0.52 + } }, - "pamanseau/OpenReasoning-Nemotron-32B": { - "id": "pamanseau/OpenReasoning-Nemotron-32B", - "name": "OpenReasoning Nemotron 32B", - "family": "nemotron", + "phi-4-reasoning": { + "id": "phi-4-reasoning", + "name": "Phi-4-reasoning", + "family": "phi", "attachment": false, "reasoning": true, "tool_call": false, - "structured_output": false, - "release_date": "2025-08-21", - "last_updated": "2025-08-21", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 32768, "input": 32768, "output": 65536 } - }, - "arcee-ai/trinity-mini": { - "id": "arcee-ai/trinity-mini", - "name": "Trinity Mini", - "family": "trinity-mini", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.045000000000000005, "output": 0.15 }, - "limit": { "context": 131072, "input": 131072, "output": 8192 } + "temperature": true, + "knowledge": "2023-10", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32000, + "output": 4096 + }, + "cost": { + "input": 0.125, + "output": 0.5 + } }, - "arcee-ai/trinity-large": { - "id": "arcee-ai/trinity-large", - "name": "Trinity Large", - "family": "trinity", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "gpt-5-mini": { + "id": "gpt-5-mini", + "name": "GPT-5 Mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 1 }, - "limit": { "context": 131072, "input": 131072, "output": 8192 } + "limit": { + "context": 272000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.03 + } }, - "deepseek-ai/DeepSeek-V3.1-Terminus:thinking": { - "id": "deepseek-ai/DeepSeek-V3.1-Terminus:thinking", - "name": "DeepSeek V3.1 Terminus (Thinking)", - "family": "deepseek-thinking", - "attachment": false, - "reasoning": false, + "gpt-5-nano": { + "id": "gpt-5-nano", + "name": "GPT-5 Nano", + "family": "gpt-nano", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-09-22", - "last_updated": "2025-09-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 0.7 }, - "limit": { "context": 128000, "input": 128000, "output": 65536 } + "limit": { + "context": 272000, + "output": 128000 + }, + "cost": { + "input": 0.05, + "output": 0.4, + "cache_read": 0.01 + } }, - "deepseek-ai/DeepSeek-V3.1:thinking": { - "id": "deepseek-ai/DeepSeek-V3.1:thinking", - "name": "DeepSeek V3.1 Thinking", - "family": "deepseek-thinking", + "meta-llama-3-70b-instruct": { + "id": "meta-llama-3-70b-instruct", + "name": "Meta-Llama-3-70B-Instruct", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-08-21", - "last_updated": "2025-08-21", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.7 }, - "limit": { "context": 128000, "input": 128000, "output": 65536 } - }, - "deepseek-ai/DeepSeek-V3.1": { - "id": "deepseek-ai/DeepSeek-V3.1", - "name": "DeepSeek V3.1", - "family": "deepseek", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-07-26", - "last_updated": "2025-07-26", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.7 }, - "limit": { "context": 128000, "input": 128000, "output": 65536 } + "temperature": true, + "knowledge": "2023-12", + "release_date": "2024-04-18", + "last_updated": "2024-04-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 2048 + }, + "cost": { + "input": 2.68, + "output": 3.54 + } }, - "deepseek-ai/deepseek-v3.2-exp-thinking": { - "id": "deepseek-ai/deepseek-v3.2-exp-thinking", - "name": "DeepSeek V3.2 Exp Thinking", - "family": "deepseek-thinking", + "phi-3-small-8k-instruct": { + "id": "phi-3-small-8k-instruct", + "name": "Phi-3-small-instruct (8k)", + "family": "phi", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.27999999999999997, "output": 0.42000000000000004 }, - "limit": { "context": 163840, "input": 163840, "output": 65536 } - }, - "deepseek-ai/DeepSeek-V3.1-Terminus": { - "id": "deepseek-ai/DeepSeek-V3.1-Terminus", - "name": "DeepSeek V3.1 Terminus", - "family": "deepseek", + "temperature": true, + "knowledge": "2023-10", + "release_date": "2024-04-23", + "last_updated": "2024-04-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 2048 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } + }, + "gpt-5.3-codex": { + "id": "gpt-5.3-codex", + "name": "GPT-5.3 Codex", + "family": "gpt-codex", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-08-02", - "last_updated": "2025-08-02", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-02-24", + "last_updated": "2026-02-24", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 0.7 }, - "limit": { "context": 128000, "input": 128000, "output": 65536 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "deepseek-ai/deepseek-v3.2-exp": { - "id": "deepseek-ai/deepseek-v3.2-exp", - "name": "DeepSeek V3.2 Exp", - "family": "deepseek", + "text-embedding-ada-002": { + "id": "text-embedding-ada-002", + "name": "text-embedding-ada-002", + "family": "text-embedding", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2022-12-15", + "last_updated": "2022-12-15", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.27999999999999997, "output": 0.42000000000000004 }, - "limit": { "context": 163840, "input": 163840, "output": 65536 } + "limit": { + "context": 8192, + "output": 1536 + }, + "cost": { + "input": 0.1, + "output": 0 + } }, - "deepseek-ai/DeepSeek-R1-0528": { - "id": "deepseek-ai/DeepSeek-R1-0528", - "name": "DeepSeek R1 0528", - "family": "deepseek", + "llama-3.2-90b-vision-instruct": { + "id": "llama-3.2-90b-vision-instruct", + "name": "Llama-3.2-90B-Vision-Instruct", + "family": "llama", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2024-09-25", + "last_updated": "2024-09-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 2.04, + "output": 2.04 + } + }, + "deepseek-r1": { + "id": "deepseek-r1", + "name": "DeepSeek-R1", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, "tool_call": false, - "structured_output": false, - "release_date": "2025-05-28", - "last_updated": "2025-05-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 1.7 }, - "limit": { "context": 128000, "input": 128000, "output": 163840 } + "temperature": true, + "knowledge": "2024-07", + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 163840 + }, + "cost": { + "input": 1.35, + "output": 5.4 + } }, - "inflatebot/MN-12B-Mag-Mell-R1": { - "id": "inflatebot/MN-12B-Mag-Mell-R1", - "name": "Mag Mell R1", - "family": "mistral-nemo", - "attachment": false, + "grok-4-1-fast-non-reasoning": { + "id": "grok-4-1-fast-non-reasoning", + "name": "Grok 4.1 Fast (Non-Reasoning)", + "family": "grok", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-07-01", - "last_updated": "2024-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "release_date": "2025-06-27", + "last_updated": "2025-06-27", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.49299999999999994, "output": 0.49299999999999994 }, - "limit": { "context": 16384, "input": 16384, "output": 8192 } + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + }, + "status": "beta", + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "MarinaraSpaghetti/NemoMix-Unleashed-12B": { - "id": "MarinaraSpaghetti/NemoMix-Unleashed-12B", - "name": "NemoMix 12B Unleashed", - "family": "mistral-nemo", + "deepseek-v3.2-speciale": { + "id": "deepseek-v3.2-speciale", + "name": "DeepSeek-V3.2-Speciale", + "family": "deepseek", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": false, - "structured_output": false, - "release_date": "2024-07-01", - "last_updated": "2024-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-07", + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.58, + "output": 1.68 + } + }, + "gpt-5.2": { + "id": "gpt-5.2", + "name": "GPT-5.2", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.49299999999999994, "output": 0.49299999999999994 }, - "limit": { "context": 32768, "input": 32768, "output": 8192 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.125 + } }, - "Alibaba-NLP/Tongyi-DeepResearch-30B-A3B": { - "id": "Alibaba-NLP/Tongyi-DeepResearch-30B-A3B", - "name": "Tongyi DeepResearch 30B A3B", - "family": "yi", + "mistral-large-2411": { + "id": "mistral-large-2411", + "name": "Mistral Large 24.11", + "family": "mistral-large", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-08-26", - "last_updated": "2025-08-26", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-09", + "release_date": "2024-11-01", + "last_updated": "2024-11-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.08, "output": 0.24000000000000002 }, - "limit": { "context": 128000, "input": 128000, "output": 65536 } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 6 + } }, - "soob3123/GrayLine-Qwen3-8B": { - "id": "soob3123/GrayLine-Qwen3-8B", - "name": "Grayline Qwen3 8B", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "claude-opus-4-1": { + "id": "claude-opus-4-1", + "name": "Claude Opus 4.1", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 0.3 }, - "limit": { "context": 16384, "input": 16384, "output": 32768 } + "limit": { + "context": 200000, + "output": 32000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "soob3123/amoral-gemma3-27B-v2": { - "id": "soob3123/amoral-gemma3-27B-v2", - "name": "Amoral Gemma3 27B v2", - "family": "gemma", + "cohere-command-a": { + "id": "cohere-command-a", + "name": "Command A", + "family": "command-a", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-05-23", - "last_updated": "2025-05-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 0.3 }, - "limit": { "context": 32768, "input": 32768, "output": 8192 } + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-06-01", + "release_date": "2025-03-13", + "last_updated": "2025-03-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 8000 + }, + "cost": { + "input": 2.5, + "output": 10 + } }, - "soob3123/Veiled-Calla-12B": { - "id": "soob3123/Veiled-Calla-12B", - "name": "Veiled Calla 12B", + "llama-3.2-11b-vision-instruct": { + "id": "llama-3.2-11b-vision-instruct", + "name": "Llama-3.2-11B-Vision-Instruct", "family": "llama", - "attachment": false, + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-13", - "last_updated": "2025-04-13", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 0.3 }, - "limit": { "context": 32768, "input": 32768, "output": 8192 } + "tool_call": true, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2024-09-25", + "last_updated": "2024-09-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.37, + "output": 0.37 + } }, - "minimax/minimax-m2-her": { - "id": "minimax/minimax-m2-her", - "name": "MiniMax M2-her", - "family": "minimax", + "meta-llama-3.1-405b-instruct": { + "id": "meta-llama-3.1-405b-instruct", + "name": "Meta-Llama-3.1-405B-Instruct", + "family": "llama", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2026-01-24", - "last_updated": "2026-01-24", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.30200000000000005, "output": 1.2069999999999999 }, - "limit": { "context": 65532, "input": 65532, "output": 2048 } + "tool_call": true, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 5.33, + "output": 16 + } }, - "minimax/minimax-01": { - "id": "minimax/minimax-01", - "name": "MiniMax 01", - "family": "minimax", + "gpt-5.1-chat": { + "id": "gpt-5.1-chat", + "name": "GPT-5.1 Chat", + "family": "gpt-codex", "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-01-15", - "last_updated": "2025-01-15", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1394, "output": 1.1219999999999999 }, - "limit": { "context": 1000192, "input": 1000192, "output": 16384 } - }, - "minimax/minimax-m2.1": { - "id": "minimax/minimax-m2.1", - "name": "MiniMax M2.1", - "family": "minimax", - "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-12-19", - "last_updated": "2025-12-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-14", + "last_updated": "2025-11-14", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text", "image", "audio"] + }, "open_weights": false, - "cost": { "input": 0.33, "output": 1.32 }, - "limit": { "context": 200000, "input": 200000, "output": 131072 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "minimax/minimax-m2.7": { - "id": "minimax/minimax-m2.7", - "name": "MiniMax M2.7", - "family": "minimax", - "attachment": false, - "reasoning": true, + "gpt-4-turbo-vision": { + "id": "gpt-4-turbo-vision", + "name": "GPT-4 Turbo Vision", + "family": "gpt", + "attachment": true, + "reasoning": false, "tool_call": true, - "structured_output": true, - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2023-11", + "release_date": "2023-11-06", + "last_updated": "2024-04-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 204800, "input": 204800, "output": 131072 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 10, + "output": 30 + } }, - "minimax/minimax-m2.5": { - "id": "minimax/minimax-m2.5", - "name": "MiniMax M2.5", - "family": "minimax", + "gpt-5.2-codex": { + "id": "gpt-5.2-codex", + "name": "GPT-5.2 Codex", + "family": "gpt-codex", "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-01-14", + "last_updated": "2026-01-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 204800, "input": 204800, "output": 131072 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.75, + "output": 14, + "cache_read": 0.175 + } }, - "qwen/qwen3.5-397b-a17b": { - "id": "qwen/qwen3.5-397b-a17b", - "name": "Qwen3.5 397B A17B", - "family": "qwen", - "attachment": false, + "cohere-embed-v-4-0": { + "id": "cohere-embed-v-4-0", + "name": "Embed v4", + "family": "cohere-embed", + "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2026-02-16", - "last_updated": "2026-02-16", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "temperature": false, + "release_date": "2025-04-15", + "last_updated": "2025-04-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 3.6 }, - "limit": { "context": 258048, "input": 258048, "output": 65536 } + "limit": { + "context": 128000, + "output": 1536 + }, + "cost": { + "input": 0.12, + "output": 0 + } }, - "dmind/dmind-1": { - "id": "dmind/dmind-1", - "name": "DMind-1", - "family": "gpt", + "gpt-5.1-codex-mini": { + "id": "gpt-5.1-codex-mini", + "name": "GPT-5.1 Codex Mini", + "family": "gpt-codex", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-06-01", - "last_updated": "2025-06-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-14", + "last_updated": "2025-11-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 0.6 }, - "limit": { "context": 32768, "input": 32768, "output": 8192 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.025 + } }, - "dmind/dmind-1-mini": { - "id": "dmind/dmind-1-mini", - "name": "DMind-1-Mini", + "gpt-3.5-turbo-0125": { + "id": "gpt-3.5-turbo-0125", + "name": "GPT-3.5 Turbo 0125", "family": "gpt", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-06-01", - "last_updated": "2025-06-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2021-08", + "release_date": "2024-01-25", + "last_updated": "2024-01-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.4 }, - "limit": { "context": 32768, "input": 32768, "output": 8192 } + "limit": { + "context": 16384, + "output": 16384 + }, + "cost": { + "input": 0.5, + "output": 1.5 + } }, - "VongolaChouko/Starcannon-Unleashed-12B-v1.0": { - "id": "VongolaChouko/Starcannon-Unleashed-12B-v1.0", - "name": "Mistral Nemo Starcannon 12b v1", - "family": "mistral-nemo", + "o1-preview": { + "id": "o1-preview", + "name": "o1-preview", + "family": "o", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-07-01", - "last_updated": "2024-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2023-09", + "release_date": "2024-09-12", + "last_updated": "2024-09-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.49299999999999994, "output": 0.49299999999999994 }, - "limit": { "context": 16384, "input": 16384, "output": 8192 } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 16.5, + "output": 66, + "cache_read": 8.25 + } }, - "xiaomi/mimo-v2-flash-thinking": { - "id": "xiaomi/mimo-v2-flash-thinking", - "name": "MiMo V2 Flash (Thinking)", - "family": "mimo", + "cohere-embed-v3-multilingual": { + "id": "cohere-embed-v3-multilingual", + "name": "Embed v3 Multilingual", + "family": "cohere-embed", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.102, "output": 0.306 }, - "limit": { "context": 256000, "input": 256000, "output": 32768 } + "temperature": false, + "release_date": "2023-11-07", + "last_updated": "2023-11-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 512, + "output": 1024 + }, + "cost": { + "input": 0.1, + "output": 0 + } }, - "xiaomi/mimo-v2-flash-thinking-original": { - "id": "xiaomi/mimo-v2-flash-thinking-original", - "name": "MiMo V2 Flash (Thinking) Original", - "family": "mimo", + "grok-4-20-non-reasoning": { + "id": "grok-4-20-non-reasoning", + "name": "Grok 4.20 (Non-Reasoning)", + "family": "grok", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2025-09", + "release_date": "2026-04-08", + "last_updated": "2026-04-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.102, "output": 0.306 }, - "limit": { "context": 256000, "input": 256000, "output": 32768 } + "limit": { + "context": 262000, + "output": 8192 + }, + "status": "beta", + "cost": { + "input": 2, + "output": 6 + } }, - "xiaomi/mimo-v2-flash-original": { - "id": "xiaomi/mimo-v2-flash-original", - "name": "MiMo V2 Flash Original", - "family": "mimo", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "gpt-5.1": { + "id": "gpt-5.1", + "name": "GPT-5.1", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-14", + "last_updated": "2025-11-14", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text", "image", "audio"] + }, + "open_weights": false, + "limit": { + "context": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } + }, + "grok-4-fast-reasoning": { + "id": "grok-4-fast-reasoning", + "name": "Grok 4 Fast (Reasoning)", + "family": "grok", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-07", + "release_date": "2025-09-19", + "last_updated": "2025-09-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.102, "output": 0.306 }, - "limit": { "context": 256000, "input": 256000, "output": 32768 } + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } }, - "xiaomi/mimo-v2-flash": { - "id": "xiaomi/mimo-v2-flash", - "name": "MiMo V2 Flash", - "family": "mimo", + "o1": { + "id": "o1", + "name": "o1", + "family": "o", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2023-09", + "release_date": "2024-12-05", + "last_updated": "2024-12-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.102, "output": 0.306 }, - "limit": { "context": 256000, "input": 256000, "output": 32768 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 15, + "output": 60, + "cache_read": 7.5 + } }, - "Salesforce/Llama-xLAM-2-70b-fc-r": { - "id": "Salesforce/Llama-xLAM-2-70b-fc-r", - "name": "Llama-xLAM-2 70B fc-r", - "family": "llama", - "attachment": false, + "mistral-small-2503": { + "id": "mistral-small-2503", + "name": "Mistral Small 3.1", + "family": "mistral-small", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-13", - "last_updated": "2025-04-13", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-09", + "release_date": "2025-03-01", + "last_updated": "2025-03-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.5, "output": 2.5 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.3 + } }, - "Gryphe/MythoMax-L2-13b": { - "id": "Gryphe/MythoMax-L2-13b", - "name": "MythoMax 13B", - "family": "llama", - "attachment": false, + "model-router": { + "id": "model-router", + "name": "Model Router", + "family": "model-router", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-08-08", - "last_updated": "2025-08-08", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "release_date": "2025-05-19", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1003, "output": 0.1003 }, - "limit": { "context": 4000, "input": 4000, "output": 4096 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.14, + "output": 0 + } }, - "baseten/Kimi-K2-Instruct-FP4": { - "id": "baseten/Kimi-K2-Instruct-FP4", - "name": "Kimi K2 0711 Instruct FP4", - "family": "kimi", + "gpt-3.5-turbo-1106": { + "id": "gpt-3.5-turbo-1106", + "name": "GPT-3.5 Turbo 1106", + "family": "gpt", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-07-11", - "last_updated": "2025-07-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2021-08", + "release_date": "2023-11-06", + "last_updated": "2023-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 2 }, - "limit": { "context": 128000, "input": 128000, "output": 131072 } + "limit": { + "context": 16384, + "output": 16384 + }, + "cost": { + "input": 1, + "output": 2 + } }, - "Steelskull/L3.3-Nevoria-R1-70b": { - "id": "Steelskull/L3.3-Nevoria-R1-70b", - "name": "Steelskull Nevoria R1 70b", - "family": "llama", + "text-embedding-3-small": { + "id": "text-embedding-3-small", + "name": "text-embedding-3-small", + "family": "text-embedding", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2024-01-25", + "last_updated": "2024-01-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.49299999999999994, "output": 0.49299999999999994 }, - "limit": { "context": 16384, "input": 16384, "output": 16384 } + "limit": { + "context": 8191, + "output": 1536 + }, + "cost": { + "input": 0.02, + "output": 0 + } }, - "Steelskull/L3.3-Electra-R1-70b": { - "id": "Steelskull/L3.3-Electra-R1-70b", - "name": "Steelskull Electra R1 70b", - "family": "llama", + "deepseek-v3.1": { + "id": "deepseek-v3.1", + "name": "DeepSeek-V3.1", + "family": "deepseek", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-07", + "release_date": "2025-08-21", + "last_updated": "2025-08-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.56, + "output": 1.68 + } + }, + "claude-opus-4-5": { + "id": "claude-opus-4-5", + "name": "Claude Opus 4.5", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-11-24", + "last_updated": "2025-08-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.69989, "output": 0.69989 }, - "limit": { "context": 16384, "input": 16384, "output": 16384 } + "limit": { + "context": 200000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "Steelskull/L3.3-Cu-Mai-R1-70b": { - "id": "Steelskull/L3.3-Cu-Mai-R1-70b", - "name": "Llama 3.3 70B Cu Mai", - "family": "llama", + "phi-3-mini-4k-instruct": { + "id": "phi-3-mini-4k-instruct", + "name": "Phi-3-mini-instruct (4k)", + "family": "phi", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.49299999999999994, "output": 0.49299999999999994 }, - "limit": { "context": 16384, "input": 16384, "output": 16384 } + "temperature": true, + "knowledge": "2023-10", + "release_date": "2024-04-23", + "last_updated": "2024-04-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 4096, + "output": 1024 + }, + "cost": { + "input": 0.13, + "output": 0.52 + } }, - "Steelskull/L3.3-MS-Evalebis-70b": { - "id": "Steelskull/L3.3-MS-Evalebis-70b", - "name": "MS Evalebis 70b", + "meta-llama-3.1-70b-instruct": { + "id": "meta-llama-3.1-70b-instruct", + "name": "Meta-Llama-3.1-70B-Instruct", "family": "llama", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.49299999999999994, "output": 0.49299999999999994 }, - "limit": { "context": 16384, "input": 16384, "output": 16384 } + "tool_call": true, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 2.68, + "output": 3.54 + } }, - "Steelskull/L3.3-MS-Nevoria-70b": { - "id": "Steelskull/L3.3-MS-Nevoria-70b", - "name": "Steelskull Nevoria 70b", - "family": "llama", + "phi-4-mini-reasoning": { + "id": "phi-4-mini-reasoning", + "name": "Phi-4-mini-reasoning", + "family": "phi", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.49299999999999994, "output": 0.49299999999999994 }, - "limit": { "context": 16384, "input": 16384, "output": 16384 } + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2023-10", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.075, + "output": 0.3 + } }, - "Steelskull/L3.3-MS-Evayale-70B": { - "id": "Steelskull/L3.3-MS-Evayale-70B", - "name": "Evayale 70b ", - "family": "llama", + "gpt-4": { + "id": "gpt-4", + "name": "GPT-4", + "family": "gpt", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2023-11", + "release_date": "2023-03-14", + "last_updated": "2023-03-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.49299999999999994, "output": 0.49299999999999994 }, - "limit": { "context": 16384, "input": 16384, "output": 16384 } + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 60, + "output": 120 + } }, - "meganova-ai/manta-mini-1.0": { - "id": "meganova-ai/manta-mini-1.0", - "name": "Manta Mini 1.0", - "family": "nova", + "meta-llama-3-8b-instruct": { + "id": "meta-llama-3-8b-instruct", + "name": "Meta-Llama-3-8B-Instruct", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-12-20", - "last_updated": "2025-12-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.02, "output": 0.16 }, - "limit": { "context": 8192, "input": 8192, "output": 8192 } + "temperature": true, + "knowledge": "2023-12", + "release_date": "2024-04-18", + "last_updated": "2024-04-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 2048 + }, + "cost": { + "input": 0.3, + "output": 0.61 + } }, - "meganova-ai/manta-pro-1.0": { - "id": "meganova-ai/manta-pro-1.0", - "name": "Manta Pro 1.0", - "family": "nova", + "kimi-k2.6": { + "id": "kimi-k2.6", + "name": "Kimi K2.6", + "family": "kimi", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-20", - "last_updated": "2025-12-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.060000000000000005, "output": 0.5 }, - "limit": { "context": 32768, "input": 32768, "output": 32768 } + "reasoning": true, + "tool_call": true, + "interleaved": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible", + "api": "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/models", + "shape": "completions" + }, + "cost": { + "input": 0.95, + "output": 4 + } }, - "meganova-ai/manta-flash-1.0": { - "id": "meganova-ai/manta-flash-1.0", - "name": "Manta Flash 1.0", - "family": "nova", + "gpt-5-codex": { + "id": "gpt-5-codex", + "name": "GPT-5-Codex", + "family": "gpt-codex", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-20", - "last_updated": "2025-12-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-09-15", + "last_updated": "2025-09-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.02, "output": 0.16 }, - "limit": { "context": 16384, "input": 16384, "output": 16384 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.13 + } }, - "meituan-longcat/LongCat-Flash-Chat-FP8": { - "id": "meituan-longcat/LongCat-Flash-Chat-FP8", - "name": "LongCat Flash", - "family": "longcat", + "phi-4-mini": { + "id": "phi-4-mini", + "name": "Phi-4-mini", + "family": "phi", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, - "release_date": "2025-08-31", - "last_updated": "2025-08-31", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.7 }, - "limit": { "context": 128000, "input": 128000, "output": 32768 } + "temperature": true, + "knowledge": "2023-10", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.075, + "output": 0.3 + } }, - "TEE/kimi-k2-thinking": { - "id": "TEE/kimi-k2-thinking", - "name": "Kimi K2 Thinking TEE", - "family": "kimi-thinking", + "grok-4-20-reasoning": { + "id": "grok-4-20-reasoning", + "name": "Grok 4.20 (Reasoning)", + "family": "grok", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-11-06", - "last_updated": "2025-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-09", + "release_date": "2026-04-08", + "last_updated": "2026-04-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 2 }, - "limit": { "context": 128000, "input": 128000, "output": 65535 } + "limit": { + "context": 262000, + "output": 8192 + }, + "status": "beta", + "cost": { + "input": 2, + "output": 6 + } }, - "TEE/kimi-k2.5": { - "id": "TEE/kimi-k2.5", - "name": "Kimi K2.5 TEE", - "family": "kimi", + "gpt-3.5-turbo-0301": { + "id": "gpt-3.5-turbo-0301", + "name": "GPT-3.5 Turbo 0301", + "family": "gpt", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2026-01-29", - "last_updated": "2026-01-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2021-08", + "release_date": "2023-03-01", + "last_updated": "2023-03-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.9 }, - "limit": { "context": 128000, "input": 128000, "output": 65535 } + "limit": { + "context": 4096, + "output": 4096 + }, + "cost": { + "input": 1.5, + "output": 2 + } }, - "TEE/glm-4.7-flash": { - "id": "TEE/glm-4.7-flash", - "name": "GLM 4.7 Flash TEE", - "family": "glm-flash", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2026-01-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "claude-opus-4-6": { + "id": "claude-opus-4-6", + "name": "Claude Opus 4.6", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.5 }, - "limit": { "context": 203000, "input": 203000, "output": 65535 } + "limit": { + "context": 200000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25, + "tiers": [ + { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 10, + "output": 37.5, + "cache_read": 1, + "cache_write": 12.5 + } + } }, - "TEE/gemma-3-27b-it": { - "id": "TEE/gemma-3-27b-it", - "name": "Gemma 3 27B TEE", - "family": "gemma", + "phi-3-small-128k-instruct": { + "id": "phi-3-small-128k-instruct", + "name": "Phi-3-small-instruct (128k)", + "family": "phi", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-03-10", - "last_updated": "2025-03-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 131072, "input": 131072, "output": 8192 } + "temperature": true, + "knowledge": "2023-10", + "release_date": "2024-04-23", + "last_updated": "2024-04-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "TEE/kimi-k2.5-thinking": { - "id": "TEE/kimi-k2.5-thinking", - "name": "Kimi K2.5 Thinking TEE", - "family": "kimi-thinking", + "deepseek-v3.2": { + "id": "deepseek-v3.2", + "name": "DeepSeek-V3.2", + "family": "deepseek", "attachment": false, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2026-01-29", - "last_updated": "2026-01-29", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 1.9 }, - "limit": { "context": 128000, "input": 128000, "output": 65535 } + "tool_call": true, + "temperature": true, + "knowledge": "2024-07", + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.58, + "output": 1.68 + } }, - "TEE/gpt-oss-120b": { - "id": "TEE/gpt-oss-120b", - "name": "GPT-OSS 120B TEE", - "family": "gpt-oss", + "phi-3-medium-128k-instruct": { + "id": "phi-3-medium-128k-instruct", + "name": "Phi-3-medium-instruct (128k)", + "family": "phi", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 2 }, - "limit": { "context": 131072, "input": 131072, "output": 16384 } + "temperature": true, + "knowledge": "2023-10", + "release_date": "2024-04-23", + "last_updated": "2024-04-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.17, + "output": 0.68 + } }, - "TEE/qwen3-coder": { - "id": "TEE/qwen3-coder", - "name": "Qwen3 Coder 480B TEE", - "family": "qwen", + "gpt-3.5-turbo-0613": { + "id": "gpt-3.5-turbo-0613", + "name": "GPT-3.5 Turbo 0613", + "family": "gpt", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2021-08", + "release_date": "2023-06-13", + "last_updated": "2023-06-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.5, "output": 2 }, - "limit": { "context": 128000, "input": 128000, "output": 32768 } + "limit": { + "context": 16384, + "output": 16384 + }, + "cost": { + "input": 3, + "output": 4 + } }, - "TEE/minimax-m2.1": { - "id": "TEE/minimax-m2.1", - "name": "MiniMax M2.1 TEE", - "family": "minimax", - "attachment": false, + "claude-sonnet-4-5": { + "id": "claude-sonnet-4-5", + "name": "Claude Sonnet 4.5", + "family": "claude-sonnet", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 200000, "input": 200000, "output": 131072 } - }, - "TEE/qwen2.5-vl-72b-instruct": { - "id": "TEE/qwen2.5-vl-72b-instruct", - "name": "Qwen2.5 VL 72B TEE", - "family": "qwen", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-02-01", - "last_updated": "2025-02-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-07-31", + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.7, "output": 0.7 }, - "limit": { "context": 65536, "input": 65536, "output": 8192 } + "limit": { + "context": 200000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "TEE/glm-4.6": { - "id": "TEE/glm-4.6", - "name": "GLM 4.6 TEE", - "family": "glm", + "phi-4": { + "id": "phi-4", + "name": "Phi-4", + "family": "phi", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2023-10", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0.125, + "output": 0.5 + } + }, + "gpt-5": { + "id": "gpt-5", + "name": "GPT-5", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.75, "output": 2 }, - "limit": { "context": 203000, "input": 203000, "output": 65535 } + "limit": { + "context": 272000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.13 + } }, - "TEE/deepseek-v3.1": { - "id": "TEE/deepseek-v3.1", - "name": "DeepSeek V3.1 TEE", - "family": "deepseek", + "gpt-4-32k": { + "id": "gpt-4-32k", + "name": "GPT-4 32K", + "family": "gpt", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-08-21", - "last_updated": "2025-08-21", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2023-11", + "release_date": "2023-03-14", + "last_updated": "2023-03-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1, "output": 2.5 }, - "limit": { "context": 164000, "input": 164000, "output": 8192 } + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 60, + "output": 120 + } }, - "TEE/llama3-3-70b": { - "id": "TEE/llama3-3-70b", - "name": "Llama 3.3 70B", - "family": "llama", + "cohere-embed-v3-english": { + "id": "cohere-embed-v3-english", + "name": "Embed v3 English", + "family": "cohere-embed", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2025-07-03", - "last_updated": "2025-07-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 2 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "temperature": false, + "release_date": "2023-11-07", + "last_updated": "2023-11-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 512, + "output": 1024 + }, + "cost": { + "input": 0.1, + "output": 0 + } }, - "TEE/glm-5": { - "id": "TEE/glm-5", - "name": "GLM 5 TEE", - "family": "glm", + "phi-4-reasoning-plus": { + "id": "phi-4-reasoning-plus", + "name": "Phi-4-reasoning-plus", + "family": "phi", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": false, - "structured_output": false, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.2, "output": 3.5 }, - "limit": { "context": 203000, "input": 203000, "output": 65535 } + "temperature": true, + "knowledge": "2023-10", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32000, + "output": 4096 + }, + "cost": { + "input": 0.125, + "output": 0.5 + } }, - "TEE/qwen3.5-397b-a17b": { - "id": "TEE/qwen3.5-397b-a17b", - "name": "Qwen3.5 397B A17B TEE", - "family": "qwen", - "attachment": false, + "mistral-medium-2505": { + "id": "mistral-medium-2505", + "name": "Mistral Medium 3", + "family": "mistral-medium", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2026-02-28", - "last_updated": "2026-02-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2025-05", + "release_date": "2025-05-07", + "last_updated": "2025-05-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 3.6 }, - "limit": { "context": 258048, "input": 258048, "output": 65536 } + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0.4, + "output": 2 + } }, - "TEE/glm-4.7": { - "id": "TEE/glm-4.7", - "name": "GLM 4.7 TEE", - "family": "glm", + "gpt-3.5-turbo-instruct": { + "id": "gpt-3.5-turbo-instruct", + "name": "GPT-3.5 Turbo Instruct", + "family": "gpt", "attachment": false, "reasoning": false, "tool_call": false, - "structured_output": false, - "release_date": "2026-01-29", - "last_updated": "2026-01-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2021-08", + "release_date": "2023-09-21", + "last_updated": "2023-09-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.85, "output": 3.3 }, - "limit": { "context": 131000, "input": 131000, "output": 65535 } + "limit": { + "context": 4096, + "output": 4096 + }, + "cost": { + "input": 1.5, + "output": 2 + } }, - "TEE/deepseek-r1-0528": { - "id": "TEE/deepseek-r1-0528", - "name": "DeepSeek R1 0528 TEE", - "family": "deepseek", + "deepseek-r1-0528": { + "id": "deepseek-r1-0528", + "name": "DeepSeek-R1-0528", + "family": "deepseek-thinking", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-07", "release_date": "2025-05-28", "last_updated": "2025-05-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 2 }, - "limit": { "context": 128000, "input": 128000, "output": 65536 } + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163840, + "output": 163840 + }, + "cost": { + "input": 1.35, + "output": 5.4 + } }, - "TEE/gpt-oss-20b": { - "id": "TEE/gpt-oss-20b", - "name": "GPT-OSS 20B TEE", - "family": "gpt-oss", + "kimi-k2-thinking": { + "id": "kimi-k2-thinking", + "name": "Kimi K2 Thinking", + "family": "kimi-thinking", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.8 }, - "limit": { "context": 131072, "input": 131072, "output": 8192 } + "reasoning": true, + "tool_call": true, + "interleaved": true, + "temperature": true, + "knowledge": "2024-08", + "release_date": "2025-11-06", + "last_updated": "2025-12-02", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 2.5, + "cache_read": 0.15 + } }, - "TEE/deepseek-v3.2": { - "id": "TEE/deepseek-v3.2", - "name": "DeepSeek V3.2 TEE", - "family": "deepseek", + "gpt-5.1-codex": { + "id": "gpt-5.1-codex", + "name": "GPT-5.1 Codex", + "family": "gpt-codex", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-14", + "last_updated": "2025-11-14", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text", "image", "audio"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 1 }, - "limit": { "context": 164000, "input": 164000, "output": 65536 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "TEE/qwen3-30b-a3b-instruct-2507": { - "id": "TEE/qwen3-30b-a3b-instruct-2507", - "name": "Qwen3 30B A3B Instruct 2507 TEE", - "family": "qwen", + "codestral-2501": { + "id": "codestral-2501", + "name": "Codestral 25.01", + "family": "codestral", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-07-29", - "last_updated": "2025-07-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-03", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.15, "output": 0.44999999999999996 }, - "limit": { "context": 262000, "input": 262000, "output": 32768 } + "limit": { + "context": 256000, + "output": 256000 + }, + "cost": { + "input": 0.3, + "output": 0.9 + } }, - "chutesai/Mistral-Small-3.2-24B-Instruct-2506": { - "id": "chutesai/Mistral-Small-3.2-24B-Instruct-2506", - "name": "Mistral Small 3.2 24b Instruct", - "family": "chutesai", - "attachment": false, + "llama-4-maverick-17b-128e-instruct-fp8": { + "id": "llama-4-maverick-17b-128e-instruct-fp8", + "name": "Llama 4 Maverick 17B 128E Instruct FP8", + "family": "llama", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-15", - "last_updated": "2025-04-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.4 }, - "limit": { "context": 128000, "input": 128000, "output": 131072 } + "tool_call": true, + "temperature": true, + "knowledge": "2024-08", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.25, + "output": 1 + } }, - "Infermatic/MN-12B-Inferor-v0.0": { - "id": "Infermatic/MN-12B-Inferor-v0.0", - "name": "Mistral Nemo Inferor 12B", - "family": "mistral-nemo", + "mai-ds-r1": { + "id": "mai-ds-r1", + "name": "MAI-DS-R1", + "family": "mai", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": false, - "structured_output": false, - "release_date": "2024-07-01", - "last_updated": "2024-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-06", + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25499999999999995, "output": 0.49299999999999994 }, - "limit": { "context": 16384, "input": 16384, "output": 8192 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 1.35, + "output": 5.4 + } }, - "aion-labs/aion-rp-llama-3.1-8b": { - "id": "aion-labs/aion-rp-llama-3.1-8b", - "name": "Llama 3.1 8b (uncensored)", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "gpt-5.1-codex-max": { + "id": "gpt-5.1-codex-max", + "name": "GPT-5.1 Codex Max", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2006, "output": 0.2006 }, - "limit": { "context": 32768, "input": 32768, "output": 16384 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "aion-labs/aion-1.0-mini": { - "id": "aion-labs/aion-1.0-mini", - "name": "Aion 1.0 mini (DeepSeek)", - "family": "deepseek", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-02-20", - "last_updated": "2025-02-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "claude-sonnet-4-6": { + "id": "claude-sonnet-4-6", + "name": "Claude Sonnet 4.6", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.7989999999999999, "output": 1.394 }, - "limit": { "context": 131072, "input": 131072, "output": 8192 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "provider": { + "npm": "@ai-sdk/anthropic", + "api": "https://${AZURE_RESOURCE_NAME}.services.ai.azure.com/anthropic/v1" + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "aion-labs/aion-1.0": { - "id": "aion-labs/aion-1.0", - "name": "Aion 1.0", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-02-01", - "last_updated": "2025-02-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "gpt-5.5": { + "id": "gpt-5.5", + "name": "GPT-5.5", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-12-01", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3.995, "output": 7.99 }, - "limit": { "context": 65536, "input": 65536, "output": 8192 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5, + "context_over_200k": { + "input": 10, + "output": 45, + "cache_read": 1 + }, + "tiers": [ + { + "input": 10, + "output": 45, + "cache_read": 1, + "tier": { + "type": "context", + "size": 272000 + } + } + ] + } }, - "mlabonne/NeuralDaredevil-8B-abliterated": { - "id": "mlabonne/NeuralDaredevil-8B-abliterated", - "name": "Neural Daredevil 8B abliterated", - "family": "llama", - "attachment": false, + "gpt-4-turbo": { + "id": "gpt-4-turbo", + "name": "GPT-4 Turbo", + "family": "gpt", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2023-11-06", + "last_updated": "2024-04-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.44, "output": 0.44 }, - "limit": { "context": 8192, "input": 8192, "output": 8192 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 10, + "output": 30 + } }, - "moonshotai/kimi-k2-thinking": { - "id": "moonshotai/kimi-k2-thinking", - "name": "Kimi K2 Thinking", - "family": "kimi-thinking", - "attachment": false, + "gpt-4o-mini": { + "id": "gpt-4o-mini", + "name": "GPT-4o mini", + "family": "gpt-mini", + "attachment": true, "reasoning": false, "tool_call": true, + "temperature": true, + "knowledge": "2023-09", + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.08 + } + }, + "gpt-5.4-mini": { + "id": "gpt-5.4-mini", + "name": "GPT-5.4 Mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": true, + "tool_call": true, "structured_output": true, - "release_date": "2025-11-06", - "last_updated": "2025-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 256000, "input": 256000, "output": 262144 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.75, + "output": 4.5, + "cache_read": 0.075 + } }, - "moonshotai/kimi-k2.5": { - "id": "moonshotai/kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi", + "cohere-command-r-08-2024": { + "id": "cohere-command-r-08-2024", + "name": "Command R", + "family": "command-r", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-06-01", + "release_date": "2024-08-30", + "last_updated": "2024-08-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4000 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } + }, + "o4-mini": { + "id": "o4-mini", + "name": "o4-mini", + "family": "o-mini", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": false, - "release_date": "2026-01-26", - "last_updated": "2026-01-26", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.9 }, - "limit": { "context": 256000, "input": 256000, "output": 65536 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.28 + } }, - "moonshotai/kimi-k2.5:thinking": { - "id": "moonshotai/kimi-k2.5:thinking", - "name": "Kimi K2.5 Thinking", - "family": "kimi-thinking", + "gpt-5.4-nano": { + "id": "gpt-5.4-nano", + "name": "GPT-5.4 Nano", + "family": "gpt-nano", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": false, - "release_date": "2026-01-26", - "last_updated": "2026-01-26", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.9 }, - "limit": { "context": 256000, "input": 256000, "output": 65536 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.2, + "output": 1.25, + "cache_read": 0.02 + } }, - "moonshotai/kimi-k2-thinking-turbo-original": { - "id": "moonshotai/kimi-k2-thinking-turbo-original", - "name": "Kimi K2 Thinking Turbo Original", - "family": "kimi-thinking", + "grok-code-fast-1": { + "id": "grok-code-fast-1", + "name": "Grok Code Fast 1", + "family": "grok", "attachment": false, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-11-06", - "last_updated": "2025-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2023-10", + "release_date": "2025-08-28", + "last_updated": "2025-08-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.15, "output": 8 }, - "limit": { "context": 256000, "input": 256000, "output": 16384 } + "limit": { + "context": 256000, + "output": 10000 + }, + "cost": { + "input": 0.2, + "output": 1.5, + "cache_read": 0.02 + } }, - "moonshotai/Kimi-K2-Instruct-0905": { - "id": "moonshotai/Kimi-K2-Instruct-0905", - "name": "Kimi K2 0905", - "family": "kimi", - "attachment": false, - "reasoning": false, + "gpt-5.4-pro": { + "id": "gpt-5.4-pro", + "name": "GPT-5.4 Pro", + "family": "gpt-pro", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "structured_output": false, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 256000, "input": 256000, "output": 262144 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 30, + "output": 180, + "context_over_200k": { + "input": 60, + "output": 270 + }, + "tiers": [ + { + "input": 60, + "output": 270, + "tier": { + "type": "context", + "size": 272000 + } + } + ] + } }, - "moonshotai/kimi-k2-thinking-original": { - "id": "moonshotai/kimi-k2-thinking-original", - "name": "Kimi K2 Thinking Original", - "family": "kimi-thinking", + "o3-mini": { + "id": "o3-mini", + "name": "o3-mini", + "family": "o-mini", "attachment": false, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-11-06", - "last_updated": "2025-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2024-12-20", + "last_updated": "2025-01-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 2.5 }, - "limit": { "context": 256000, "input": 256000, "output": 16384 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.55 + } }, - "moonshotai/kimi-k2-instruct-0711": { - "id": "moonshotai/kimi-k2-instruct-0711", - "name": "Kimi K2 0711", - "family": "kimi", + "grok-4": { + "id": "grok-4", + "name": "Grok 4", + "family": "grok", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-07-11", - "last_updated": "2025-07-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": true, + "knowledge": "2025-07", + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 2 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 } + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "reasoning": 15, + "cache_read": 0.75 + } }, - "moonshotai/Kimi-Dev-72B": { - "id": "moonshotai/Kimi-Dev-72B", - "name": "Kimi Dev 72B", - "family": "kimi", + "gpt-5.4": { + "id": "gpt-5.4", + "name": "GPT-5.4", + "family": "gpt", "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-04-15", - "last_updated": "2025-04-15", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 0.4 }, - "limit": { "context": 128000, "input": 128000, "output": 131072 } - }, - "moonshotai/kimi-k2-instruct": { - "id": "moonshotai/kimi-k2-instruct", - "name": "Kimi K2 Instruct", - "family": "kimi", - "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, - "release_date": "2025-07-01", - "last_updated": "2025-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 2 }, - "limit": { "context": 256000, "input": 256000, "output": 8192 } - }, - "tencent/Hunyuan-MT-7B": { - "id": "tencent/Hunyuan-MT-7B", - "name": "Hunyuan MT 7B", - "family": "hunyuan", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-09-18", - "last_updated": "2025-09-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 10, "output": 20 }, - "limit": { "context": 8192, "input": 8192, "output": 8192 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 2.5, + "output": 15, + "cache_read": 0.25, + "context_over_200k": { + "input": 5, + "output": 22.5, + "cache_read": 0.5 + }, + "tiers": [ + { + "input": 5, + "output": 22.5, + "cache_read": 0.5, + "tier": { + "type": "context", + "size": 272000 + } + } + ] + } }, - "Envoid/Llama-3.05-NT-Storybreaker-Ministral-70B": { - "id": "Envoid/Llama-3.05-NT-Storybreaker-Ministral-70B", - "name": "Llama 3.05 Storybreaker Ministral 70b", - "family": "llama", - "attachment": false, + "gpt-4.1-nano": { + "id": "gpt-4.1-nano", + "name": "GPT-4.1 nano", + "family": "gpt-nano", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.49299999999999994, "output": 0.49299999999999994 }, - "limit": { "context": 16384, "input": 16384, "output": 8192 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.03 + } }, - "Envoid/Llama-3.05-Nemotron-Tenyxchat-Storybreaker-70B": { - "id": "Envoid/Llama-3.05-Nemotron-Tenyxchat-Storybreaker-70B", - "name": "Nemotron Tenyxchat Storybreaker 70b", - "family": "nemotron", + "grok-3-mini": { + "id": "grok-3-mini", + "name": "Grok 3 Mini", + "family": "grok", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-01", - "last_updated": "2024-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.49299999999999994, "output": 0.49299999999999994 }, - "limit": { "context": 16384, "input": 16384, "output": 8192 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.3, + "output": 0.5, + "reasoning": 0.5, + "cache_read": 0.075 + } }, - "Tongyi-Zhiwen/QwenLong-L1-32B": { - "id": "Tongyi-Zhiwen/QwenLong-L1-32B", - "name": "QwenLong L1 32B", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-01-25", - "last_updated": "2025-01-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "o3": { + "id": "o3", + "name": "o3", + "family": "o", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2024-05", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.13999999999999999, "output": 0.6 }, - "limit": { "context": 128000, "input": 128000, "output": 40960 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } }, - "featherless-ai/Qwerky-72B": { - "id": "featherless-ai/Qwerky-72B", - "name": "Qwerky 72B", - "family": "qwerky", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-03-20", - "last_updated": "2025-03-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "gpt-5-pro": { + "id": "gpt-5-pro", + "name": "GPT-5 Pro", + "family": "gpt-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2025-10-06", + "last_updated": "2025-10-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 0.5 }, - "limit": { "context": 32000, "input": 32000, "output": 8192 } + "limit": { + "context": 400000, + "output": 272000 + }, + "cost": { + "input": 15, + "output": 120 + } }, - "Sao10K/L3.3-70B-Euryale-v2.3": { - "id": "Sao10K/L3.3-70B-Euryale-v2.3", - "name": "Llama 3.3 70B Euryale", - "family": "llama", - "attachment": false, + "gpt-4o": { + "id": "gpt-4o", + "name": "GPT-4o", + "family": "gpt", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2023-09", + "release_date": "2024-05-13", + "last_updated": "2024-08-06", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.49299999999999994, "output": 0.49299999999999994 }, - "limit": { "context": 20480, "input": 20480, "output": 16384 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10, + "cache_read": 1.25 + } }, - "Sao10K/L3.1-70B-Hanami-x1": { - "id": "Sao10K/L3.1-70B-Hanami-x1", - "name": "Llama 3.1 70B Hanami", - "family": "llama", + "cohere-command-r-plus-08-2024": { + "id": "cohere-command-r-plus-08-2024", + "name": "Command R+", + "family": "command-r", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.49299999999999994, "output": 0.49299999999999994 }, - "limit": { "context": 16384, "input": 16384, "output": 16384 } + "tool_call": true, + "temperature": true, + "knowledge": "2024-06-01", + "release_date": "2024-08-30", + "last_updated": "2024-08-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4000 + }, + "cost": { + "input": 2.5, + "output": 10 + } }, - "Sao10K/L3.1-70B-Euryale-v2.2": { - "id": "Sao10K/L3.1-70B-Euryale-v2.2", - "name": "Llama 3.1 70B Euryale", - "family": "llama", - "attachment": false, + "gpt-4.1": { + "id": "gpt-4.1", + "name": "GPT-4.1", + "family": "gpt", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.306, "output": 0.357 }, - "limit": { "context": 20480, "input": 20480, "output": 16384 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } }, - "Sao10K/L3-8B-Stheno-v3.2": { - "id": "Sao10K/L3-8B-Stheno-v3.2", - "name": "Sao10K Stheno 8b", - "family": "llama", - "attachment": false, + "gpt-4.1-mini": { + "id": "gpt-4.1-mini", + "name": "GPT-4.1 mini", + "family": "gpt-mini", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-11-29", - "last_updated": "2024-11-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2006, "output": 0.2006 }, - "limit": { "context": 16384, "input": 16384, "output": 8192 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 0.4, + "output": 1.6, + "cache_read": 0.1 + } }, - "inflection/inflection-3-pi": { - "id": "inflection/inflection-3-pi", - "name": "Inflection 3 Pi", - "family": "gpt", + "grok-3": { + "id": "grok-3", + "name": "Grok 3", + "family": "grok", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-10-11", - "last_updated": "2024-10-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.499, "output": 9.996 }, - "limit": { "context": 8000, "input": 8000, "output": 4096 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75 + } }, - "inflection/inflection-3-productivity": { - "id": "inflection/inflection-3-productivity", - "name": "Inflection 3 Productivity", - "family": "gpt", - "attachment": false, + "grok-4-fast-non-reasoning": { + "id": "grok-4-fast-non-reasoning", + "name": "Grok 4 Fast (Non-Reasoning)", + "family": "grok", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-10-11", - "last_updated": "2024-10-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2025-07", + "release_date": "2025-09-19", + "last_updated": "2025-09-19", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.499, "output": 9.996 }, - "limit": { "context": 8000, "input": 8000, "output": 4096 } - }, - "GalrionSoftworks/MN-LooseCannon-12B-v1": { - "id": "GalrionSoftworks/MN-LooseCannon-12B-v1", - "name": "MN-LooseCannon-12B-v1", - "family": "mistral-nemo", + "limit": { + "context": 2000000, + "output": 30000 + }, + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } + } + } + }, + "fastrouter": { + "id": "fastrouter", + "env": ["FASTROUTER_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://go.fastrouter.ai/api/v1", + "name": "FastRouter", + "doc": "https://fastrouter.ai/models", + "models": { + "x-ai/grok-4": { + "id": "x-ai/grok-4", + "name": "Grok 4", + "family": "grok", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-07-01", - "last_updated": "2024-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-07", + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.49299999999999994, "output": 0.49299999999999994 }, - "limit": { "context": 16384, "input": 16384, "output": 8192 } + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75, + "cache_write": 15 + } }, - "LatitudeGames/Wayfarer-Large-70B-Llama-3.3": { - "id": "LatitudeGames/Wayfarer-Large-70B-Llama-3.3", - "name": "Llama 3.3 70B Wayfarer", - "family": "llama", + "deepseek-ai/deepseek-r1-distill-llama-70b": { + "id": "deepseek-ai/deepseek-r1-distill-llama-70b", + "name": "DeepSeek R1 Distill Llama 70B", + "family": "deepseek-thinking", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": false, - "structured_output": false, - "release_date": "2025-02-20", - "last_updated": "2025-02-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.700000007, "output": 0.700000007 }, - "limit": { "context": 16384, "input": 16384, "output": 16384 } + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-01-23", + "last_updated": "2025-01-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.03, + "output": 0.14 + } }, - "anthracite-org/magnum-v4-72b": { - "id": "anthracite-org/magnum-v4-72b", - "name": "Magnum v4 72B", - "family": "llama", + "openai/gpt-5-mini": { + "id": "openai/gpt-5-mini", + "name": "GPT-5 Mini", + "family": "gpt-mini", "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-10-01", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.006, "output": 2.992 }, - "limit": { "context": 16384, "input": 16384, "output": 8192 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.025 + } }, - "anthracite-org/magnum-v2-72b": { - "id": "anthracite-org/magnum-v2-72b", - "name": "Magnum V2 72B", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-07-01", - "last_updated": "2024-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "openai/gpt-5-nano": { + "id": "openai/gpt-5-nano", + "name": "GPT-5 Nano", + "family": "gpt-nano", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-10-01", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.006, "output": 2.992 }, - "limit": { "context": 16384, "input": 16384, "output": 8192 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 0.05, + "output": 0.4, + "cache_read": 0.005 + } }, - "tngtech/tng-r1t-chimera": { - "id": "tngtech/tng-r1t-chimera", - "name": "TNG R1T Chimera", - "family": "tngtech", + "openai/gpt-oss-20b": { + "id": "openai/gpt-oss-20b", + "name": "GPT OSS 20B", + "family": "gpt-oss", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-11-26", - "last_updated": "2025-11-26", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.05, + "output": 0.2 + } + }, + "openai/gpt-5": { + "id": "openai/gpt-5", + "name": "GPT-5", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-10-01", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 128000, "input": 128000, "output": 65536 } + "limit": { + "context": 400000, + "output": 128000 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125 + } }, - "tngtech/DeepSeek-TNG-R1T2-Chimera": { - "id": "tngtech/DeepSeek-TNG-R1T2-Chimera", - "name": "DeepSeek TNG R1T2 Chimera", - "family": "tngtech", + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "name": "GPT OSS 120B", + "family": "gpt-oss", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.31, "output": 0.31 }, - "limit": { "context": 128000, "input": 128000, "output": 8192 } + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "deepcogito/cogito-v2.1-671b": { - "id": "deepcogito/cogito-v2.1-671b", - "name": "Cogito v2.1 671B MoE", - "family": "cogito", + "z-ai/glm-5": { + "id": "z-ai/glm-5", + "name": "GLM-5", + "family": "glm", "attachment": false, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-11-19", - "last_updated": "2025-11-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 1.25 }, - "limit": { "context": 128000, "input": 128000, "output": 16384 } + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.95, + "output": 3.15 + } }, - "deepcogito/cogito-v1-preview-qwen-32B": { - "id": "deepcogito/cogito-v1-preview-qwen-32B", - "name": "Cogito v1 Preview Qwen 32B", + "qwen/qwen3-coder": { + "id": "qwen/qwen3-coder", + "name": "Qwen3 Coder", "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-05-10", - "last_updated": "2025-05-10", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 66536 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } + }, + "google/gemini-2.5-pro": { + "id": "google/gemini-2.5-pro", + "name": "Gemini 2.5 Pro", + "family": "gemini-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.7999999999999998, "output": 1.7999999999999998 }, - "limit": { "context": 128000, "input": 128000, "output": 32768 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.31 + } }, - "huihui-ai/DeepSeek-R1-Distill-Llama-70B-abliterated": { - "id": "huihui-ai/DeepSeek-R1-Distill-Llama-70B-abliterated", - "name": "DeepSeek R1 Llama 70B Abliterated", - "family": "deepseek", - "attachment": false, + "google/gemini-2.5-flash": { + "id": "google/gemini-2.5-flash", + "name": "Gemini 2.5 Flash", + "family": "gemini-flash", + "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.7, "output": 0.7 }, - "limit": { "context": 16384, "input": 16384, "output": 8192 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.0375 + } }, - "huihui-ai/Llama-3.3-70B-Instruct-abliterated": { - "id": "huihui-ai/Llama-3.3-70B-Instruct-abliterated", - "name": "Llama 3.3 70B Instruct abliterated", - "family": "llama", + "moonshotai/kimi-k2": { + "id": "moonshotai/kimi-k2", + "name": "Kimi K2", + "family": "kimi", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-08-08", - "last_updated": "2025-08-08", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.7, "output": 0.7 }, - "limit": { "context": 16384, "input": 16384, "output": 16384 } + "tool_call": true, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-07-11", + "last_updated": "2025-07-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.55, + "output": 2.2 + } }, - "huihui-ai/Qwen2.5-32B-Instruct-abliterated": { - "id": "huihui-ai/Qwen2.5-32B-Instruct-abliterated", - "name": "Qwen 2.5 32B Abliterated", - "family": "qwen", - "attachment": false, + "openai/gpt-4.1": { + "id": "openai/gpt-4.1", + "name": "GPT-4.1", + "family": "gpt", + "attachment": true, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-01-06", - "last_updated": "2025-01-06", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.7, "output": 0.7 }, - "limit": { "context": 32768, "input": 32768, "output": 8192 } + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } }, - "huihui-ai/DeepSeek-R1-Distill-Qwen-32B-abliterated": { - "id": "huihui-ai/DeepSeek-R1-Distill-Qwen-32B-abliterated", - "name": "DeepSeek R1 Qwen Abliterated", - "family": "qwen", - "attachment": false, + "anthropic/claude-opus-4.1": { + "id": "anthropic/claude-opus-4.1", + "name": "Claude Opus 4.1", + "family": "claude-opus", + "attachment": true, "reasoning": true, - "tool_call": false, - "structured_output": false, - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.4, "output": 1.4 }, - "limit": { "context": 16384, "input": 16384, "output": 8192 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "huihui-ai/Llama-3.1-Nemotron-70B-Instruct-HF-abliterated": { - "id": "huihui-ai/Llama-3.1-Nemotron-70B-Instruct-HF-abliterated", - "name": "Nemotron 3.1 70B abliterated", - "family": "nemotron", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "anthropic/claude-sonnet-4": { + "id": "anthropic/claude-sonnet-4", + "name": "Claude Sonnet 4", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.7, "output": 0.7 }, - "limit": { "context": 16384, "input": 16384, "output": 16384 } - }, - "mistralai/mistral-medium-3.1": { - "id": "mistralai/mistral-medium-3.1", - "name": "Mistral Medium 3.1", - "family": "mistral-medium", - "attachment": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + } + } + }, + "stackit": { + "id": "stackit", + "env": ["STACKIT_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.openai-compat.model-serving.eu01.onstackit.cloud/v1", + "name": "STACKIT", + "doc": "https://docs.stackit.cloud/products/data-and-ai/ai-model-serving/basics/available-shared-models", + "models": { + "Qwen/Qwen3-VL-Embedding-8B": { + "id": "Qwen/Qwen3-VL-Embedding-8B", + "name": "Qwen3-VL Embedding 8B", + "family": "qwen", + "attachment": true, "reasoning": false, "tool_call": false, "structured_output": false, - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 131072, "input": 131072, "output": 32768 } + "temperature": false, + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32000, + "output": 4096 + }, + "cost": { + "input": 0.09, + "output": 0.09 + } }, - "mistralai/mistral-7b-instruct": { - "id": "mistralai/mistral-7b-instruct", - "name": "Mistral 7B Instruct", - "family": "mistral", + "Qwen/Qwen3-VL-235B-A22B-Instruct-FP8": { + "id": "Qwen/Qwen3-VL-235B-A22B-Instruct-FP8", + "name": "Qwen3-VL 235B", + "family": "qwen", "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, "structured_output": false, - "release_date": "2024-05-27", - "last_updated": "2024-05-27", - "modalities": { "input": ["text", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.0544, "output": 0.0544 }, - "limit": { "context": 32768, "input": 32768, "output": 8192 } + "temperature": true, + "release_date": "2024-11-01", + "last_updated": "2024-11-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 218000, + "output": 8192 + }, + "cost": { + "input": 1.64, + "output": 1.91 + } }, - "mistralai/devstral-2-123b-instruct-2512": { - "id": "mistralai/devstral-2-123b-instruct-2512", - "name": "Devstral 2 123B", - "family": "devstral", + "neuralmagic/Meta-Llama-3.1-8B-Instruct-FP8": { + "id": "neuralmagic/Meta-Llama-3.1-8B-Instruct-FP8", + "name": "Llama 3.1 8B", + "family": "llama", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-09", - "last_updated": "2025-12-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 1.4 }, - "limit": { "context": 262144, "input": 262144, "output": 65536 } + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.16, + "output": 0.27 + } }, - "mistralai/mixtral-8x22b-instruct-v0.1": { - "id": "mistralai/mixtral-8x22b-instruct-v0.1", - "name": "Mixtral 8x22B", - "family": "mixtral", + "neuralmagic/Mistral-Nemo-Instruct-2407-FP8": { + "id": "neuralmagic/Mistral-Nemo-Instruct-2407-FP8", + "name": "Mistral Nemo", + "family": "mistral", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "structured_output": false, - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.8999999999999999, "output": 0.8999999999999999 }, - "limit": { "context": 65536, "input": 65536, "output": 32768 } + "temperature": true, + "release_date": "2024-07-01", + "last_updated": "2024-07-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.49, + "output": 0.71 + } }, - "mistralai/mistral-large-3-675b-instruct-2512": { - "id": "mistralai/mistral-large-3-675b-instruct-2512", - "name": "Mistral Large 3 675B", - "family": "mistral-large", - "attachment": true, - "reasoning": false, - "tool_call": false, + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "name": "GPT-OSS 120B", + "family": "gpt", + "attachment": false, + "reasoning": true, + "tool_call": true, "structured_output": false, - "release_date": "2025-12-02", - "last_updated": "2025-12-02", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1, "output": 3 }, - "limit": { "context": 262144, "input": 262144, "output": 256000 } + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131000, + "output": 8192 + }, + "cost": { + "input": 0.49, + "output": 0.71 + } }, - "mistralai/Devstral-Small-2505": { - "id": "mistralai/Devstral-Small-2505", - "name": "Mistral Devstral Small 2505", - "family": "devstral", + "cortecs/Llama-3.3-70B-Instruct-FP8-Dynamic": { + "id": "cortecs/Llama-3.3-70B-Instruct-FP8-Dynamic", + "name": "Llama 3.3 70B", + "family": "llama", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "structured_output": false, - "release_date": "2025-08-02", - "last_updated": "2025-08-02", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.060000000000000005, "output": 0.060000000000000005 }, - "limit": { "context": 32768, "input": 32768, "output": 8192 } + "temperature": true, + "release_date": "2024-12-05", + "last_updated": "2024-12-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.49, + "output": 0.71 + } }, - "mistralai/mistral-medium-3": { - "id": "mistralai/mistral-medium-3", - "name": "Mistral Medium 3", - "family": "mistral-medium", + "google/gemma-3-27b-it": { + "id": "google/gemma-3-27b-it", + "name": "Gemma 3 27B", + "family": "gemma", "attachment": true, "reasoning": false, "tool_call": false, "structured_output": false, - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 131072, "input": 131072, "output": 32768 } - }, - "mistralai/ministral-8b-2512": { - "id": "mistralai/ministral-8b-2512", - "name": "Ministral 8B", - "family": "ministral", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-04", - "last_updated": "2025-12-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.15 }, - "limit": { "context": 262144, "input": 262144, "output": 32768 } + "temperature": true, + "release_date": "2025-05-17", + "last_updated": "2025-05-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 37000, + "output": 8192 + }, + "cost": { + "input": 0.49, + "output": 0.71 + } }, - "mistralai/ministral-14b-2512": { - "id": "mistralai/ministral-14b-2512", - "name": "Ministral 14B", - "family": "ministral", + "intfloat/e5-mistral-7b-instruct": { + "id": "intfloat/e5-mistral-7b-instruct", + "name": "E5 Mistral 7B", + "family": "mistral", "attachment": false, "reasoning": false, "tool_call": false, "structured_output": false, - "release_date": "2025-12-04", - "last_updated": "2025-12-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.2 }, - "limit": { "context": 262144, "input": 262144, "output": 32768 } + "temperature": false, + "release_date": "2023-12-11", + "last_updated": "2023-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 4096, + "output": 4096 + }, + "cost": { + "input": 0.02, + "output": 0.02 + } + } + } + }, + "tencent-coding-plan": { + "id": "tencent-coding-plan", + "env": ["TENCENT_CODING_PLAN_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.lkeap.cloud.tencent.com/coding/v3", + "name": "Tencent Coding Plan (China)", + "doc": "https://cloud.tencent.com/document/product/1772/128947", + "models": { + "kimi-k2.5": { + "id": "kimi-k2.5", + "name": "Kimi-K2.5", + "family": "kimi", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "mistralai/Mistral-Nemo-Instruct-2407": { - "id": "mistralai/Mistral-Nemo-Instruct-2407", - "name": "Mistral Nemo", - "family": "mistral-nemo", + "glm-5": { + "id": "glm-5", + "name": "GLM-5", + "family": "glm", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1003, "output": 0.1207 }, - "limit": { "context": 16384, "input": 16384, "output": 8192 } + "limit": { + "context": 202752, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "mistralai/codestral-2508": { - "id": "mistralai/codestral-2508", - "name": "Codestral 2508", - "family": "codestral", + "hunyuan-turbos": { + "id": "hunyuan-turbos", + "name": "Hunyuan-TurboS", + "family": "hunyuan", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-08-01", - "last_updated": "2025-08-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-08", + "last_updated": "2026-03-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 0.8999999999999999 }, - "limit": { "context": 256000, "input": 256000, "output": 32768 } + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "mistralai/mistral-small-creative": { - "id": "mistralai/mistral-small-creative", - "name": "Mistral Small Creative", - "family": "mistral-small", + "hunyuan-t1": { + "id": "hunyuan-t1", + "name": "Hunyuan-T1", + "family": "hunyuan", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, - "release_date": "2025-12-16", - "last_updated": "2025-12-16", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 32768, "input": 32768, "output": 32768 } - }, - "mistralai/ministral-14b-instruct-2512": { - "id": "mistralai/ministral-14b-instruct-2512", - "name": "Ministral 3 14B", - "family": "ministral", - "attachment": true, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-02", - "last_updated": "2025-12-02", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-03-08", + "last_updated": "2026-03-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 262144, "input": 262144, "output": 32768 } + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "mistralai/mistral-large": { - "id": "mistralai/mistral-large", - "name": "Mistral Large 2411", - "family": "mistral-large", + "hunyuan-2.0-instruct": { + "id": "hunyuan-2.0-instruct", + "name": "Tencent HY 2.0 Instruct", + "family": "hunyuan", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2024-02-26", - "last_updated": "2024-02-26", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-08", + "last_updated": "2026-03-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2.006, "output": 6.001 }, - "limit": { "context": 128000, "input": 128000, "output": 256000 } + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "mistralai/ministral-3b-2512": { - "id": "mistralai/ministral-3b-2512", - "name": "Ministral 3B", - "family": "ministral", + "minimax-m2.5": { + "id": "minimax-m2.5", + "name": "MiniMax-M2.5", + "family": "minimax", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-04", - "last_updated": "2025-12-04", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.1 }, - "limit": { "context": 131072, "input": 131072, "output": 32768 } + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "mistralai/mistral-tiny": { - "id": "mistralai/mistral-tiny", - "name": "Mistral Tiny", - "family": "mistral", + "tc-code-latest": { + "id": "tc-code-latest", + "name": "Auto", + "family": "auto", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2023-12-11", - "last_updated": "2024-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-08", + "last_updated": "2026-03-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25499999999999995, "output": 0.25499999999999995 }, - "limit": { "context": 32000, "input": 32000, "output": 8192 } + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "mistralai/mixtral-8x7b-instruct-v0.1": { - "id": "mistralai/mixtral-8x7b-instruct-v0.1", - "name": "Mixtral 8x7B", - "family": "mixtral", + "hunyuan-2.0-thinking": { + "id": "hunyuan-2.0-thinking", + "name": "Tencent HY 2.0 Think", + "family": "hunyuan", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-03-08", + "last_updated": "2026-03-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.27, "output": 0.27 }, - "limit": { "context": 32768, "input": 32768, "output": 32768 } + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } + } + } + }, + "privatemode-ai": { + "id": "privatemode-ai", + "env": ["PRIVATEMODE_API_KEY", "PRIVATEMODE_ENDPOINT"], + "npm": "@ai-sdk/openai-compatible", + "api": "http://localhost:8080/v1", + "name": "Privatemode AI", + "doc": "https://docs.privatemode.ai/api/overview", + "models": { + "gemma-3-27b": { + "id": "gemma-3-27b", + "name": "Gemma 3 27B", + "family": "gemma", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2024-08", + "release_date": "2025-03-12", + "last_updated": "2025-03-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "mistralai/mistral-saba": { - "id": "mistralai/mistral-saba", - "name": "Mistral Saba", - "family": "mistral", - "attachment": false, + "whisper-large-v3": { + "id": "whisper-large-v3", + "name": "Whisper large-v3", + "family": "whisper", + "attachment": true, "reasoning": false, "tool_call": false, "structured_output": false, - "release_date": "2025-02-17", - "last_updated": "2025-02-17", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1989, "output": 0.595 }, - "limit": { "context": 32000, "input": 32000, "output": 32768 } + "temperature": true, + "knowledge": "2023-09", + "release_date": "2023-09-01", + "last_updated": "2023-09-01", + "modalities": { + "input": ["audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 0, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "EVA-UNIT-01/EVA-Qwen2.5-72B-v0.2": { - "id": "EVA-UNIT-01/EVA-Qwen2.5-72B-v0.2", - "name": "EVA-Qwen2.5-72B-v0.2", + "qwen3-embedding-4b": { + "id": "qwen3-embedding-4b", + "name": "Qwen3-Embedding 4B", "family": "qwen", "attachment": false, "reasoning": false, "tool_call": false, "structured_output": false, - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.7989999999999999, "output": 0.7989999999999999 }, - "limit": { "context": 16384, "input": 16384, "output": 8192 } + "temperature": true, + "knowledge": "2025-06", + "release_date": "2025-06-06", + "last_updated": "2025-06-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32000, + "output": 2560 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "EVA-UNIT-01/EVA-LLaMA-3.33-70B-v0.1": { - "id": "EVA-UNIT-01/EVA-LLaMA-3.33-70B-v0.1", - "name": "EVA-LLaMA-3.33-70B-v0.1", - "family": "llama", + "gpt-oss-120b": { + "id": "gpt-oss-120b", + "name": "gpt-oss-120b", + "family": "gpt-oss", "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.006, "output": 2.006 }, - "limit": { "context": 16384, "input": 16384, "output": 16384 } + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-08", + "release_date": "2025-08-04", + "last_updated": "2025-08-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "EVA-UNIT-01/EVA-Qwen2.5-32B-v0.2": { - "id": "EVA-UNIT-01/EVA-Qwen2.5-32B-v0.2", - "name": "EVA-Qwen2.5-32B-v0.2", + "qwen3-coder-30b-a3b": { + "id": "qwen3-coder-30b-a3b", + "name": "Qwen3-Coder 30B-A3B", "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-07-26", - "last_updated": "2025-07-26", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.7989999999999999, "output": 0.7989999999999999 }, - "limit": { "context": 16384, "input": 16384, "output": 8192 } - }, - "EVA-UNIT-01/EVA-LLaMA-3.33-70B-v0.0": { - "id": "EVA-UNIT-01/EVA-LLaMA-3.33-70B-v0.0", - "name": "EVA Llama 3.33 70B", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": false, - "structured_output": false, - "release_date": "2025-07-26", - "last_updated": "2025-07-26", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.006, "output": 2.006 }, - "limit": { "context": 16384, "input": 16384, "output": 16384 } + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } } } }, - "clarifai": { - "id": "clarifai", - "env": ["CLARIFAI_PAT"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.clarifai.com/v2/ext/openai/v1", - "name": "Clarifai", - "doc": "https://docs.clarifai.com/compute/inference/", + "google": { + "id": "google", + "env": ["GOOGLE_GENERATIVE_AI_API_KEY", "GEMINI_API_KEY"], + "npm": "@ai-sdk/google", + "name": "Google", + "doc": "https://ai.google.dev/gemini-api/docs/models", "models": { - "openai/chat-completion/models/gpt-oss-20b": { - "id": "openai/chat-completion/models/gpt-oss-20b", - "name": "GPT OSS 20B", - "family": "gpt-oss", - "attachment": false, + "gemini-flash-lite-latest": { + "id": "gemini-flash-lite-latest", + "name": "Gemini Flash-Lite Latest", + "family": "gemini-flash-lite", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-12-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.045, "output": 0.18 }, - "limit": { "context": 131072, "output": 16384 } + "knowledge": "2025-01", + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.025 + } }, - "openai/chat-completion/models/gpt-oss-120b-high-throughput": { - "id": "openai/chat-completion/models/gpt-oss-120b-high-throughput", - "name": "GPT OSS 120B High Throughput", - "family": "gpt-oss", - "attachment": false, + "gemini-2.5-pro-preview-05-06": { + "id": "gemini-2.5-pro-preview-05-06", + "name": "Gemini 2.5 Pro Preview 05-06", + "family": "gemini-pro", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2026-02-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.09, "output": 0.36 }, - "limit": { "context": 131072, "output": 16384 } + "knowledge": "2025-01", + "release_date": "2025-05-06", + "last_updated": "2025-05-06", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.31 + } }, - "arcee_ai/AFM/models/trinity-mini": { - "id": "arcee_ai/AFM/models/trinity-mini", - "name": "Trinity Mini", - "family": "trinity-mini", + "gemini-live-2.5-flash-preview-native-audio": { + "id": "gemini-live-2.5-flash-preview-native-audio", + "name": "Gemini Live 2.5 Flash Preview Native Audio", + "family": "gemini-flash", "attachment": false, "reasoning": true, "tool_call": true, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2025-06-17", + "last_updated": "2025-09-18", + "modalities": { + "input": ["text", "audio", "video"], + "output": ["text", "audio"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 2, + "input_audio": 3, + "output_audio": 12 + } + }, + "gemini-3.1-pro-preview-customtools": { + "id": "gemini-3.1-pro-preview-customtools", + "name": "Gemini 3.1 Pro Preview Custom Tools", + "family": "gemini-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-02-19", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2, + "tiers": [ + { + "input": 4, + "output": 18, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 18, + "cache_read": 0.4 + } + } + }, + "gemini-2.5-flash-lite-preview-09-2025": { + "id": "gemini-2.5-flash-lite-preview-09-2025", + "name": "Gemini 2.5 Flash Lite Preview 09-25", + "family": "gemini-flash-lite", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.025 + } + }, + "gemini-1.5-flash": { + "id": "gemini-1.5-flash", + "name": "Gemini 1.5 Flash", + "family": "gemini-flash", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2024-05-14", + "last_updated": "2024-05-14", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 8192 + }, + "cost": { + "input": 0.075, + "output": 0.3, + "cache_read": 0.01875 + } + }, + "gemini-1.5-pro": { + "id": "gemini-1.5-pro", + "name": "Gemini 1.5 Pro", + "family": "gemini-pro", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2024-02-15", + "last_updated": "2024-02-15", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 8192 + }, + "cost": { + "input": 1.25, + "output": 5, + "cache_read": 0.3125 + } + }, + "gemma-3n-e4b-it": { + "id": "gemma-3n-e4b-it", + "name": "Gemma 3n 4B", + "family": "gemma", + "attachment": true, + "reasoning": false, + "tool_call": false, "temperature": true, "knowledge": "2024-10", - "release_date": "2025-12", - "last_updated": "2026-02-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-05-20", + "last_updated": "2025-05-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.045, "output": 0.15 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 8192, + "output": 2000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "minimaxai/chat-completion/models/MiniMax-M2_5-high-throughput": { - "id": "minimaxai/chat-completion/models/MiniMax-M2_5-high-throughput", - "name": "MiniMax-M2.5 High Throughput", - "family": "minimax", - "attachment": false, + "gemini-3.1-flash-lite-preview": { + "id": "gemini-3.1-flash-lite-preview", + "name": "Gemini 3.1 Flash Lite Preview", + "family": "gemini-flash-lite", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 204800, "output": 131072 } + "knowledge": "2025-01", + "release_date": "2026-03-03", + "last_updated": "2026-03-03", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.25, + "output": 1.5, + "cache_read": 0.025, + "input_audio": 0.5 + } }, - "deepseek-ai/deepseek-ocr/models/DeepSeek-OCR": { - "id": "deepseek-ai/deepseek-ocr/models/DeepSeek-OCR", - "name": "DeepSeek OCR", - "family": "deepseek", + "gemini-3.1-pro-preview": { + "id": "gemini-3.1-pro-preview", + "name": "Gemini 3.1 Pro Preview", + "family": "gemini-pro", "attachment": true, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-10-20", - "last_updated": "2026-02-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.2, "output": 0.7 }, - "limit": { "context": 8192, "output": 8192 } + "knowledge": "2025-01", + "release_date": "2026-02-19", + "last_updated": "2026-02-19", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2, + "tiers": [ + { + "input": 4, + "output": 18, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 18, + "cache_read": 0.4 + } + } }, - "qwen/qwenCoder/models/Qwen3-Coder-30B-A3B-Instruct": { - "id": "qwen/qwenCoder/models/Qwen3-Coder-30B-A3B-Instruct", - "name": "Qwen3 Coder 30B A3B Instruct", - "family": "qwen", - "attachment": false, + "gemini-2.0-flash": { + "id": "gemini-2.0-flash", + "name": "Gemini 2.0 Flash", + "family": "gemini-flash", + "attachment": true, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-31", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.11458, "output": 0.74812 }, - "limit": { "context": 262144, "output": 65536 } + "knowledge": "2024-06", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.025 + } }, - "qwen/qwenLM/models/Qwen3-30B-A3B-Thinking-2507": { - "id": "qwen/qwenLM/models/Qwen3-30B-A3B-Thinking-2507", - "name": "Qwen3 30B A3B Thinking 2507", - "family": "qwen", - "attachment": false, + "gemini-3-flash-preview": { + "id": "gemini-3-flash-preview", + "name": "Gemini 3 Flash Preview", + "family": "gemini-flash", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-07-31", - "last_updated": "2026-02-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.36, "output": 1.3 }, - "limit": { "context": 262144, "output": 131072 } + "knowledge": "2025-01", + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.05, + "input_audio": 1 + } }, - "qwen/qwenLM/models/Qwen3-30B-A3B-Instruct-2507": { - "id": "qwen/qwenLM/models/Qwen3-30B-A3B-Instruct-2507", - "name": "Qwen3 30B A3B Instruct 2507", - "family": "qwen", + "gemini-2.5-flash-preview-tts": { + "id": "gemini-2.5-flash-preview-tts", + "name": "Gemini 2.5 Flash Preview TTS", + "family": "gemini-flash", "attachment": false, "reasoning": false, + "tool_call": false, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2025-05-01", + "last_updated": "2025-05-01", + "modalities": { + "input": ["text"], + "output": ["audio"] + }, + "open_weights": false, + "limit": { + "context": 8000, + "output": 16000 + }, + "cost": { + "input": 0.5, + "output": 10 + } + }, + "gemini-3-pro-preview": { + "id": "gemini-3-pro-preview", + "name": "Gemini 3 Pro Preview", + "family": "gemini-pro", + "attachment": true, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-07-30", - "last_updated": "2026-02-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 0.5 }, - "limit": { "context": 262144, "output": 262144 } + "knowledge": "2025-01", + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2, + "tiers": [ + { + "input": 4, + "output": 18, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 18, + "cache_read": 0.4 + } + } }, - "clarifai/main/models/mm-poly-8b": { - "id": "clarifai/main/models/mm-poly-8b", - "name": "MM Poly 8B", - "family": "mm-poly", + "gemini-2.5-flash-preview-05-20": { + "id": "gemini-2.5-flash-preview-05-20", + "name": "Gemini 2.5 Flash Preview 05-20", + "family": "gemini-flash", "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-05-20", + "last_updated": "2025-05-20", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.0375 + } + }, + "gemini-embedding-001": { + "id": "gemini-embedding-001", + "name": "Gemini Embedding 001", + "family": "gemini", + "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-06", - "last_updated": "2026-02-25", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-05", + "release_date": "2025-05-20", + "last_updated": "2025-05-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.658, "output": 1.11 }, - "limit": { "context": 32768, "output": 4096 } + "limit": { + "context": 2048, + "output": 3072 + }, + "cost": { + "input": 0.15, + "output": 0 + } }, - "mistralai/completion/models/Ministral-3-14B-Reasoning-2512": { - "id": "mistralai/completion/models/Ministral-3-14B-Reasoning-2512", - "name": "Ministral 3 14B Reasoning 2512", - "family": "ministral", + "gemini-2.5-pro": { + "id": "gemini-2.5-pro", + "name": "Gemini 2.5 Pro", + "family": "gemini-pro", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-12", - "release_date": "2025-12-01", - "last_updated": "2025-12-12", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2.5, "output": 1.7 }, - "limit": { "context": 262144, "output": 262144 } + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.125, + "tiers": [ + { + "input": 2.5, + "output": 15, + "cache_read": 0.25, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 2.5, + "output": 15, + "cache_read": 0.25 + } + } }, - "mistralai/completion/models/Ministral-3-3B-Reasoning-2512": { - "id": "mistralai/completion/models/Ministral-3-3B-Reasoning-2512", - "name": "Ministral 3 3B Reasoning 2512", - "family": "ministral", + "gemini-flash-latest": { + "id": "gemini-flash-latest", + "name": "Gemini Flash Latest", + "family": "gemini-flash", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-12", - "last_updated": "2026-02-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1.039, "output": 0.54825 }, - "limit": { "context": 262144, "output": 262144 } - } - } - }, - "cerebras": { - "id": "cerebras", - "env": ["CEREBRAS_API_KEY"], - "npm": "@ai-sdk/cerebras", - "name": "Cerebras", - "doc": "https://inference-docs.cerebras.ai/models/overview", - "models": { - "gpt-oss-120b": { - "id": "gpt-oss-120b", - "name": "GPT OSS 120B", - "family": "gpt-oss", + "knowledge": "2025-01", + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.075, + "input_audio": 1 + } + }, + "gemma-4-31b-it": { + "id": "gemma-4-31b-it", + "name": "Gemma 4 31B", + "family": "gemma", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.25, "output": 0.69 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 256000, + "output": 8192 + } }, - "llama3.1-8b": { - "id": "llama3.1-8b", - "name": "Llama 3.1 8B", - "family": "llama", - "attachment": false, - "reasoning": false, + "gemini-2.5-pro-preview-06-05": { + "id": "gemini-2.5-pro-preview-06-05", + "name": "Gemini 2.5 Pro Preview 06-05", + "family": "gemini-pro", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.1, "output": 0.1 }, - "limit": { "context": 32000, "output": 8000 } + "knowledge": "2025-01", + "release_date": "2025-06-05", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.31 + } }, - "zai-glm-4.7": { - "id": "zai-glm-4.7", - "name": "Z.AI GLM-4.7", - "attachment": false, - "reasoning": false, - "tool_call": true, + "gemini-2.5-flash-image": { + "id": "gemini-2.5-flash-image", + "name": "Gemini 2.5 Flash Image", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, + "tool_call": false, "temperature": true, - "release_date": "2026-01-10", - "last_updated": "2026-01-10", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2.25, "output": 2.75, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 131072, "output": 40000 } + "knowledge": "2025-06", + "release_date": "2025-08-26", + "last_updated": "2025-08-26", + "modalities": { + "input": ["text", "image"], + "output": ["text", "image"] + }, + "open_weights": false, + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.3, + "output": 30, + "cache_read": 0.075 + } }, - "qwen-3-235b-a22b-instruct-2507": { - "id": "qwen-3-235b-a22b-instruct-2507", - "name": "Qwen 3 235B Instruct", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-22", - "last_updated": "2025-07-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 1.2 }, - "limit": { "context": 131000, "output": 32000 } - } - } - }, - "stackit": { - "id": "stackit", - "env": ["STACKIT_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.openai-compat.model-serving.eu01.onstackit.cloud/v1", - "name": "STACKIT", - "doc": "https://docs.stackit.cloud/products/data-and-ai/ai-model-serving/basics/available-shared-models", - "models": { - "openai/gpt-oss-120b": { - "id": "openai/gpt-oss-120b", - "name": "GPT-OSS 120B", - "family": "gpt", - "attachment": false, + "gemini-2.5-flash-lite-preview-06-17": { + "id": "gemini-2.5-flash-lite-preview-06-17", + "name": "Gemini 2.5 Flash Lite Preview 06-17", + "family": "gemini-flash-lite", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": false, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.49, "output": 0.71 }, - "limit": { "context": 131000, "output": 8192 } + "knowledge": "2025-01", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.025, + "input_audio": 0.3 + } }, - "google/gemma-3-27b-it": { - "id": "google/gemma-3-27b-it", - "name": "Gemma 3 27B", + "gemma-3-12b-it": { + "id": "gemma-3-12b-it", + "name": "Gemma 3 12B", "family": "gemma", "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": false, - "temperature": true, - "release_date": "2025-05-17", - "last_updated": "2025-05-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.49, "output": 0.71 }, - "limit": { "context": 37000, "output": 8192 } - }, - "cortecs/Llama-3.3-70B-Instruct-FP8-Dynamic": { - "id": "cortecs/Llama-3.3-70B-Instruct-FP8-Dynamic", - "name": "Llama 3.3 70B", - "family": "llama", - "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": false, + "structured_output": true, "temperature": true, - "release_date": "2024-12-05", - "last_updated": "2024-12-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-03-13", + "last_updated": "2025-03-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.49, "output": 0.71 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 32768, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "neuralmagic/Mistral-Nemo-Instruct-2407-FP8": { - "id": "neuralmagic/Mistral-Nemo-Instruct-2407-FP8", - "name": "Mistral Nemo", - "family": "mistral", - "attachment": false, - "reasoning": false, + "gemini-2.5-flash": { + "id": "gemini-2.5-flash", + "name": "Gemini 2.5 Flash", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, "tool_call": true, - "structured_output": false, + "structured_output": true, "temperature": true, - "release_date": "2024-07-01", - "last_updated": "2024-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.49, "output": 0.71 }, - "limit": { "context": 128000, "output": 8192 } + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-05", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.03, + "input_audio": 1 + } }, - "neuralmagic/Meta-Llama-3.1-8B-Instruct-FP8": { - "id": "neuralmagic/Meta-Llama-3.1-8B-Instruct-FP8", - "name": "Llama 3.1 8B", - "family": "llama", - "attachment": false, + "gemma-3n-e2b-it": { + "id": "gemma-3n-e2b-it", + "name": "Gemma 3n 2B", + "family": "gemma", + "attachment": true, "reasoning": false, - "tool_call": true, - "structured_output": true, + "tool_call": false, "temperature": true, - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.16, "output": 0.27 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 8192, + "output": 2000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "intfloat/e5-mistral-7b-instruct": { - "id": "intfloat/e5-mistral-7b-instruct", - "name": "E5 Mistral 7B", - "family": "mistral", - "attachment": false, - "reasoning": false, + "gemini-3.1-flash-image-preview": { + "id": "gemini-3.1-flash-image-preview", + "name": "Gemini 3.1 Flash Image (Preview)", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, "tool_call": false, - "structured_output": false, - "temperature": false, - "release_date": "2023-12-11", - "last_updated": "2023-12-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.02, "output": 0.02 }, - "limit": { "context": 4096, "output": 4096 } + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-02-26", + "last_updated": "2026-02-26", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text", "image"] + }, + "open_weights": false, + "limit": { + "context": 131072, + "output": 32768 + }, + "cost": { + "input": 0.5, + "output": 60 + } }, - "Qwen/Qwen3-VL-235B-A22B-Instruct-FP8": { - "id": "Qwen/Qwen3-VL-235B-A22B-Instruct-FP8", - "name": "Qwen3-VL 235B", - "family": "qwen", + "gemini-3.1-flash-lite": { + "id": "gemini-3.1-flash-lite", + "name": "Gemini 3.1 Flash Lite", + "family": "gemini-flash-lite", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": false, + "structured_output": true, "temperature": true, - "release_date": "2024-11-01", - "last_updated": "2024-11-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1.64, "output": 1.91 }, - "limit": { "context": 218000, "output": 8192 } + "knowledge": "2025-01", + "release_date": "2026-05-07", + "last_updated": "2026-05-07", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.25, + "output": 1.5, + "cache_read": 0.025, + "input_audio": 0.5 + } }, - "Qwen/Qwen3-VL-Embedding-8B": { - "id": "Qwen/Qwen3-VL-Embedding-8B", - "name": "Qwen3-VL Embedding 8B", - "family": "qwen", + "gemma-3-4b-it": { + "id": "gemma-3-4b-it", + "name": "Gemma 3 4B", + "family": "gemma", "attachment": true, "reasoning": false, "tool_call": false, - "structured_output": false, - "temperature": false, - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-03-13", + "last_updated": "2025-03-13", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.09, "output": 0.09 }, - "limit": { "context": 32000, "output": 4096 } - } - } - }, - "cloudflare-workers-ai": { - "id": "cloudflare-workers-ai", - "env": ["CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/ai/v1", - "name": "Cloudflare Workers AI", - "doc": "https://developers.cloudflare.com/workers-ai/models/", - "models": { - "@cf/openai/gpt-oss-120b": { - "id": "@cf/openai/gpt-oss-120b", - "name": "GPT OSS 120B", - "attachment": false, - "reasoning": false, - "tool_call": false, + "limit": { + "context": 32768, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "gemini-2.5-flash-preview-04-17": { + "id": "gemini-2.5-flash-preview-04-17", + "name": "Gemini 2.5 Flash Preview 04-17", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-04-17", + "last_updated": "2025-04-17", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.35, "output": 0.75 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.0375 + } }, - "@cf/openai/gpt-oss-20b": { - "id": "@cf/openai/gpt-oss-20b", - "name": "GPT OSS 20B", + "gemini-2.5-pro-preview-tts": { + "id": "gemini-2.5-pro-preview-tts", + "name": "Gemini 2.5 Pro Preview TTS", + "family": "gemini-flash", "attachment": false, "reasoning": false, "tool_call": false, - "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2025-05-01", + "last_updated": "2025-05-01", + "modalities": { + "input": ["text"], + "output": ["audio"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.3 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 8000, + "output": 16000 + }, + "cost": { + "input": 1, + "output": 20 + } }, - "@cf/zai-org/glm-4.7-flash": { - "id": "@cf/zai-org/glm-4.7-flash", - "name": "GLM-4.7-Flash", - "family": "glm-flash", - "attachment": false, + "gemini-2.5-flash-preview-09-2025": { + "id": "gemini-2.5-flash-preview-09-2025", + "name": "Gemini 2.5 Flash Preview 09-25", + "family": "gemini-flash", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-01-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-09-25", + "last_updated": "2025-09-25", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.075, + "input_audio": 1 + } + }, + "gemma-3-27b-it": { + "id": "gemma-3-27b-it", + "name": "Gemma 3 27B", + "family": "gemma", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-03-12", + "last_updated": "2025-03-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.06, "output": 0.4 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "@cf/nvidia/nemotron-3-120b-a12b": { - "id": "@cf/nvidia/nemotron-3-120b-a12b", - "name": "Nemotron 3 Super 120B", - "family": "nemotron", + "gemma-4-26b-a4b-it": { + "id": "gemma-4-26b-a4b-it", + "name": "Gemma 4 26B", + "family": "gemma", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "structured_output": true, "temperature": true, - "release_date": "2026-03-11", - "last_updated": "2026-03-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.5, "output": 1.5 }, - "limit": { "context": 256000, "output": 256000 } + "limit": { + "context": 256000, + "output": 8192 + } }, - "@cf/myshell-ai/melotts": { - "id": "@cf/myshell-ai/melotts", - "name": "MyShell MeloTTS", - "family": "melotts", - "attachment": false, - "reasoning": false, - "tool_call": false, + "gemini-2.5-flash-lite": { + "id": "gemini-2.5-flash-lite", + "name": "Gemini 2.5 Flash Lite", + "family": "gemini-flash-lite", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.4, + "cache_read": 0.01, + "input_audio": 0.3 + } }, - "@cf/google/gemma-3-12b-it": { - "id": "@cf/google/gemma-3-12b-it", - "name": "Gemma 3 12B IT", - "family": "gemma", - "attachment": false, - "reasoning": false, + "gemini-2.5-flash-image-preview": { + "id": "gemini-2.5-flash-image-preview", + "name": "Gemini 2.5 Flash Image (Preview)", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, "tool_call": false, "temperature": true, - "release_date": "2025-04-11", - "last_updated": "2025-04-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-06", + "release_date": "2025-08-26", + "last_updated": "2025-08-26", + "modalities": { + "input": ["text", "image"], + "output": ["text", "image"] + }, "open_weights": false, - "cost": { "input": 0.35, "output": 0.56 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.3, + "output": 30, + "cache_read": 0.075 + } }, - "@cf/ibm-granite/granite-4.0-h-micro": { - "id": "@cf/ibm-granite/granite-4.0-h-micro", - "name": "IBM Granite 4.0 H Micro", - "family": "granite", - "attachment": false, + "gemini-1.5-flash-8b": { + "id": "gemini-1.5-flash-8b", + "name": "Gemini 1.5 Flash-8B", + "family": "gemini-flash", + "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2024-10-03", + "last_updated": "2024-10-03", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.017, "output": 0.11 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 1000000, + "output": 8192 + }, + "cost": { + "input": 0.0375, + "output": 0.15, + "cache_read": 0.01 + } }, - "@cf/ai4bharat/indictrans2-en-indic-1B": { - "id": "@cf/ai4bharat/indictrans2-en-indic-1B", - "name": "IndicTrans2 EN-Indic 1B", - "family": "indictrans", - "attachment": false, - "reasoning": false, - "tool_call": false, + "gemini-live-2.5-flash": { + "id": "gemini-live-2.5-flash", + "name": "Gemini Live 2.5 Flash", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-09-01", + "last_updated": "2025-09-01", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text", "audio"] + }, "open_weights": false, - "cost": { "input": 0.34, "output": 0.34 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 128000, + "output": 8000 + }, + "cost": { + "input": 0.5, + "output": 2, + "input_audio": 3, + "output_audio": 12 + } }, - "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b": { - "id": "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b", - "name": "DeepSeek R1 Distill Qwen 32B", - "family": "deepseek-thinking", - "attachment": false, + "gemini-2.0-flash-lite": { + "id": "gemini-2.0-flash-lite", + "name": "Gemini 2.0 Flash Lite", + "family": "gemini-flash-lite", + "attachment": true, "reasoning": false, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-06", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 4.88 }, - "limit": { "context": 128000, "output": 16384 } - }, - "@cf/qwen/qwq-32b": { - "id": "@cf/qwen/qwq-32b", - "name": "QwQ 32B", - "family": "qwen", + "limit": { + "context": 1048576, + "output": 8192 + }, + "cost": { + "input": 0.075, + "output": 0.3 + } + } + } + }, + "drun": { + "id": "drun", + "env": ["DRUN_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://chat.d.run/v1", + "name": "D.Run (China)", + "doc": "https://www.d.run", + "models": { + "public/deepseek-r1": { + "id": "public/deepseek-r1", + "name": "DeepSeek R1", + "family": "deepseek-thinking", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-04-11", - "last_updated": "2025-04-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.66, "output": 1 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2024-12", + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 32000 + }, + "cost": { + "input": 0.55, + "output": 2.2 + } }, - "@cf/qwen/qwen3-30b-a3b-fp8": { - "id": "@cf/qwen/qwen3-30b-a3b-fp8", - "name": "Qwen3 30B A3B FP8", - "family": "qwen", + "public/minimax-m25": { + "id": "public/minimax-m25", + "name": "MiniMax M2.5", + "family": "minimax", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_details" + }, "temperature": true, - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-03-01", + "last_updated": "2025-03-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.051, "output": 0.34 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.29, + "output": 1.16 + } }, - "@cf/qwen/qwen3-embedding-0.6b": { - "id": "@cf/qwen/qwen3-embedding-0.6b", - "name": "Qwen3 Embedding 0.6B", - "family": "qwen", + "public/deepseek-v3": { + "id": "public/deepseek-v3", + "name": "DeepSeek V3", + "family": "deepseek", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.012, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } - }, - "@cf/qwen/qwen2.5-coder-32b-instruct": { - "id": "@cf/qwen/qwen2.5-coder-32b-instruct", - "name": "Qwen 2.5 Coder 32B Instruct", - "family": "qwen", + "knowledge": "2024-07", + "release_date": "2024-12-26", + "last_updated": "2024-12-26", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 8192 + }, + "cost": { + "input": 0.28, + "output": 1.1 + } + } + } + }, + "moonshotai": { + "id": "moonshotai", + "env": ["MOONSHOT_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.moonshot.ai/v1", + "name": "Moonshot AI", + "doc": "https://platform.moonshot.ai/docs/api/chat", + "models": { + "kimi-k2-0905-preview": { + "id": "kimi-k2-0905-preview", + "name": "Kimi K2 0905", + "family": "kimi", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2025-04-11", - "last_updated": "2025-04-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.66, "output": 1 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2024-10", + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 2.5, + "cache_read": 0.15 + } }, - "@cf/huggingface/distilbert-sst-2-int8": { - "id": "@cf/huggingface/distilbert-sst-2-int8", - "name": "DistilBERT SST-2 INT8", - "family": "distilbert", + "kimi-k2.5": { + "id": "kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi-k2.5", "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.026, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 3, + "cache_read": 0.1 + } }, - "@cf/facebook/bart-large-cnn": { - "id": "@cf/facebook/bart-large-cnn", - "name": "BART Large CNN", - "family": "bart", + "kimi-k2-thinking-turbo": { + "id": "kimi-k2-thinking-turbo", + "name": "Kimi K2 Thinking Turbo", + "family": "kimi-thinking", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-04-09", - "last_updated": "2025-04-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2024-08", + "release_date": "2025-11-06", + "last_updated": "2025-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 1.15, + "output": 8, + "cache_read": 0.15 + } }, - "@cf/baai/bge-base-en-v1.5": { - "id": "@cf/baai/bge-base-en-v1.5", - "name": "BGE Base EN v1.5", - "family": "bge", + "kimi-k2.6": { + "id": "kimi-k2.6", + "name": "Kimi K2.6", + "family": "kimi-k2.6", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.95, + "output": 4, + "cache_read": 0.16 + } + }, + "kimi-k2-turbo-preview": { + "id": "kimi-k2-turbo-preview", + "name": "Kimi K2 Turbo", + "family": "kimi", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.067, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2024-10", + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 2.4, + "output": 10, + "cache_read": 0.6 + } }, - "@cf/baai/bge-small-en-v1.5": { - "id": "@cf/baai/bge-small-en-v1.5", - "name": "BGE Small EN v1.5", - "family": "bge", + "kimi-k2-0711-preview": { + "id": "kimi-k2-0711-preview", + "name": "Kimi K2 0711", + "family": "kimi", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.02, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2024-10", + "release_date": "2025-07-14", + "last_updated": "2025-07-14", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.6, + "output": 2.5, + "cache_read": 0.15 + } }, - "@cf/baai/bge-large-en-v1.5": { - "id": "@cf/baai/bge-large-en-v1.5", - "name": "BGE Large EN v1.5", - "family": "bge", + "kimi-k2-thinking": { + "id": "kimi-k2-thinking", + "name": "Kimi K2 Thinking", + "family": "kimi-thinking", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2024-08", + "release_date": "2025-11-06", + "last_updated": "2025-11-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.6, + "output": 2.5, + "cache_read": 0.15 + } + } + } + }, + "berget": { + "id": "berget", + "env": ["BERGET_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.berget.ai/v1", + "name": "Berget.AI", + "doc": "https://api.berget.ai", + "models": { + "zai-org/GLM-4.7": { + "id": "zai-org/GLM-4.7", + "name": "GLM 4.7", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-12", + "release_date": "2026-01-19", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.77, + "output": 2.75 + } }, - "@cf/baai/bge-reranker-base": { - "id": "@cf/baai/bge-reranker-base", - "name": "BGE Reranker Base", - "family": "bge", + "mistralai/Mistral-Small-3.2-24B-Instruct-2506": { + "id": "mistralai/Mistral-Small-3.2-24B-Instruct-2506", + "name": "Mistral Small 3.2 24B Instruct 2506", + "family": "mistral-small", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-04-09", - "last_updated": "2025-04-09", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.0031, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2025-09", + "release_date": "2025-10-01", + "last_updated": "2025-10-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32000, + "output": 8192 + }, + "cost": { + "input": 0.33, + "output": 0.33 + } }, - "@cf/baai/bge-m3": { - "id": "@cf/baai/bge-m3", - "name": "BGE M3", - "family": "bge", + "mistralai/Mistral-Medium-3.5-128B": { + "id": "mistralai/Mistral-Medium-3.5-128B", + "name": "Mistral Medium 3.5 128B", + "family": "mistral-medium", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2026-04", + "release_date": "2026-04-29", + "last_updated": "2026-04-29", + "modalities": { + "input": ["image", "text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 1.65, + "output": 5.5 + } + }, + "meta-llama/Llama-3.3-70B-Instruct": { + "id": "meta-llama/Llama-3.3-70B-Instruct", + "name": "Llama 3.3 70B Instruct", + "family": "llama", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.012, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2023-12", + "release_date": "2025-04-27", + "last_updated": "2025-04-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.99, + "output": 0.99 + } }, - "@cf/pipecat-ai/smart-turn-v2": { - "id": "@cf/pipecat-ai/smart-turn-v2", - "name": "Pipecat Smart Turn v2", - "family": "smart-turn", + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "name": "GPT-OSS-120B", + "family": "gpt-oss", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2025-08", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.44, + "output": 0.99 + } }, - "@cf/moonshotai/kimi-k2.5": { - "id": "@cf/moonshotai/kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi", + "google/gemma-4-31B-it": { + "id": "google/gemma-4-31B-it", + "name": "Gemma 4 31B Instruct", + "family": "gemma", "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-12", + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": ["audio", "image", "text", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 3, "cache_read": 0.1 }, - "limit": { "context": 256000, "output": 256000 } - }, - "@cf/deepgram/aura-2-en": { - "id": "@cf/deepgram/aura-2-en", - "name": "Deepgram Aura 2 (EN)", - "family": "aura", + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0.275, + "output": 0.55 + } + } + } + }, + "github-models": { + "id": "github-models", + "env": ["GITHUB_TOKEN"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://models.github.ai/inference", + "name": "GitHub Models", + "doc": "https://docs.github.com/en/github-models", + "models": { + "deepseek/deepseek-v3-0324": { + "id": "deepseek/deepseek-v3-0324", + "name": "DeepSeek-V3-0324", + "family": "deepseek", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2024-06", + "release_date": "2025-03-24", + "last_updated": "2025-03-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "@cf/deepgram/nova-3": { - "id": "@cf/deepgram/nova-3", - "name": "Deepgram Nova 3", - "family": "nova", + "deepseek/deepseek-r1": { + "id": "deepseek/deepseek-r1", + "name": "DeepSeek-R1", + "family": "deepseek-thinking", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2024-06", + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 65536, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "@cf/deepgram/aura-2-es": { - "id": "@cf/deepgram/aura-2-es", - "name": "Deepgram Aura 2 (ES)", - "family": "aura", + "deepseek/deepseek-r1-0528": { + "id": "deepseek/deepseek-r1-0528", + "name": "DeepSeek-R1-0528", + "family": "deepseek-thinking", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-11-14", - "last_updated": "2025-11-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2024-06", + "release_date": "2025-05-28", + "last_updated": "2025-05-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 65536, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "@cf/mistral/mistral-7b-instruct-v0.1": { - "id": "@cf/mistral/mistral-7b-instruct-v0.1", - "name": "Mistral 7B Instruct v0.1", - "family": "mistral", + "ai21-labs/ai21-jamba-1.5-mini": { + "id": "ai21-labs/ai21-jamba-1.5-mini", + "name": "AI21 Jamba 1.5 Mini", + "family": "jamba", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-03", + "release_date": "2024-08-29", + "last_updated": "2024-08-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.11, "output": 0.19 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 256000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "@cf/aisingapore/gemma-sea-lion-v4-27b-it": { - "id": "@cf/aisingapore/gemma-sea-lion-v4-27b-it", - "name": "Gemma SEA-LION v4 27B IT", - "family": "gemma", + "ai21-labs/ai21-jamba-1.5-large": { + "id": "ai21-labs/ai21-jamba-1.5-large", + "name": "AI21 Jamba 1.5 Large", + "family": "jamba", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-03", + "release_date": "2024-08-29", + "last_updated": "2024-08-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.35, "output": 0.56 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 256000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "@cf/meta/llama-3.3-70b-instruct-fp8-fast": { - "id": "@cf/meta/llama-3.3-70b-instruct-fp8-fast", - "name": "Llama 3.3 70B Instruct FP8 Fast", - "family": "llama", + "microsoft/phi-3.5-mini-instruct": { + "id": "microsoft/phi-3.5-mini-instruct", + "name": "Phi-3.5-mini instruct (128k)", + "family": "phi", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.29, "output": 2.25 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2023-10", + "release_date": "2024-08-20", + "last_updated": "2024-08-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "@cf/meta/llama-3-8b-instruct-awq": { - "id": "@cf/meta/llama-3-8b-instruct-awq", - "name": "Llama 3 8B Instruct AWQ", - "family": "llama", + "microsoft/phi-3-medium-4k-instruct": { + "id": "microsoft/phi-3-medium-4k-instruct", + "name": "Phi-3-medium instruct (4k)", + "family": "phi", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.12, "output": 0.27 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2023-10", + "release_date": "2024-04-23", + "last_updated": "2024-04-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 4096, + "output": 1024 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "@cf/meta/llama-3.2-1b-instruct": { - "id": "@cf/meta/llama-3.2-1b-instruct", - "name": "Llama 3.2 1B Instruct", - "family": "llama", + "microsoft/phi-3.5-moe-instruct": { + "id": "microsoft/phi-3.5-moe-instruct", + "name": "Phi-3.5-MoE instruct (128k)", + "family": "phi", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.027, "output": 0.2 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2023-10", + "release_date": "2024-08-20", + "last_updated": "2024-08-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "@cf/meta/m2m100-1.2b": { - "id": "@cf/meta/m2m100-1.2b", - "name": "M2M100 1.2B", - "family": "m2m", + "microsoft/phi-3-mini-128k-instruct": { + "id": "microsoft/phi-3-mini-128k-instruct", + "name": "Phi-3-mini instruct (128k)", + "family": "phi", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.34, "output": 0.34 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2023-10", + "release_date": "2024-04-23", + "last_updated": "2024-04-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "@cf/meta/llama-3.1-8b-instruct": { - "id": "@cf/meta/llama-3.1-8b-instruct", - "name": "Llama 3.1 8B Instruct", - "family": "llama", + "microsoft/phi-4-mini-instruct": { + "id": "microsoft/phi-4-mini-instruct", + "name": "Phi-4-mini-instruct", + "family": "phi", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.28, "output": 0.8299999999999998 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2023-10", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "@cf/meta/llama-4-scout-17b-16e-instruct": { - "id": "@cf/meta/llama-4-scout-17b-16e-instruct", - "name": "Llama 4 Scout 17B 16E Instruct", - "family": "llama", + "microsoft/phi-4-reasoning": { + "id": "microsoft/phi-4-reasoning", + "name": "Phi-4-Reasoning", + "family": "phi", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.27, "output": 0.85 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2023-10", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "@cf/meta/llama-3.2-11b-vision-instruct": { - "id": "@cf/meta/llama-3.2-11b-vision-instruct", - "name": "Llama 3.2 11B Vision Instruct", - "family": "llama", + "microsoft/phi-3-small-8k-instruct": { + "id": "microsoft/phi-3-small-8k-instruct", + "name": "Phi-3-small instruct (8k)", + "family": "phi", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.049, "output": 0.68 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2023-10", + "release_date": "2024-04-23", + "last_updated": "2024-04-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 2048 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "@cf/meta/llama-3-8b-instruct": { - "id": "@cf/meta/llama-3-8b-instruct", - "name": "Llama 3 8B Instruct", - "family": "llama", + "microsoft/phi-3.5-vision-instruct": { + "id": "microsoft/phi-3.5-vision-instruct", + "name": "Phi-3.5-vision instruct (128k)", + "family": "phi", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.28, "output": 0.83 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2023-10", + "release_date": "2024-08-20", + "last_updated": "2024-08-20", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "@cf/meta/llama-guard-3-8b": { - "id": "@cf/meta/llama-guard-3-8b", - "name": "Llama Guard 3 8B", - "family": "llama", + "microsoft/phi-3-mini-4k-instruct": { + "id": "microsoft/phi-3-mini-4k-instruct", + "name": "Phi-3-mini instruct (4k)", + "family": "phi", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.48, "output": 0.03 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2023-10", + "release_date": "2024-04-23", + "last_updated": "2024-04-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 4096, + "output": 1024 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "@cf/meta/llama-3.2-3b-instruct": { - "id": "@cf/meta/llama-3.2-3b-instruct", - "name": "Llama 3.2 3B Instruct", - "family": "llama", + "microsoft/phi-4-mini-reasoning": { + "id": "microsoft/phi-4-mini-reasoning", + "name": "Phi-4-mini-reasoning", + "family": "phi", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.051, "output": 0.34 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2023-10", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "@cf/meta/llama-3.1-8b-instruct-awq": { - "id": "@cf/meta/llama-3.1-8b-instruct-awq", - "name": "Llama 3.1 8B Instruct AWQ", - "family": "llama", + "microsoft/phi-3-small-128k-instruct": { + "id": "microsoft/phi-3-small-128k-instruct", + "name": "Phi-3-small instruct (128k)", + "family": "phi", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.12, "output": 0.27 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2023-10", + "release_date": "2024-04-23", + "last_updated": "2024-04-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "@cf/meta/llama-3.1-8b-instruct-fp8": { - "id": "@cf/meta/llama-3.1-8b-instruct-fp8", - "name": "Llama 3.1 8B Instruct FP8", - "family": "llama", + "microsoft/phi-3-medium-128k-instruct": { + "id": "microsoft/phi-3-medium-128k-instruct", + "name": "Phi-3-medium instruct (128k)", + "family": "phi", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.29 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2023-10", + "release_date": "2024-04-23", + "last_updated": "2024-04-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "@cf/meta/llama-2-7b-chat-fp16": { - "id": "@cf/meta/llama-2-7b-chat-fp16", - "name": "Llama 2 7B Chat FP16", - "family": "llama", + "microsoft/phi-4": { + "id": "microsoft/phi-4", + "name": "Phi-4", + "family": "phi", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-04-03", - "last_updated": "2025-04-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.56, "output": 6.67 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2023-10", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 16000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "@cf/pfnet/plamo-embedding-1b": { - "id": "@cf/pfnet/plamo-embedding-1b", - "name": "PLaMo Embedding 1B", - "family": "plamo", + "microsoft/phi-4-multimodal-instruct": { + "id": "microsoft/phi-4-multimodal-instruct", + "name": "Phi-4-multimodal-instruct", + "family": "phi", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.019, "output": 0 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2023-10", + "release_date": "2024-12-11", + "last_updated": "2024-12-11", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "@cf/mistralai/mistral-small-3.1-24b-instruct": { - "id": "@cf/mistralai/mistral-small-3.1-24b-instruct", - "name": "Mistral Small 3.1 24B Instruct", - "family": "mistral-small", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2025-04-11", - "last_updated": "2025-04-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.35, "output": 0.56 }, - "limit": { "context": 128000, "output": 16384 } - } - } - }, - "siliconflow": { - "id": "siliconflow", - "env": ["SILICONFLOW_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.siliconflow.com/v1", - "name": "SiliconFlow", - "doc": "https://cloud.siliconflow.com/models", - "models": { - "THUDM/GLM-Z1-32B-0414": { - "id": "THUDM/GLM-Z1-32B-0414", - "name": "THUDM/GLM-Z1-32B-0414", - "family": "glm-z", + "microsoft/mai-ds-r1": { + "id": "microsoft/mai-ds-r1", + "name": "MAI-DS-R1", + "family": "mai", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-04-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-06", + "release_date": "2025-01-20", + "last_updated": "2025-01-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.14, "output": 0.57 }, - "limit": { "context": 131000, "output": 131000 } + "limit": { + "context": 65536, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "THUDM/GLM-Z1-9B-0414": { - "id": "THUDM/GLM-Z1-9B-0414", - "name": "THUDM/GLM-Z1-9B-0414", - "family": "glm-z", + "cohere/cohere-command-r-08-2024": { + "id": "cohere/cohere-command-r-08-2024", + "name": "Cohere Command R 08-2024", + "family": "command-r", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-04-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-03", + "release_date": "2024-08-01", + "last_updated": "2024-08-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.086, "output": 0.086 }, - "limit": { "context": 131000, "output": 131000 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "THUDM/GLM-4-32B-0414": { - "id": "THUDM/GLM-4-32B-0414", - "name": "THUDM/GLM-4-32B-0414", - "family": "glm", + "cohere/cohere-command-a": { + "id": "cohere/cohere-command-a", + "name": "Cohere Command A", + "family": "command-a", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-04-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-03", + "release_date": "2024-11-01", + "last_updated": "2024-11-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.27, "output": 0.27 }, - "limit": { "context": 33000, "output": 33000 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "THUDM/GLM-4-9B-0414": { - "id": "THUDM/GLM-4-9B-0414", - "name": "THUDM/GLM-4-9B-0414", - "family": "glm", + "cohere/cohere-command-r-plus": { + "id": "cohere/cohere-command-r-plus", + "name": "Cohere Command R+", + "family": "command-r", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-04-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-03", + "release_date": "2024-04-04", + "last_updated": "2024-08-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.086, "output": 0.086 }, - "limit": { "context": 33000, "output": 33000 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "openai/gpt-oss-120b": { - "id": "openai/gpt-oss-120b", - "name": "openai/gpt-oss-120b", - "family": "gpt-oss", + "cohere/cohere-command-r": { + "id": "cohere/cohere-command-r", + "name": "Cohere Command R", + "family": "command-r", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-08-13", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-03", + "release_date": "2024-03-11", + "last_updated": "2024-08-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.05, "output": 0.45 }, - "limit": { "context": 131000, "output": 8000 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "openai/gpt-oss-20b": { - "id": "openai/gpt-oss-20b", - "name": "openai/gpt-oss-20b", - "family": "gpt-oss", + "cohere/cohere-command-r-plus-08-2024": { + "id": "cohere/cohere-command-r-plus-08-2024", + "name": "Cohere Command R+ 08-2024", + "family": "command-r", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-08-13", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-03", + "release_date": "2024-08-01", + "last_updated": "2024-08-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.04, "output": 0.18 }, - "limit": { "context": 131000, "output": 8000 } + "limit": { + "context": 128000, + "output": 4096 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "zai-org/GLM-4.5-Air": { - "id": "zai-org/GLM-4.5-Air", - "name": "zai-org/GLM-4.5-Air", - "family": "glm-air", + "xai/grok-3-mini": { + "id": "xai/grok-3-mini", + "name": "Grok 3 Mini", + "family": "grok", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-28", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2024-12-09", + "last_updated": "2024-12-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.14, "output": 0.86 }, - "limit": { "context": 131000, "output": 131000 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "zai-org/GLM-4.7": { - "id": "zai-org/GLM-4.7", - "name": "zai-org/GLM-4.7", - "family": "glm", + "xai/grok-3": { + "id": "xai/grok-3", + "name": "Grok 3", + "family": "grok", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-10", + "release_date": "2024-12-09", + "last_updated": "2024-12-09", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.6, "output": 2.2 }, - "limit": { "context": 205000, "output": 205000 } + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "zai-org/GLM-4.6V": { - "id": "zai-org/GLM-4.6V", - "name": "zai-org/GLM-4.6V", - "family": "glm", - "attachment": true, + "openai/o1-mini": { + "id": "openai/o1-mini", + "name": "OpenAI o1-mini", + "family": "o-mini", + "attachment": false, "reasoning": true, - "tool_call": true, - "structured_output": false, - "temperature": true, - "release_date": "2025-12-07", - "last_updated": "2025-12-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "tool_call": false, + "temperature": false, + "knowledge": "2023-10", + "release_date": "2024-09-12", + "last_updated": "2024-12-17", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 0.9 }, - "limit": { "context": 131000, "output": 131000 } + "limit": { + "context": 128000, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "zai-org/GLM-4.5V": { - "id": "zai-org/GLM-4.5V", - "name": "zai-org/GLM-4.5V", - "family": "glm", + "openai/gpt-4o-mini": { + "id": "openai/gpt-4o-mini", + "name": "GPT-4o mini", + "family": "gpt-mini", "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-08-13", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2023-10", + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.14, "output": 0.86 }, - "limit": { "context": 66000, "output": 66000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "zai-org/GLM-4.5": { - "id": "zai-org/GLM-4.5", - "name": "zai-org/GLM-4.5", - "family": "glm", + "openai/o4-mini": { + "id": "openai/o4-mini", + "name": "OpenAI o4-mini", + "family": "o-mini", "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-07-28", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "reasoning": true, + "tool_call": false, + "temperature": false, + "knowledge": "2024-04", + "release_date": "2025-01-31", + "last_updated": "2025-01-31", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 131000, "output": 131000 } + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "zai-org/GLM-5": { - "id": "zai-org/GLM-5", - "name": "zai-org/GLM-5", - "family": "glm", + "openai/o1-preview": { + "id": "openai/o1-preview", + "name": "OpenAI o1-preview", + "family": "o", "attachment": false, "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, - "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1, "output": 3.2 }, - "limit": { "context": 205000, "output": 205000 } + "tool_call": false, + "temperature": false, + "knowledge": "2023-10", + "release_date": "2024-09-12", + "last_updated": "2024-09-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "zai-org/GLM-4.6": { - "id": "zai-org/GLM-4.6", - "name": "zai-org/GLM-4.6", - "family": "glm", + "openai/o1": { + "id": "openai/o1", + "name": "OpenAI o1", + "family": "o", "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": false, + "knowledge": "2023-10", + "release_date": "2024-09-12", + "last_updated": "2024-12-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "openai/o3-mini": { + "id": "openai/o3-mini", + "name": "OpenAI o3-mini", + "family": "o-mini", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": false, + "knowledge": "2024-04", + "release_date": "2025-01-31", + "last_updated": "2025-01-31", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "openai/gpt-4.1-nano": { + "id": "openai/gpt-4.1-nano", + "name": "GPT-4.1-nano", + "family": "gpt-nano", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-04", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 1.9 }, - "limit": { "context": 205000, "output": 205000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "nex-agi/DeepSeek-V3.1-Nex-N1": { - "id": "nex-agi/DeepSeek-V3.1-Nex-N1", - "name": "nex-agi/DeepSeek-V3.1-Nex-N1", - "family": "deepseek", + "openai/o3": { + "id": "openai/o3", + "name": "OpenAI o3", + "family": "o", "attachment": false, "reasoning": true, + "tool_call": false, + "temperature": false, + "knowledge": "2024-04", + "release_date": "2025-01-31", + "last_updated": "2025-01-31", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 100000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "openai/gpt-4o": { + "id": "openai/gpt-4o", + "name": "GPT-4o", + "family": "gpt", + "attachment": true, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-01-01", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2023-10", + "release_date": "2024-05-13", + "last_updated": "2024-05-13", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.5, "output": 2 }, - "limit": { "context": 131000, "output": 131000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "stepfun-ai/Step-3.5-Flash": { - "id": "stepfun-ai/Step-3.5-Flash", - "name": "stepfun-ai/Step-3.5-Flash", - "family": "step", - "attachment": false, - "reasoning": true, + "openai/gpt-4.1": { + "id": "openai/gpt-4.1", + "name": "GPT-4.1", + "family": "gpt", + "attachment": true, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.3 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "baidu/ERNIE-4.5-300B-A47B": { - "id": "baidu/ERNIE-4.5-300B-A47B", - "name": "baidu/ERNIE-4.5-300B-A47B", - "family": "ernie", - "attachment": false, + "openai/gpt-4.1-mini": { + "id": "openai/gpt-4.1-mini", + "name": "GPT-4.1-mini", + "family": "gpt-mini", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-02", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.28, "output": 1.1 }, - "limit": { "context": 131000, "output": 131000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "meta-llama/Meta-Llama-3.1-8B-Instruct": { - "id": "meta-llama/Meta-Llama-3.1-8B-Instruct", - "name": "meta-llama/Meta-Llama-3.1-8B-Instruct", + "meta/llama-4-scout-17b-16e-instruct": { + "id": "meta/llama-4-scout-17b-16e-instruct", + "name": "Llama 4 Scout 17B 16E Instruct", "family": "llama", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-04-23", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.06, "output": 0.06 }, - "limit": { "context": 33000, "output": 4000 } + "knowledge": "2024-12", + "release_date": "2025-01-31", + "last_updated": "2025-01-31", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "MiniMaxAI/MiniMax-M2.1": { - "id": "MiniMaxAI/MiniMax-M2.1", - "name": "MiniMaxAI/MiniMax-M2.1", - "family": "minimax", + "meta/meta-llama-3.1-8b-instruct": { + "id": "meta/meta-llama-3.1-8b-instruct", + "name": "Meta-Llama-3.1-8B-Instruct", + "family": "llama", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 197000, "output": 131000 } + "knowledge": "2023-12", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "MiniMaxAI/MiniMax-M2.5": { - "id": "MiniMaxAI/MiniMax-M2.5", - "name": "MiniMaxAI/MiniMax-M2.5", - "family": "minimax", + "meta/llama-3.3-70b-instruct": { + "id": "meta/llama-3.3-70b-instruct", + "name": "Llama-3.3-70B-Instruct", + "family": "llama", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": false, "temperature": true, - "release_date": "2026-02-15", - "last_updated": "2026-02-15", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 1.2 }, - "limit": { "context": 197000, "output": 131000 } + "knowledge": "2023-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "deepseek-ai/deepseek-vl2": { - "id": "deepseek-ai/deepseek-vl2", - "name": "deepseek-ai/deepseek-vl2", - "family": "deepseek", - "attachment": true, - "reasoning": false, + "meta/meta-llama-3-70b-instruct": { + "id": "meta/meta-llama-3-70b-instruct", + "name": "Meta-Llama-3-70B-Instruct", + "family": "llama", + "attachment": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-12-13", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.15 }, - "limit": { "context": 4000, "output": 4000 } + "knowledge": "2023-12", + "release_date": "2024-04-18", + "last_updated": "2024-04-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 2048 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B": { - "id": "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", - "name": "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", - "family": "qwen", + "meta/llama-3.2-90b-vision-instruct": { + "id": "meta/llama-3.2-90b-vision-instruct", + "name": "Llama-3.2-90B-Vision-Instruct", + "family": "llama", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-01-20", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.18, "output": 0.18 }, - "limit": { "context": 131000, "output": 131000 } + "knowledge": "2023-12", + "release_date": "2024-09-25", + "last_updated": "2024-09-25", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "deepseek-ai/DeepSeek-V3.1": { - "id": "deepseek-ai/DeepSeek-V3.1", - "name": "deepseek-ai/DeepSeek-V3.1", - "family": "deepseek", + "meta/llama-3.2-11b-vision-instruct": { + "id": "meta/llama-3.2-11b-vision-instruct", + "name": "Llama-3.2-11B-Vision-Instruct", + "family": "llama", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-08-25", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.27, "output": 1 }, - "limit": { "context": 164000, "output": 164000 } + "knowledge": "2023-12", + "release_date": "2024-09-25", + "last_updated": "2024-09-25", + "modalities": { + "input": ["text", "image", "audio"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "deepseek-ai/DeepSeek-V3.2-Exp": { - "id": "deepseek-ai/DeepSeek-V3.2-Exp", - "name": "deepseek-ai/DeepSeek-V3.2-Exp", - "family": "deepseek", + "meta/meta-llama-3.1-405b-instruct": { + "id": "meta/meta-llama-3.1-405b-instruct", + "name": "Meta-Llama-3.1-405B-Instruct", + "family": "llama", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-10", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.27, "output": 0.41 }, - "limit": { "context": 164000, "output": 164000 } + "knowledge": "2023-12", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "deepseek-ai/DeepSeek-R1": { - "id": "deepseek-ai/DeepSeek-R1", - "name": "deepseek-ai/DeepSeek-R1", - "family": "deepseek-thinking", + "meta/meta-llama-3.1-70b-instruct": { + "id": "meta/meta-llama-3.1-70b-instruct", + "name": "Meta-Llama-3.1-70B-Instruct", + "family": "llama", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-05-28", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 2.18 }, - "limit": { "context": 164000, "output": 164000 } + "knowledge": "2023-12", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "deepseek-ai/DeepSeek-V3.1-Terminus": { - "id": "deepseek-ai/DeepSeek-V3.1-Terminus", - "name": "deepseek-ai/DeepSeek-V3.1-Terminus", - "family": "deepseek", + "meta/meta-llama-3-8b-instruct": { + "id": "meta/meta-llama-3-8b-instruct", + "name": "Meta-Llama-3-8B-Instruct", + "family": "llama", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-09-29", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.27, "output": 1 }, - "limit": { "context": 164000, "output": 164000 } + "knowledge": "2023-12", + "release_date": "2024-04-18", + "last_updated": "2024-04-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 2048 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B": { - "id": "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B", - "name": "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B", - "family": "qwen", + "meta/llama-4-maverick-17b-128e-instruct-fp8": { + "id": "meta/llama-4-maverick-17b-128e-instruct-fp8", + "name": "Llama 4 Maverick 17B 128E Instruct FP8", + "family": "llama", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-01-20", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.1 }, - "limit": { "context": 131000, "output": 131000 } + "knowledge": "2024-12", + "release_date": "2025-01-31", + "last_updated": "2025-01-31", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "deepseek-ai/DeepSeek-V3.2": { - "id": "deepseek-ai/DeepSeek-V3.2", - "name": "deepseek-ai/DeepSeek-V3.2", - "family": "deepseek", + "core42/jais-30b-chat": { + "id": "core42/jais-30b-chat", + "name": "JAIS 30b Chat", + "family": "jais", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-12-03", - "last_updated": "2025-12-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.27, "output": 0.42 }, - "limit": { "context": 164000, "output": 164000 } + "knowledge": "2023-03", + "release_date": "2023-08-30", + "last_updated": "2023-08-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 2048 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "deepseek-ai/DeepSeek-V3": { - "id": "deepseek-ai/DeepSeek-V3", - "name": "deepseek-ai/DeepSeek-V3", - "family": "deepseek", + "mistral-ai/mistral-nemo": { + "id": "mistral-ai/mistral-nemo", + "name": "Mistral Nemo", + "family": "mistral-nemo", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-12-26", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 1 }, - "limit": { "context": 164000, "output": 164000 } + "knowledge": "2024-03", + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "ByteDance-Seed/Seed-OSS-36B-Instruct": { - "id": "ByteDance-Seed/Seed-OSS-36B-Instruct", - "name": "ByteDance-Seed/Seed-OSS-36B-Instruct", - "family": "seed", + "mistral-ai/ministral-3b": { + "id": "mistral-ai/ministral-3b", + "name": "Ministral 3B", + "family": "ministral", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-09-04", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.21, "output": 0.57 }, - "limit": { "context": 262000, "output": 262000 } + "knowledge": "2024-03", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "Qwen/Qwen2.5-Coder-32B-Instruct": { - "id": "Qwen/Qwen2.5-Coder-32B-Instruct", - "name": "Qwen/Qwen2.5-Coder-32B-Instruct", - "family": "qwen", + "mistral-ai/mistral-large-2411": { + "id": "mistral-ai/mistral-large-2411", + "name": "Mistral Large 24.11", + "family": "mistral-large", "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2024-11-11", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.18, "output": 0.18 }, - "limit": { "context": 33000, "output": 4000 } - }, - "Qwen/Qwen3-Omni-30B-A3B-Thinking": { - "id": "Qwen/Qwen3-Omni-30B-A3B-Thinking", - "name": "Qwen/Qwen3-Omni-30B-A3B-Thinking", - "family": "qwen", - "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-04", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image", "audio"], "output": ["text"] }, + "knowledge": "2024-09", + "release_date": "2024-11-01", + "last_updated": "2024-11-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 66000, "output": 66000 } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "Qwen/Qwen3-Next-80B-A3B-Thinking": { - "id": "Qwen/Qwen3-Next-80B-A3B-Thinking", - "name": "Qwen/Qwen3-Next-80B-A3B-Thinking", - "family": "qwen", + "mistral-ai/mistral-small-2503": { + "id": "mistral-ai/mistral-small-2503", + "name": "Mistral Small 3.1", + "family": "mistral-small", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-09-25", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.14, "output": 0.57 }, - "limit": { "context": 262000, "output": 262000 } - }, - "Qwen/Qwen3-VL-30B-A3B-Instruct": { - "id": "Qwen/Qwen3-VL-30B-A3B-Instruct", - "name": "Qwen/Qwen3-VL-30B-A3B-Instruct", - "family": "qwen", - "attachment": true, - "reasoning": false, - "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-05", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-09", + "release_date": "2025-03-01", + "last_updated": "2025-03-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.29, "output": 1 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "Qwen/Qwen3-VL-235B-A22B-Thinking": { - "id": "Qwen/Qwen3-VL-235B-A22B-Thinking", - "name": "Qwen/Qwen3-VL-235B-A22B-Thinking", - "family": "qwen", - "attachment": true, + "mistral-ai/mistral-medium-2505": { + "id": "mistral-ai/mistral-medium-2505", + "name": "Mistral Medium 3 (25.05)", + "family": "mistral-medium", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-04", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-09", + "release_date": "2025-05-01", + "last_updated": "2025-05-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.45, "output": 3.5 }, - "limit": { "context": 262000, "output": 262000 } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "Qwen/Qwen3-30B-A3B-Thinking-2507": { - "id": "Qwen/Qwen3-30B-A3B-Thinking-2507", - "name": "Qwen/Qwen3-30B-A3B-Thinking-2507", - "family": "qwen", + "mistral-ai/codestral-2501": { + "id": "mistral-ai/codestral-2501", + "name": "Codestral 25.01", + "family": "codestral", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-31", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-03", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.09, "output": 0.3 }, - "limit": { "context": 262000, "output": 131000 } - }, - "Qwen/Qwen2.5-14B-Instruct": { - "id": "Qwen/Qwen2.5-14B-Instruct", - "name": "Qwen/Qwen2.5-14B-Instruct", - "family": "qwen", + "limit": { + "context": 32000, + "output": 8192 + }, + "cost": { + "input": 0, + "output": 0 + } + } + } + }, + "neuralwatt": { + "id": "neuralwatt", + "env": ["NEURALWATT_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.neuralwatt.com/v1", + "name": "Neuralwatt", + "doc": "https://portal.neuralwatt.com/docs", + "models": { + "glm-5-fast": { + "id": "glm-5-fast", + "name": "GLM 5 Fast", + "family": "glm", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-09-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.1 }, - "limit": { "context": 33000, "output": 4000 } + "release_date": "2026-04-07", + "last_updated": "2026-04-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 200000, + "output": 200000 + }, + "cost": { + "input": 1.1, + "output": 3.6 + } }, - "Qwen/Qwen3-32B": { - "id": "Qwen/Qwen3-32B", - "name": "Qwen/Qwen3-32B", - "family": "qwen", - "attachment": false, + "kimi-k2.6-fast": { + "id": "kimi-k2.6-fast", + "name": "Kimi K2.6 Fast", + "family": "kimi", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-04-30", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.14, "output": 0.57 }, - "limit": { "context": 131000, "output": 131000 } + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.69, + "output": 3.22 + } }, - "Qwen/Qwen3-14B": { - "id": "Qwen/Qwen3-14B", - "name": "Qwen/Qwen3-14B", + "qwen3.5-397b-fast": { + "id": "qwen3.5-397b-fast", + "name": "Qwen3.5 397B Fast", "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-04-30", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.07, "output": 0.28 }, - "limit": { "context": 131000, "output": 131000 } + "release_date": "2026-02-01", + "last_updated": "2026-02-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.69, + "output": 4.14 + } }, - "Qwen/Qwen3-235B-A22B": { - "id": "Qwen/Qwen3-235B-A22B", - "name": "Qwen/Qwen3-235B-A22B", - "family": "qwen", + "glm-5.1-fast": { + "id": "glm-5.1-fast", + "name": "GLM 5.1 Fast", + "family": "glm", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-04-30", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.35, "output": 1.42 }, - "limit": { "context": 131000, "output": 131000 } + "release_date": "2026-04-07", + "last_updated": "2026-04-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 200000, + "output": 200000 + }, + "cost": { + "input": 1.1, + "output": 3.6 + } }, - "Qwen/Qwen2.5-72B-Instruct-128K": { - "id": "Qwen/Qwen2.5-72B-Instruct-128K", - "name": "Qwen/Qwen2.5-72B-Instruct-128K", - "family": "qwen", - "attachment": false, + "qwen3.6-35b-fast": { + "id": "qwen3.6-35b-fast", + "name": "Qwen3.6 35B Fast", + "family": "qwen3.6", + "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-09-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.59, "output": 0.59 }, - "limit": { "context": 131000, "output": 4000 } + "release_date": "2026-04-01", + "last_updated": "2026-04-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.05, + "output": 0.1 + } }, - "Qwen/Qwen3-235B-A22B-Thinking-2507": { - "id": "Qwen/Qwen3-235B-A22B-Thinking-2507", - "name": "Qwen/Qwen3-235B-A22B-Thinking-2507", - "family": "qwen", - "attachment": false, - "reasoning": true, + "kimi-k2.5-fast": { + "id": "kimi-k2.5-fast", + "name": "Kimi K2.5 Fast", + "family": "kimi", + "attachment": true, + "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-28", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.13, "output": 0.6 }, - "limit": { "context": 262000, "output": 262000 } + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.52, + "output": 2.59 + } }, - "Qwen/Qwen3-Next-80B-A3B-Instruct": { - "id": "Qwen/Qwen3-Next-80B-A3B-Instruct", - "name": "Qwen/Qwen3-Next-80B-A3B-Instruct", + "Qwen/Qwen3.5-397B-A17B-FP8": { + "id": "Qwen/Qwen3.5-397B-A17B-FP8", + "name": "Qwen3.5 397B A17B FP8", "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-09-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.14, "output": 1.4 }, - "limit": { "context": 262000, "output": 262000 } + "release_date": "2026-02-01", + "last_updated": "2026-02-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.69, + "output": 4.14 + } }, - "Qwen/Qwen2.5-VL-72B-Instruct": { - "id": "Qwen/Qwen2.5-VL-72B-Instruct", - "name": "Qwen/Qwen2.5-VL-72B-Instruct", - "family": "qwen", + "Qwen/Qwen3.6-35B-A3B": { + "id": "Qwen/Qwen3.6-35B-A3B", + "name": "Qwen3.6 35B A3B", + "family": "qwen3.6", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-01-28", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.59, "output": 0.59 }, - "limit": { "context": 131000, "output": 4000 } + "release_date": "2026-04-01", + "last_updated": "2026-04-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.05, + "output": 0.1 + } }, - "Qwen/Qwen3-VL-8B-Thinking": { - "id": "Qwen/Qwen3-VL-8B-Thinking", - "name": "Qwen/Qwen3-VL-8B-Thinking", - "family": "qwen", - "attachment": true, + "zai-org/GLM-5.1-FP8": { + "id": "zai-org/GLM-5.1-FP8", + "name": "GLM 5.1 FP8", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-10-15", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.18, "output": 2 }, - "limit": { "context": 262000, "output": 262000 } + "release_date": "2026-04-07", + "last_updated": "2026-04-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 200000, + "output": 200000 + }, + "cost": { + "input": 1.1, + "output": 3.6 + } }, - "Qwen/Qwen3-Omni-30B-A3B-Captioner": { - "id": "Qwen/Qwen3-Omni-30B-A3B-Captioner", - "name": "Qwen/Qwen3-Omni-30B-A3B-Captioner", - "family": "qwen", + "mistralai/Devstral-Small-2-24B-Instruct-2512": { + "id": "mistralai/Devstral-Small-2-24B-Instruct-2512", + "name": "Devstral Small 2 24B Instruct 2512", + "family": "devstral", "attachment": true, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-04", - "last_updated": "2025-11-25", - "modalities": { "input": ["audio"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 66000, "output": 66000 } + "release_date": "2025-12-09", + "last_updated": "2025-12-09", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.12, + "output": 0.35 + } }, - "Qwen/Qwen3-VL-30B-A3B-Thinking": { - "id": "Qwen/Qwen3-VL-30B-A3B-Thinking", - "name": "Qwen/Qwen3-VL-30B-A3B-Thinking", - "family": "qwen", - "attachment": true, + "openai/gpt-oss-20b": { + "id": "openai/gpt-oss-20b", + "name": "GPT OSS 20B", + "family": "gpt-oss", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-11", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.29, "output": 1 }, - "limit": { "context": 262000, "output": 262000 } + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 16384, + "output": 16384 + }, + "cost": { + "input": 0.03, + "output": 0.16 + } }, - "Qwen/Qwen2.5-VL-7B-Instruct": { - "id": "Qwen/Qwen2.5-VL-7B-Instruct", - "name": "Qwen/Qwen2.5-VL-7B-Instruct", - "family": "qwen", + "moonshotai/Kimi-K2.6": { + "id": "moonshotai/Kimi-K2.6", + "name": "Kimi K2.6", + "family": "kimi", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-01-28", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.05, "output": 0.05 }, - "limit": { "context": 33000, "output": 4000 } + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.69, + "output": 3.22 + } }, - "Qwen/Qwen2.5-VL-32B-Instruct": { - "id": "Qwen/Qwen2.5-VL-32B-Instruct", - "name": "Qwen/Qwen2.5-VL-32B-Instruct", - "family": "qwen", + "moonshotai/Kimi-K2.5": { + "id": "moonshotai/Kimi-K2.5", + "name": "Kimi K2.5", + "family": "kimi", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-03-24", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.27, "output": 0.27 }, - "limit": { "context": 131000, "output": 131000 } + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.52, + "output": 2.59 + } }, - "Qwen/Qwen3-Coder-30B-A3B-Instruct": { - "id": "Qwen/Qwen3-Coder-30B-A3B-Instruct", - "name": "Qwen/Qwen3-Coder-30B-A3B-Instruct", - "family": "qwen", + "MiniMaxAI/MiniMax-M2.5": { + "id": "MiniMaxAI/MiniMax-M2.5", + "name": "MiniMax M2.5", + "family": "minimax", "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-08-01", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.07, "output": 0.28 }, - "limit": { "context": 262000, "output": 262000 } - }, - "Qwen/Qwen3-VL-8B-Instruct": { - "id": "Qwen/Qwen3-VL-8B-Instruct", - "name": "Qwen/Qwen3-VL-8B-Instruct", - "family": "qwen", - "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-10-15", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.18, "output": 0.68 }, - "limit": { "context": 262000, "output": 262000 } - }, - "Qwen/Qwen3-8B": { - "id": "Qwen/Qwen3-8B", - "name": "Qwen/Qwen3-8B", - "family": "qwen", + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 196608, + "output": 196608 + }, + "cost": { + "input": 0.35, + "output": 1.38 + } + } + } + }, + "sarvam": { + "id": "sarvam", + "env": ["SARVAM_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.sarvam.ai/v1", + "name": "Sarvam AI", + "doc": "https://docs.sarvam.ai/api-reference-docs/getting-started/models", + "models": { + "sarvam-105b": { + "id": "sarvam-105b", + "name": "Sarvam-105B", + "family": "sarvam", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-04-30", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.06, "output": 0.06 }, - "limit": { "context": 131000, "output": 131000 } + "release_date": "2026-02-18", + "last_updated": "2026-03-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + } }, - "Qwen/QwQ-32B": { - "id": "Qwen/QwQ-32B", - "name": "Qwen/QwQ-32B", - "family": "qwen", + "sarvam-30b": { + "id": "sarvam-30b", + "name": "Sarvam-30B", + "family": "sarvam", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-03-06", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.58 }, - "limit": { "context": 131000, "output": 131000 } - }, - "Qwen/Qwen3-Coder-480B-A35B-Instruct": { - "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct", - "name": "Qwen/Qwen3-Coder-480B-A35B-Instruct", - "family": "qwen", + "release_date": "2026-02-18", + "last_updated": "2026-03-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 65536, + "output": 65536 + } + } + } + }, + "togetherai": { + "id": "togetherai", + "env": ["TOGETHER_API_KEY"], + "npm": "@ai-sdk/togetherai", + "name": "Together AI", + "doc": "https://docs.together.ai/docs/serverless-models", + "models": { + "essentialai/Rnj-1-Instruct": { + "id": "essentialai/Rnj-1-Instruct", + "name": "Rnj-1 Instruct", + "family": "rnj", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2025-07-31", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 1 }, - "limit": { "context": 262000, "output": 262000 } - }, - "Qwen/Qwen3-VL-32B-Instruct": { - "id": "Qwen/Qwen3-VL-32B-Instruct", - "name": "Qwen/Qwen3-VL-32B-Instruct", - "family": "qwen", - "attachment": true, - "reasoning": false, - "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-21", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.6 }, - "limit": { "context": 262000, "output": 262000 } + "knowledge": "2024-10", + "release_date": "2025-12-05", + "last_updated": "2025-12-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 32768 + }, + "cost": { + "input": 0.15, + "output": 0.15 + } }, - "Qwen/Qwen3-30B-A3B-Instruct-2507": { - "id": "Qwen/Qwen3-30B-A3B-Instruct-2507", - "name": "Qwen/Qwen3-30B-A3B-Instruct-2507", + "Qwen/Qwen3.5-397B-A17B": { + "id": "Qwen/Qwen3.5-397B-A17B", + "name": "Qwen3.5 397B A17B", "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-30", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.09, "output": 0.3 }, - "limit": { "context": 262000, "output": 262000 } + "release_date": "2026-02-16", + "last_updated": "2026-02-16", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 130000 + }, + "cost": { + "input": 0.6, + "output": 3.6 + } }, - "Qwen/Qwen3-Omni-30B-A3B-Instruct": { - "id": "Qwen/Qwen3-Omni-30B-A3B-Instruct", - "name": "Qwen/Qwen3-Omni-30B-A3B-Instruct", + "Qwen/Qwen3.6-Plus": { + "id": "Qwen/Qwen3.6-Plus", + "name": "Qwen3.6 Plus", "family": "qwen", - "attachment": true, - "reasoning": false, + "attachment": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-04", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image", "audio"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 66000, "output": 66000 } + "release_date": "2026-04-30", + "last_updated": "2026-04-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1000000, + "output": 500000 + }, + "cost": { + "input": 0.5, + "output": 3 + } }, - "Qwen/Qwen2.5-7B-Instruct": { - "id": "Qwen/Qwen2.5-7B-Instruct", - "name": "Qwen/Qwen2.5-7B-Instruct", + "Qwen/Qwen3-Coder-Next-FP8": { + "id": "Qwen/Qwen3-Coder-Next-FP8", + "name": "Qwen3 Coder Next FP8", "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-09-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.05, "output": 0.05 }, - "limit": { "context": 33000, "output": 4000 } + "knowledge": "2026-02-03", + "release_date": "2026-02-03", + "last_updated": "2026-02-03", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.5, + "output": 1.2 + } }, - "Qwen/Qwen3-VL-235B-A22B-Instruct": { - "id": "Qwen/Qwen3-VL-235B-A22B-Instruct", - "name": "Qwen/Qwen3-VL-235B-A22B-Instruct", + "Qwen/Qwen3-235B-A22B-Instruct-2507-tput": { + "id": "Qwen/Qwen3-235B-A22B-Instruct-2507-tput", + "name": "Qwen3 235B A22B Instruct 2507 FP8", "family": "qwen", - "attachment": true, - "reasoning": false, + "attachment": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-10-04", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.3, "output": 1.5 }, - "limit": { "context": 262000, "output": 262000 } + "knowledge": "2025-07", + "release_date": "2025-07-25", + "last_updated": "2025-07-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.2, + "output": 0.6 + } }, - "Qwen/Qwen2.5-72B-Instruct": { - "id": "Qwen/Qwen2.5-72B-Instruct", - "name": "Qwen/Qwen2.5-72B-Instruct", + "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8": { + "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8", + "name": "Qwen3 Coder 480B A35B Instruct", "family": "qwen", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-09-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.59, "output": 0.59 }, - "limit": { "context": 33000, "output": 4000 } + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 2, + "output": 2 + } }, - "Qwen/Qwen3-VL-32B-Thinking": { - "id": "Qwen/Qwen3-VL-32B-Thinking", - "name": "Qwen/Qwen3-VL-32B-Thinking", - "family": "qwen", - "attachment": true, + "zai-org/GLM-5.1": { + "id": "zai-org/GLM-5.1", + "name": "GLM-5.1", + "family": "glm", + "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-10-21", - "last_updated": "2025-11-25", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 1.5 }, - "limit": { "context": 262000, "output": 262000 } + "knowledge": "2025-11", + "release_date": "2026-04-07", + "last_updated": "2026-04-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 131072 + }, + "cost": { + "input": 1.4, + "output": 4.4 + } }, - "Qwen/Qwen2.5-32B-Instruct": { - "id": "Qwen/Qwen2.5-32B-Instruct", - "name": "Qwen/Qwen2.5-32B-Instruct", - "family": "qwen", + "meta-llama/Llama-3.3-70B-Instruct-Turbo": { + "id": "meta-llama/Llama-3.3-70B-Instruct-Turbo", + "name": "Llama 3.3 70B", + "family": "llama", "attachment": false, "reasoning": false, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2024-09-19", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.18, "output": 0.18 }, - "limit": { "context": 33000, "output": 4000 } + "knowledge": "2023-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.88, + "output": 0.88 + } }, - "Qwen/Qwen3-235B-A22B-Instruct-2507": { - "id": "Qwen/Qwen3-235B-A22B-Instruct-2507", - "name": "Qwen/Qwen3-235B-A22B-Instruct-2507", - "family": "qwen", + "deepseek-ai/DeepSeek-V3": { + "id": "deepseek-ai/DeepSeek-V3", + "name": "DeepSeek V3", + "family": "deepseek", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-23", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.09, "output": 0.6 }, - "limit": { "context": 262000, "output": 262000 } + "knowledge": "2024-07", + "release_date": "2025-01-20", + "last_updated": "2025-05-29", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 1.25, + "output": 1.25 + } }, - "inclusionAI/Ling-flash-2.0": { - "id": "inclusionAI/Ling-flash-2.0", - "name": "inclusionAI/Ling-flash-2.0", - "family": "ling", + "deepseek-ai/DeepSeek-R1": { + "id": "deepseek-ai/DeepSeek-R1", + "name": "DeepSeek R1", + "family": "deepseek-thinking", "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": true, + "reasoning": true, + "tool_call": false, "temperature": true, - "release_date": "2025-09-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.14, "output": 0.57 }, - "limit": { "context": 131000, "output": 131000 } + "knowledge": "2024-07", + "release_date": "2024-12-26", + "last_updated": "2025-03-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 163839, + "output": 163839 + }, + "cost": { + "input": 3, + "output": 7 + } }, - "inclusionAI/Ring-flash-2.0": { - "id": "inclusionAI/Ring-flash-2.0", - "name": "inclusionAI/Ring-flash-2.0", - "family": "ring", + "deepseek-ai/DeepSeek-V3-1": { + "id": "deepseek-ai/DeepSeek-V3-1", + "name": "DeepSeek V3.1", + "family": "deepseek", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-09-29", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.14, "output": 0.57 }, - "limit": { "context": 131000, "output": 131000 } + "knowledge": "2025-08", + "release_date": "2025-08-21", + "last_updated": "2025-08-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 1.7 + } }, - "inclusionAI/Ling-mini-2.0": { - "id": "inclusionAI/Ling-mini-2.0", - "name": "inclusionAI/Ling-mini-2.0", - "family": "ling", + "deepseek-ai/DeepSeek-V4-Pro": { + "id": "deepseek-ai/DeepSeek-V4-Pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, "temperature": true, - "release_date": "2025-09-10", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.07, "output": 0.28 }, - "limit": { "context": 131000, "output": 131000 } + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 512000, + "output": 384000 + }, + "cost": { + "input": 2.1, + "output": 4.4, + "cache_read": 0.2 + } }, - "moonshotai/Kimi-K2-Instruct": { - "id": "moonshotai/Kimi-K2-Instruct", - "name": "moonshotai/Kimi-K2-Instruct", - "family": "kimi", + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "name": "GPT OSS 120B", + "family": "gpt-oss", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-07-13", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.58, "output": 2.29 }, - "limit": { "context": 131000, "output": 131000 } + "knowledge": "2025-08", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "moonshotai/Kimi-K2.5": { - "id": "moonshotai/Kimi-K2.5", - "name": "moonshotai/Kimi-K2.5", - "family": "kimi", - "attachment": false, + "google/gemma-4-31B-it": { + "id": "google/gemma-4-31B-it", + "name": "Gemma 4 31B Instruct", + "family": "gemma", + "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.55, "output": 3 }, - "limit": { "context": 262000, "output": 262000 } + "knowledge": "2025-01", + "release_date": "2026-04-07", + "last_updated": "2026-04-07", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0.2, + "output": 0.5 + } }, - "moonshotai/Kimi-K2-Instruct-0905": { - "id": "moonshotai/Kimi-K2-Instruct-0905", - "name": "moonshotai/Kimi-K2-Instruct-0905", - "family": "kimi", - "attachment": false, - "reasoning": false, + "moonshotai/Kimi-K2.6": { + "id": "moonshotai/Kimi-K2.6", + "name": "Kimi K2.6", + "family": "kimi-k2.6", + "attachment": true, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-09-08", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.4, "output": 2 }, - "limit": { "context": 262000, "output": 262000 } + "knowledge": "2025-01", + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 131000 + }, + "cost": { + "input": 1.2, + "output": 4.5, + "cache_read": 0.2 + } }, - "moonshotai/Kimi-K2-Thinking": { - "id": "moonshotai/Kimi-K2-Thinking", - "name": "moonshotai/Kimi-K2-Thinking", - "family": "kimi-thinking", + "moonshotai/Kimi-K2.5": { + "id": "moonshotai/Kimi-K2.5", + "name": "Kimi K2.5", + "family": "kimi", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": true, "temperature": true, - "release_date": "2025-11-07", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.55, "output": 2.5 }, - "limit": { "context": 262000, "output": 262000 } + "knowledge": "2026-01", + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.5, + "output": 2.8 + } }, - "tencent/Hunyuan-MT-7B": { - "id": "tencent/Hunyuan-MT-7B", - "name": "tencent/Hunyuan-MT-7B", - "family": "hunyuan", + "MiniMaxAI/MiniMax-M2.5": { + "id": "MiniMaxAI/MiniMax-M2.5", + "name": "MiniMax-M2.5", + "family": "minimax", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "release_date": "2025-09-18", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 33000, "output": 33000 } + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06 + } }, - "tencent/Hunyuan-A13B-Instruct": { - "id": "tencent/Hunyuan-A13B-Instruct", - "name": "tencent/Hunyuan-A13B-Instruct", - "family": "hunyuan", + "MiniMaxAI/MiniMax-M2.7": { + "id": "MiniMaxAI/MiniMax-M2.7", + "name": "MiniMax-M2.7", + "family": "minimax", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2025-06-30", - "last_updated": "2025-11-25", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.14, "output": 0.57 }, - "limit": { "context": 131000, "output": 131000 } + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 202752, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06 + } } } }, - "alibaba-coding-plan": { - "id": "alibaba-coding-plan", - "env": ["ALIBABA_CODING_PLAN_API_KEY"], + "qihang-ai": { + "id": "qihang-ai", + "env": ["QIHANG_API_KEY"], "npm": "@ai-sdk/openai-compatible", - "api": "https://coding-intl.dashscope.aliyuncs.com/v1", - "name": "Alibaba Coding Plan", - "doc": "https://www.alibabacloud.com/help/en/model-studio/coding-plan", + "api": "https://api.qhaigc.net/v1", + "name": "QiHang", + "doc": "https://www.qhaigc.net/docs", "models": { - "qwen3.5-plus": { - "id": "qwen3.5-plus", - "name": "Qwen3.5 Plus", - "family": "qwen", - "attachment": false, + "claude-opus-4-5-20251101": { + "id": "claude-opus-4-5-20251101", + "name": "Claude Opus 4.5", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-02-16", - "last_updated": "2026-02-16", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2025-03", + "release_date": "2025-11-01", + "last_updated": "2025-11-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 1000000, "output": 65536 } - }, - "qwen3-coder-next": { - "id": "qwen3-coder-next", - "name": "Qwen3 Coder Next", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, - "structured_output": true, - "temperature": true, - "release_date": "2026-02-03", - "last_updated": "2026-02-03", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 0.71, + "output": 3.57 + } }, - "kimi-k2.5": { - "id": "kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi", + "gemini-3-flash-preview": { + "id": "gemini-3-flash-preview", + "name": "Gemini 3 Flash Preview", + "family": "gemini-flash", "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, + "structured_output": true, "temperature": true, "knowledge": "2025-01", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 262144, "output": 32768 } - }, - "MiniMax-M2.5": { - "id": "MiniMax-M2.5", - "name": "MiniMax-M2.5", - "family": "minimax", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 196608, "input": 196601, "output": 24576 } + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.07, + "output": 0.43, + "tiers": [ + { + "input": 0.07, + "output": 0.43, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 0.07, + "output": 0.43 + } + } }, - "glm-5": { - "id": "glm-5", - "name": "GLM-5", - "family": "glm", - "attachment": false, + "gpt-5-mini": { + "id": "gpt-5-mini", + "name": "GPT-5-Mini", + "family": "gpt-mini", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-09-30", + "release_date": "2025-09-15", + "last_updated": "2025-09-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 202752, "output": 16384 } - }, - "qwen3-coder-plus": { - "id": "qwen3-coder-plus", - "name": "Qwen3 Coder Plus", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 1000000, "output": 65536 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 0.04, + "output": 0.29 + } }, - "glm-4.7": { - "id": "glm-4.7", - "name": "GLM-4.7", - "family": "glm", - "attachment": false, + "gemini-3-pro-preview": { + "id": "gemini-3-pro-preview", + "name": "Gemini 3 Pro Preview", + "family": "gemini-pro", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 202752, "output": 16384 } + "knowledge": "2025-11", + "release_date": "2025-11-19", + "last_updated": "2025-11-19", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 65000 + }, + "cost": { + "input": 0.57, + "output": 3.43 + } }, - "qwen3-max-2026-01-23": { - "id": "qwen3-max-2026-01-23", - "name": "Qwen3 Max", - "family": "qwen", - "attachment": false, - "reasoning": false, + "claude-sonnet-4-5-20250929": { + "id": "claude-sonnet-4-5-20250929", + "name": "Claude Sonnet 4.5", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-01-23", - "last_updated": "2026-01-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 262144, "output": 32768 } - } - } - }, - "inception": { - "id": "inception", - "env": ["INCEPTION_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.inceptionlabs.ai/v1/", - "name": "Inception", - "doc": "https://platform.inceptionlabs.ai/docs", - "models": { - "mercury-edit": { - "id": "mercury-edit", - "name": "Mercury Edit", - "attachment": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 0.43, + "output": 2.14 + } + }, + "gpt-5.2": { + "id": "gpt-5.2", + "name": "GPT-5.2", + "family": "gpt", + "attachment": true, "reasoning": true, - "tool_call": false, + "tool_call": true, + "structured_output": true, "temperature": true, - "release_date": "2026-02-24", - "last_updated": "2026-02-24", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 0.75, "cache_read": 0.025 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.25, + "output": 2 + } }, - "mercury-2": { - "id": "mercury-2", - "name": "Mercury 2", - "family": "mercury", - "attachment": false, + "gpt-5.2-codex": { + "id": "gpt-5.2-codex", + "name": "GPT-5.2 Codex", + "family": "gpt-codex", + "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": true, - "knowledge": "2025-01-01", - "release_date": "2026-02-24", - "last_updated": "2026-02-24", - "modalities": { "input": ["text"], "output": ["text"] }, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 0.75, "cache_read": 0.025 }, - "limit": { "context": 128000, "output": 50000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0.14, + "output": 1.14 + } }, - "mercury-coder": { - "id": "mercury-coder", - "name": "Mercury Coder", - "family": "mercury", - "attachment": false, - "reasoning": false, + "gemini-2.5-flash": { + "id": "gemini-2.5-flash", + "name": "Gemini 2.5 Flash", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2025-02-26", - "last_updated": "2025-07-31", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-01", + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": ["text", "image", "video", "audio", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 1, "cache_read": 0.25, "cache_write": 1 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 1048576, + "output": 65536 + }, + "cost": { + "input": 0.09, + "output": 0.71, + "tiers": [ + { + "input": 0.09, + "output": 0.71, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 0.09, + "output": 0.71 + } + } }, - "mercury": { - "id": "mercury", - "name": "Mercury", - "family": "mercury", - "attachment": false, - "reasoning": false, + "claude-haiku-4-5-20251001": { + "id": "claude-haiku-4-5-20251001", + "name": "Claude Haiku 4.5", + "family": "claude-haiku", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2023-10", - "release_date": "2025-06-26", - "last_updated": "2025-07-31", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07-31", + "release_date": "2025-10-01", + "last_updated": "2025-10-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.25, "output": 1, "cache_read": 0.25, "cache_write": 1 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 0.14, + "output": 0.71 + } } } }, - "zhipuai-coding-plan": { - "id": "zhipuai-coding-plan", - "env": ["ZHIPU_API_KEY"], + "tencent-tokenhub": { + "id": "tencent-tokenhub", + "env": ["TENCENT_TOKENHUB_API_KEY"], "npm": "@ai-sdk/openai-compatible", - "api": "https://open.bigmodel.cn/api/coding/paas/v4", - "name": "Zhipu AI Coding Plan", - "doc": "https://docs.bigmodel.cn/cn/coding-plan/overview", + "api": "https://tokenhub.tencentmaas.com/v1", + "name": "Tencent TokenHub", + "doc": "https://cloud.tencent.com/document/product/1823/130050", "models": { - "glm-5.1": { - "id": "glm-5.1", - "name": "GLM-5.1", - "family": "glm", + "hy3-preview": { + "id": "hy3-preview", + "name": "Hy3 preview", + "family": "Hy", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "release_date": "2026-03-27", - "last_updated": "2026-03-27", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 200000, "output": 131072 } - }, - "glm-4.6v-flash": { - "id": "glm-4.6v-flash", - "name": "GLM-4.6V-Flash", - "family": "glm", + "release_date": "2026-04-20", + "last_updated": "2026-04-20", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 256000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } + } + } + }, + "anthropic": { + "id": "anthropic", + "env": ["ANTHROPIC_API_KEY"], + "npm": "@ai-sdk/anthropic", + "name": "Anthropic", + "doc": "https://docs.anthropic.com/en/docs/about-claude/models", + "models": { + "claude-3-sonnet-20240229": { + "id": "claude-3-sonnet-20240229", + "name": "Claude Sonnet 3", + "family": "claude-sonnet", "attachment": true, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-08", - "last_updated": "2025-12-08", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 32768 } + "knowledge": "2023-08-31", + "release_date": "2024-03-04", + "last_updated": "2024-03-04", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 4096 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 0.3 + } }, - "glm-4.7": { - "id": "glm-4.7", - "name": "GLM-4.7", - "family": "glm", - "attachment": false, + "claude-haiku-4-5": { + "id": "claude-haiku-4-5", + "name": "Claude Haiku 4.5 (latest)", + "family": "claude-haiku", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } }, - "glm-4.5v": { - "id": "glm-4.5v", - "name": "GLM-4.5V", - "family": "glm", + "claude-opus-4-5-20251101": { + "id": "claude-opus-4-5-20251101", + "name": "Claude Opus 4.5", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-08-11", - "last_updated": "2025-08-11", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 64000, "output": 16384 } + "knowledge": "2025-03-31", + "release_date": "2025-11-01", + "last_updated": "2025-11-01", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "glm-4.5-air": { - "id": "glm-4.5-air", - "name": "GLM-4.5-Air", - "family": "glm-air", - "attachment": false, - "reasoning": true, + "claude-3-opus-20240229": { + "id": "claude-3-opus-20240229", + "name": "Claude Opus 3", + "family": "claude-opus", + "attachment": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 131072, "output": 98304 } + "knowledge": "2023-08-31", + "release_date": "2024-02-29", + "last_updated": "2024-02-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 4096 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "glm-5": { - "id": "glm-5", - "name": "GLM-5", - "family": "glm", - "attachment": false, - "reasoning": true, + "claude-3-5-haiku-20241022": { + "id": "claude-3-5-haiku-20241022", + "name": "Claude Haiku 3.5", + "family": "claude-haiku", + "attachment": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } + "knowledge": "2024-07-31", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 4, + "cache_read": 0.08, + "cache_write": 1 + } }, - "glm-4.5-flash": { - "id": "glm-4.5-flash", - "name": "GLM-4.5-Flash", - "family": "glm-flash", - "attachment": false, - "reasoning": true, + "claude-3-5-sonnet-20241022": { + "id": "claude-3-5-sonnet-20241022", + "name": "Claude Sonnet 3.5 v2", + "family": "claude-sonnet", + "attachment": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 131072, "output": 98304 } + "knowledge": "2024-04-30", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "glm-4.6v": { - "id": "glm-4.6v", - "name": "GLM-4.6V", - "family": "glm", + "claude-sonnet-4-6": { + "id": "claude-sonnet-4-6", + "name": "Claude Sonnet 4.6", + "family": "claude-sonnet", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-08", - "last_updated": "2025-12-08", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0 }, - "limit": { "context": 128000, "output": 32768 } + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "glm-4.6": { - "id": "glm-4.6", - "name": "GLM-4.6", - "family": "glm", - "attachment": false, + "claude-opus-4-0": { + "id": "claude-opus-4-0", + "name": "Claude Opus 4 (latest)", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 204800, "output": 131072 } + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "glm-4.7-flashx": { - "id": "glm-4.7-flashx", - "name": "GLM-4.7-FlashX", - "family": "glm-flash", - "attachment": false, + "claude-opus-4-7": { + "id": "claude-opus-4-7", + "name": "Claude Opus 4.7", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-01-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.07, "output": 0.4, "cache_read": 0.01, "cache_write": 0 }, - "limit": { "context": 200000, "output": 131072 } + "temperature": false, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "experimental": { + "modes": { + "fast": { + "cost": { + "input": 30, + "output": 150, + "cache_read": 3, + "cache_write": 37.5 + }, + "provider": { + "body": { + "speed": "fast" + }, + "headers": { + "anthropic-beta": "fast-mode-2026-02-01" + } + } + } + } + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "glm-4.5": { - "id": "glm-4.5", - "name": "GLM-4.5", - "family": "glm", - "attachment": false, - "reasoning": true, + "claude-3-haiku-20240307": { + "id": "claude-3-haiku-20240307", + "name": "Claude Haiku 3", + "family": "claude-haiku", + "attachment": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 131072, "output": 98304 } + "knowledge": "2023-08-31", + "release_date": "2024-03-13", + "last_updated": "2024-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 4096 + }, + "cost": { + "input": 0.25, + "output": 1.25, + "cache_read": 0.03, + "cache_write": 0.3 + } }, - "glm-5-turbo": { - "id": "glm-5-turbo", - "name": "GLM-5-Turbo", - "family": "glm", - "attachment": false, + "claude-sonnet-4-5-20250929": { + "id": "claude-sonnet-4-5-20250929", + "name": "Claude Sonnet 4.5", + "family": "claude-sonnet", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, "temperature": true, - "release_date": "2026-03-16", - "last_updated": "2026-03-16", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 200000, "output": 131072 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "glm-4.7-flash": { - "id": "glm-4.7-flash", - "name": "GLM-4.7-Flash", - "family": "glm-flash", - "attachment": false, - "reasoning": true, + "claude-3-5-haiku-latest": { + "id": "claude-3-5-haiku-latest", + "name": "Claude Haiku 3.5 (latest)", + "family": "claude-haiku", + "attachment": true, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-01-19", - "last_updated": "2026-01-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0, "cache_write": 0 }, - "limit": { "context": 200000, "output": 131072 } - } - } - }, - "moonshotai-cn": { - "id": "moonshotai-cn", - "env": ["MOONSHOT_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.moonshot.cn/v1", - "name": "Moonshot AI (China)", - "doc": "https://platform.moonshot.cn/docs/api/chat", - "models": { - "kimi-k2-0711-preview": { - "id": "kimi-k2-0711-preview", - "name": "Kimi K2 0711", - "family": "kimi", - "attachment": false, - "reasoning": false, + "knowledge": "2024-07-31", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 0.8, + "output": 4, + "cache_read": 0.08, + "cache_write": 1 + } + }, + "claude-opus-4-1": { + "id": "claude-opus-4-1", + "name": "Claude Opus 4.1 (latest)", + "family": "claude-opus", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-07-14", - "last_updated": "2025-07-14", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.5, "cache_read": 0.15 }, - "limit": { "context": 131072, "output": 16384 } + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "kimi-k2-turbo-preview": { - "id": "kimi-k2-turbo-preview", - "name": "Kimi K2 Turbo", - "family": "kimi", - "attachment": false, - "reasoning": false, + "claude-sonnet-4-0": { + "id": "claude-sonnet-4-0", + "name": "Claude Sonnet 4 (latest)", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 2.4, "output": 10, "cache_read": 0.6 }, - "limit": { "context": 262144, "output": 262144 } + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "kimi-k2-0905-preview": { - "id": "kimi-k2-0905-preview", - "name": "Kimi K2 0905", - "family": "kimi", - "attachment": false, + "claude-3-5-sonnet-20240620": { + "id": "claude-3-5-sonnet-20240620", + "name": "Claude Sonnet 3.5", + "family": "claude-sonnet", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-09-05", - "last_updated": "2025-09-05", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.5, "cache_read": 0.15 }, - "limit": { "context": 262144, "output": 262144 } + "knowledge": "2024-04-30", + "release_date": "2024-06-20", + "last_updated": "2024-06-20", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "kimi-k2-thinking-turbo": { - "id": "kimi-k2-thinking-turbo", - "name": "Kimi K2 Thinking Turbo", - "family": "kimi-thinking", - "attachment": false, + "claude-opus-4-5": { + "id": "claude-opus-4-5", + "name": "Claude Opus 4.5 (latest)", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-11-06", - "last_updated": "2025-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1.15, "output": 8, "cache_read": 0.15 }, - "limit": { "context": 262144, "output": 262144 } + "knowledge": "2025-03-31", + "release_date": "2025-11-24", + "last_updated": "2025-11-24", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "kimi-k2.5": { - "id": "kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi", - "attachment": false, + "claude-opus-4-1-20250805": { + "id": "claude-opus-4-1-20250805", + "name": "Claude Opus 4.1", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "structured_output": true, - "temperature": false, - "knowledge": "2025-01", - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 3, "cache_read": 0.1 }, - "limit": { "context": 262144, "output": 262144 } + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } }, - "kimi-k2-thinking": { - "id": "kimi-k2-thinking", - "name": "Kimi K2 Thinking", - "family": "kimi-thinking", - "attachment": false, + "claude-haiku-4-5-20251001": { + "id": "claude-haiku-4-5-20251001", + "name": "Claude Haiku 4.5", + "family": "claude-haiku", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-11-06", - "last_updated": "2025-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.5, "cache_read": 0.15 }, - "limit": { "context": 262144, "output": 262144 } - } - } - }, - "fireworks-ai": { - "id": "fireworks-ai", - "env": ["FIREWORKS_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://api.fireworks.ai/inference/v1/", - "name": "Fireworks AI", - "doc": "https://fireworks.ai/docs/", - "models": { - "accounts/fireworks/routers/kimi-k2p5-turbo": { - "id": "accounts/fireworks/routers/kimi-k2p5-turbo", - "name": "Kimi K2.5 Turbo", - "family": "kimi-thinking", - "attachment": false, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } + }, + "claude-sonnet-4-20250514": { + "id": "claude-sonnet-4-20250514", + "name": "Claude Sonnet 4", + "family": "claude-sonnet", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0, "output": 0, "cache_read": 0 }, - "limit": { "context": 256000, "output": 256000 } + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "accounts/fireworks/models/kimi-k2p5": { - "id": "accounts/fireworks/models/kimi-k2p5", - "name": "Kimi K2.5", - "family": "kimi-thinking", - "attachment": false, + "claude-opus-4-6": { + "id": "claude-opus-4-6", + "name": "Claude Opus 4.6", + "family": "claude-opus", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 3, "cache_read": 0.1 }, - "limit": { "context": 256000, "output": 256000 } + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-03-13", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "experimental": { + "modes": { + "fast": { + "cost": { + "input": 30, + "output": 150, + "cache_read": 3, + "cache_write": 37.5 + }, + "provider": { + "body": { + "speed": "fast" + }, + "headers": { + "anthropic-beta": "fast-mode-2026-02-01" + } + } + } + } + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } }, - "accounts/fireworks/models/kimi-k2-thinking": { - "id": "accounts/fireworks/models/kimi-k2-thinking", - "name": "Kimi K2 Thinking", - "family": "kimi-thinking", - "attachment": false, + "claude-3-7-sonnet-20250219": { + "id": "claude-3-7-sonnet-20250219", + "name": "Claude Sonnet 3.7", + "family": "claude-sonnet", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "release_date": "2025-11-06", - "last_updated": "2025-11-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.5, "cache_read": 0.3 }, - "limit": { "context": 256000, "output": 256000 } + "knowledge": "2024-10-31", + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "accounts/fireworks/models/deepseek-v3p1": { - "id": "accounts/fireworks/models/deepseek-v3p1", - "name": "DeepSeek V3.1", - "family": "deepseek", - "attachment": false, + "claude-sonnet-4-5": { + "id": "claude-sonnet-4-5", + "name": "Claude Sonnet 4.5 (latest)", + "family": "claude-sonnet", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-08-21", - "last_updated": "2025-08-21", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.56, "output": 1.68 }, - "limit": { "context": 163840, "output": 163840 } + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, - "accounts/fireworks/models/minimax-m2p1": { - "id": "accounts/fireworks/models/minimax-m2p1", - "name": "MiniMax-M2.1", - "family": "minimax", + "claude-opus-4-20250514": { + "id": "claude-opus-4-20250514", + "name": "Claude Opus 4", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 32000 + }, + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } + } + } + }, + "modelscope": { + "id": "modelscope", + "env": ["MODELSCOPE_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api-inference.modelscope.cn/v1", + "name": "ModelScope", + "doc": "https://modelscope.cn/docs/model-service/API-Inference/intro", + "models": { + "Qwen/Qwen3-30B-A3B-Thinking-2507": { + "id": "Qwen/Qwen3-30B-A3B-Thinking-2507", + "name": "Qwen3 30B A3B Thinking 2507", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-30", + "last_updated": "2025-07-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.03 }, - "limit": { "context": 200000, "output": 200000 } + "limit": { + "context": 262144, + "output": 32768 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "accounts/fireworks/models/minimax-m2p5": { - "id": "accounts/fireworks/models/minimax-m2p5", - "name": "MiniMax-M2.5", - "family": "minimax", + "Qwen/Qwen3-30B-A3B-Instruct-2507": { + "id": "Qwen/Qwen3-30B-A3B-Instruct-2507", + "name": "Qwen3 30B A3B Instruct 2507", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-30", + "last_updated": "2025-07-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.03 }, - "limit": { "context": 196608, "output": 196608 } + "limit": { + "context": 262144, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "accounts/fireworks/models/gpt-oss-120b": { - "id": "accounts/fireworks/models/gpt-oss-120b", - "name": "GPT OSS 120B", - "family": "gpt-oss", + "Qwen/Qwen3-235B-A22B-Instruct-2507": { + "id": "Qwen/Qwen3-235B-A22B-Instruct-2507", + "name": "Qwen3 235B A22B Instruct 2507", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-04-28", + "last_updated": "2025-07-21", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "accounts/fireworks/models/glm-4p7": { - "id": "accounts/fireworks/models/glm-4p7", - "name": "GLM 4.7", - "family": "glm", + "Qwen/Qwen3-Coder-30B-A3B-Instruct": { + "id": "Qwen/Qwen3-Coder-30B-A3B-Instruct", + "name": "Qwen3 Coder 30B A3B Instruct", + "family": "qwen", "attachment": false, - "reasoning": true, + "reasoning": false, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-07-31", + "last_updated": "2025-07-31", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.2, "cache_read": 0.3 }, - "limit": { "context": 198000, "output": 198000 } + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "accounts/fireworks/models/deepseek-v3p2": { - "id": "accounts/fireworks/models/deepseek-v3p2", - "name": "DeepSeek V3.2", - "family": "deepseek", + "Qwen/Qwen3-235B-A22B-Thinking-2507": { + "id": "Qwen/Qwen3-235B-A22B-Thinking-2507", + "name": "Qwen3-235B-A22B-Thinking-2507", + "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-25", + "last_updated": "2025-07-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.56, "output": 1.68, "cache_read": 0.28 }, - "limit": { "context": 160000, "output": 160000 } + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "accounts/fireworks/models/glm-4p5": { - "id": "accounts/fireworks/models/glm-4p5", - "name": "GLM 4.5", + "ZhipuAI/GLM-4.5": { + "id": "ZhipuAI/GLM-4.5", + "name": "GLM-4.5", "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, "knowledge": "2025-04", - "release_date": "2025-07-29", - "last_updated": "2025-07-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.55, "output": 2.19 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 131072, + "output": 98304 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "accounts/fireworks/models/glm-5": { - "id": "accounts/fireworks/models/glm-5", - "name": "GLM 5", + "ZhipuAI/GLM-4.6": { + "id": "ZhipuAI/GLM-4.6", + "name": "GLM-4.6", "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-07", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1, "output": 3.2, "cache_read": 0.5 }, - "limit": { "context": 202752, "output": 131072 } - }, - "accounts/fireworks/models/glm-4p5-air": { - "id": "accounts/fireworks/models/glm-4p5-air", - "name": "GLM 4.5 Air", - "family": "glm-air", + "limit": { + "context": 202752, + "output": 98304 + }, + "cost": { + "input": 0, + "output": 0 + } + } + } + }, + "hpc-ai": { + "id": "hpc-ai", + "env": ["HPC_AI_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.hpc-ai.com/inference/v1", + "name": "HPC-AI", + "doc": "https://www.hpc-ai.com/doc/docs/quickstart/", + "models": { + "zai-org/glm-5.1": { + "id": "zai-org/glm-5.1", + "name": "GLM 5.1", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-08-01", - "last_updated": "2025-08-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-04-08", + "last_updated": "2026-04-08", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.22, "output": 0.88 }, - "limit": { "context": 131072, "output": 131072 } + "limit": { + "context": 202000, + "output": 202000 + }, + "cost": { + "input": 0.66, + "output": 2, + "cache_read": 0.12 + } }, - "accounts/fireworks/models/gpt-oss-20b": { - "id": "accounts/fireworks/models/gpt-oss-20b", - "name": "GPT OSS 20B", - "family": "gpt-oss", + "minimax/minimax-m2.5": { + "id": "minimax/minimax-m2.5", + "name": "MiniMax M2.5", + "family": "minimax-m2.5", "attachment": false, "reasoning": true, "tool_call": true, + "structured_output": false, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-02-12", + "last_updated": "2026-03-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.05, "output": 0.2 }, - "limit": { "context": 131072, "output": 32768 } + "limit": { + "context": 1000000, + "output": 131072 + }, + "cost": { + "input": 0.14, + "output": 0.56, + "cache_read": 0.014 + } }, - "accounts/fireworks/models/kimi-k2-instruct": { - "id": "accounts/fireworks/models/kimi-k2-instruct", - "name": "Kimi K2 Instruct", + "moonshotai/kimi-k2.5": { + "id": "moonshotai/kimi-k2.5", + "name": "Kimi K2.5", "family": "kimi", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-07-11", - "last_updated": "2025-07-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": false, + "knowledge": "2025-01-01", + "release_date": "2026-01-01", + "last_updated": "2026-03-25", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1, "output": 3 }, - "limit": { "context": 128000, "output": 16384 } + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.21, + "output": 1, + "cache_read": 0.03 + } } } }, - "opencode-go": { - "id": "opencode-go", - "env": ["OPENCODE_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://opencode.ai/zen/go/v1", - "name": "OpenCode Go", - "doc": "https://opencode.ai/docs/zen", + "gitlab": { + "id": "gitlab", + "env": ["GITLAB_TOKEN"], + "npm": "gitlab-ai-provider", + "name": "GitLab Duo", + "doc": "https://docs.gitlab.com/user/duo_agent_platform/", "models": { - "kimi-k2.5": { - "id": "kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi", + "duo-chat-gpt-5-4-nano": { + "id": "duo-chat-gpt-5-4-nano", + "name": "Agentic Chat (GPT-5.4 Nano)", + "family": "gpt-nano", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "duo-chat-gpt-5-mini": { + "id": "duo-chat-gpt-5-mini", + "name": "Agentic Chat (GPT-5 Mini)", + "family": "gpt-mini", "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 3, "cache_read": 0.1 }, - "limit": { "context": 262144, "output": 65536 } + "structured_output": true, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2026-01-22", + "last_updated": "2026-01-22", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "minimax-m2.7": { - "id": "minimax-m2.7", - "name": "MiniMax M2.7", - "family": "minimax-m2.7", - "attachment": false, + "duo-chat-sonnet-4-6": { + "id": "duo-chat-sonnet-4-6", + "name": "Agentic Chat (Claude Sonnet 4.6)", + "family": "claude-sonnet", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-03-18", - "last_updated": "2026-03-18", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.06 }, - "limit": { "context": 204800, "output": 131072 }, - "provider": { "npm": "@ai-sdk/anthropic" } + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-02-17", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "glm-5": { - "id": "glm-5", - "name": "GLM-5", - "family": "glm", - "attachment": false, + "duo-chat-gpt-5-2": { + "id": "duo-chat-gpt-5-2", + "name": "Agentic Chat (GPT-5.2)", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, - "interleaved": { "field": "reasoning_content" }, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 1, "output": 3.2, "cache_read": 0.2 }, - "limit": { "context": 204800, "output": 131072 } + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-01-23", + "last_updated": "2026-01-23", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "minimax-m2.5": { - "id": "minimax-m2.5", - "name": "MiniMax M2.5", - "family": "minimax-m2.5", + "duo-chat-gpt-5-codex": { + "id": "duo-chat-gpt-5-codex", + "name": "Agentic Chat (GPT-5 Codex)", + "family": "gpt-codex", "attachment": false, "reasoning": true, "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.3, "output": 1.2, "cache_read": 0.03 }, - "limit": { "context": 204800, "output": 131072 }, - "provider": { "npm": "@ai-sdk/anthropic" } - } - } - }, - "abacus": { - "id": "abacus", - "env": ["ABACUS_API_KEY"], - "npm": "@ai-sdk/openai-compatible", - "api": "https://routellm.abacus.ai/v1", - "name": "Abacus", - "doc": "https://abacus.ai/help/api", - "models": { - "gpt-5.2-codex": { - "id": "gpt-5.2-codex", - "name": "GPT-5.2 Codex", + "structured_output": true, + "temperature": false, + "knowledge": "2024-09-30", + "release_date": "2026-01-22", + "last_updated": "2026-01-22", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "duo-chat-gpt-5-1": { + "id": "duo-chat-gpt-5-1", + "name": "Agentic Chat (GPT-5.1)", "family": "gpt", "attachment": true, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2024-09-30", + "release_date": "2026-01-22", + "last_updated": "2026-01-22", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "llama-3.3-70b-versatile": { - "id": "llama-3.3-70b-versatile", - "name": "Llama 3.3 70B Versatile", - "family": "llama", - "attachment": false, - "reasoning": false, + "duo-chat-gpt-5-2-codex": { + "id": "duo-chat-gpt-5-2-codex", + "name": "Agentic Chat (GPT-5.2 Codex)", + "family": "gpt-codex", + "attachment": true, + "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.59, "output": 0.79 }, - "limit": { "context": 128000, "output": 32768 } + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-01-22", + "last_updated": "2026-01-22", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "claude-opus-4-5-20251101": { - "id": "claude-opus-4-5-20251101", - "name": "Claude Opus 4.5", - "family": "claude-opus", + "duo-chat-sonnet-4-5": { + "id": "duo-chat-sonnet-4-5", + "name": "Agentic Chat (Claude Sonnet 4.5)", + "family": "claude-sonnet", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-11-01", - "last_updated": "2025-11-01", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-07-31", + "release_date": "2026-01-08", + "last_updated": "2026-01-08", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 5, "output": 25 }, - "limit": { "context": 200000, "output": 64000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "o3-mini": { - "id": "o3-mini", - "name": "o3-mini", - "family": "o-mini", - "attachment": false, + "duo-chat-gpt-5-4": { + "id": "duo-chat-gpt-5-4", + "name": "Agentic Chat (GPT-5.4)", + "family": "gpt", + "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": false, - "knowledge": "2024-05", - "release_date": "2024-12-20", - "last_updated": "2025-01-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.1, "output": 4.4 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "gpt-5.2-chat-latest": { - "id": "gpt-5.2-chat-latest", - "name": "GPT-5.2 Chat Latest", - "family": "gpt", + "duo-chat-haiku-4-5": { + "id": "duo-chat-haiku-4-5", + "name": "Agentic Chat (Claude Haiku 4.5)", + "family": "claude-haiku", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-09-30", - "release_date": "2026-01-01", - "last_updated": "2026-01-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-02-28", + "release_date": "2026-01-08", + "last_updated": "2026-01-08", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "gpt-5": { - "id": "gpt-5", - "name": "GPT-5", - "family": "gpt", + "duo-chat-gpt-5-3-codex": { + "id": "duo-chat-gpt-5-3-codex", + "name": "Agentic Chat (GPT-5.3 Codex)", + "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, + "structured_output": true, "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-08-31", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "claude-opus-4-20250514": { - "id": "claude-opus-4-20250514", - "name": "Claude Opus 4", + "duo-chat-gpt-5-4-mini": { + "id": "duo-chat-gpt-5-4-mini", + "name": "Agentic Chat (GPT-5.4 Mini)", + "family": "gpt-mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + }, + "cost": { + "input": 0, + "output": 0 + } + }, + "duo-chat-opus-4-7": { + "id": "duo-chat-opus-4-7", + "name": "Agentic Chat (Claude Opus 4.7)", "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": true, - "release_date": "2025-05-14", - "last_updated": "2025-05-14", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "temperature": false, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "gemini-3.1-pro-preview": { - "id": "gemini-3.1-pro-preview", - "name": "Gemini 3.1 Pro Preview", - "family": "gemini-pro", + "duo-chat-opus-4-5": { + "id": "duo-chat-opus-4-5", + "name": "Agentic Chat (Claude Opus 4.5)", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-02-19", - "last_updated": "2026-02-19", - "modalities": { "input": ["text", "image", "video", "audio", "pdf"], "output": ["text"] }, + "knowledge": "2025-03-31", + "release_date": "2026-01-08", + "last_updated": "2026-01-08", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 2, "output": 12 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 200000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "claude-3-7-sonnet-20250219": { - "id": "claude-3-7-sonnet-20250219", - "name": "Claude Sonnet 3.7", - "family": "claude-sonnet", + "duo-chat-opus-4-6": { + "id": "duo-chat-opus-4-6", + "name": "Agentic Chat (Claude Opus 4.6)", + "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-10-31", - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "knowledge": "2025-05-31", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", + "modalities": { + "input": ["text", "image", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 200000, "output": 64000 } - }, - "kimi-k2.5": { - "id": "kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi", + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } + } + } + }, + "xiaomi": { + "id": "xiaomi", + "env": ["XIAOMI_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.xiaomimimo.com/v1", + "name": "Xiaomi", + "doc": "https://platform.xiaomimimo.com/#/docs", + "models": { + "mimo-v2.5-pro": { + "id": "mimo-v2.5-pro", + "name": "MiMo-V2.5-Pro", + "family": "mimo", "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-01", - "last_updated": "2026-01", - "modalities": { "input": ["text", "image", "video"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 3 }, - "limit": { "context": 262144, "output": 32768 } + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.2, + "tiers": [ + { + "input": 2, + "output": 6, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 256000 + } + } + ], + "context_over_200k": { + "input": 2, + "output": 6, + "cache_read": 0.4 + } + } }, - "grok-4-fast-non-reasoning": { - "id": "grok-4-fast-non-reasoning", - "name": "Grok 4 Fast (Non-Reasoning)", - "family": "grok", + "mimo-v2-omni": { + "id": "mimo-v2-omni", + "name": "MiMo-V2-Omni", + "family": "mimo", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 0.5 }, - "limit": { "context": 2000000, "output": 16384 } + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0.4, + "output": 2, + "cache_read": 0.08 + } }, - "grok-code-fast-1": { - "id": "grok-code-fast-1", - "name": "Grok Code Fast 1", - "family": "grok", + "mimo-v2.5": { + "id": "mimo-v2.5", + "name": "MiMo-V2.5", + "family": "mimo", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-09-01", - "last_updated": "2025-09-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 0.4, + "output": 2, + "cache_read": 0.08, + "tiers": [ + { + "input": 0.8, + "output": 4, + "cache_read": 0.16, + "tier": { + "type": "context", + "size": 256000 + } + } + ], + "context_over_200k": { + "input": 0.8, + "output": 4, + "cache_read": 0.16 + } + } + }, + "mimo-v2-pro": { + "id": "mimo-v2-pro", + "name": "MiMo-V2-Pro", + "family": "mimo", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.2, "output": 1.5 }, - "limit": { "context": 256000, "output": 16384 } + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.2, + "tiers": [ + { + "input": 2, + "output": 6, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 256000 + } + } + ], + "context_over_200k": { + "input": 2, + "output": 6, + "cache_read": 0.4 + } + } }, - "gpt-5.3-codex": { - "id": "gpt-5.3-codex", - "name": "GPT-5.3 Codex", - "family": "gpt", + "mimo-v2-flash": { + "id": "mimo-v2-flash", + "name": "MiMo-V2-Flash", + "family": "mimo", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12-01", + "release_date": "2025-12-16", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.1, + "output": 0.3, + "cache_read": 0.01 + } + } + } + }, + "clarifai": { + "id": "clarifai", + "env": ["CLARIFAI_PAT"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.clarifai.com/v2/ext/openai/v1", + "name": "Clarifai", + "doc": "https://docs.clarifai.com/compute/inference/", + "models": { + "arcee_ai/AFM/models/trinity-mini": { + "id": "arcee_ai/AFM/models/trinity-mini", + "name": "Trinity Mini", + "family": "trinity-mini", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-10", + "release_date": "2025-12", + "last_updated": "2026-02-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 131072 + }, + "cost": { + "input": 0.045, + "output": 0.15 + } + }, + "mistralai/completion/models/Ministral-3-14B-Reasoning-2512": { + "id": "mistralai/completion/models/Ministral-3-14B-Reasoning-2512", + "name": "Ministral 3 14B Reasoning 2512", + "family": "ministral", "attachment": true, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "temperature": true, + "knowledge": "2025-12", + "release_date": "2025-12-01", + "last_updated": "2025-12-12", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 2.5, + "output": 1.7 + } }, - "gpt-5-mini": { - "id": "gpt-5-mini", - "name": "GPT-5 Mini", - "family": "gpt-mini", + "mistralai/completion/models/Ministral-3-3B-Reasoning-2512": { + "id": "mistralai/completion/models/Ministral-3-3B-Reasoning-2512", + "name": "Ministral 3 3B Reasoning 2512", + "family": "ministral", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 2 }, - "limit": { "context": 400000, "output": 128000 } + "temperature": true, + "release_date": "2025-12", + "last_updated": "2026-02-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 1.039, + "output": 0.54825 + } }, - "claude-opus-4-6": { - "id": "claude-opus-4-6", - "name": "Claude Opus 4.6", - "family": "claude-opus", + "deepseek-ai/deepseek-ocr/models/DeepSeek-OCR": { + "id": "deepseek-ai/deepseek-ocr/models/DeepSeek-OCR", + "name": "DeepSeek OCR", + "family": "deepseek", "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-10-20", + "last_updated": "2026-02-25", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 8192 + }, + "cost": { + "input": 0.2, + "output": 0.7 + } + }, + "openai/chat-completion/models/gpt-oss-20b": { + "id": "openai/chat-completion/models/gpt-oss-20b", + "name": "GPT OSS 20B", + "family": "gpt-oss", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2025-12-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.045, + "output": 0.18 + } + }, + "openai/chat-completion/models/gpt-oss-120b-high-throughput": { + "id": "openai/chat-completion/models/gpt-oss-120b-high-throughput", + "name": "GPT OSS 120B High Throughput", + "family": "gpt-oss", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-05", - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 5, "output": 25 }, - "limit": { "context": 200000, "output": 128000 } + "release_date": "2025-08-05", + "last_updated": "2026-02-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 131072, + "output": 16384 + }, + "cost": { + "input": 0.09, + "output": 0.36 + } }, - "claude-sonnet-4-5-20250929": { - "id": "claude-sonnet-4-5-20250929", - "name": "Claude Sonnet 4.5", - "family": "claude-sonnet", - "attachment": true, + "minimaxai/chat-completion/models/MiniMax-M2_5-high-throughput": { + "id": "minimaxai/chat-completion/models/MiniMax-M2_5-high-throughput", + "name": "MiniMax-M2.5 High Throughput", + "family": "minimax", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 200000, "output": 64000 } + "release_date": "2026-02-12", + "last_updated": "2026-02-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "gpt-4o-mini": { - "id": "gpt-4o-mini", - "name": "GPT-4o Mini", - "family": "gpt", - "attachment": true, + "qwen/qwenCoder/models/Qwen3-Coder-30B-A3B-Instruct": { + "id": "qwen/qwenCoder/models/Qwen3-Coder-30B-A3B-Instruct", + "name": "Qwen3 Coder 30B A3B Instruct", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 0.6 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2025-04", + "release_date": "2025-07-31", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0.11458, + "output": 0.74812 + } }, - "gpt-5.1-codex-max": { - "id": "gpt-5.1-codex-max", - "name": "GPT-5.1 Codex Max", - "family": "gpt", - "attachment": true, + "qwen/qwenLM/models/Qwen3-30B-A3B-Thinking-2507": { + "id": "qwen/qwenLM/models/Qwen3-30B-A3B-Thinking-2507", + "name": "Qwen3 30B A3B Thinking 2507", + "family": "qwen", + "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } - }, - "claude-sonnet-4-6": { - "id": "claude-sonnet-4-6", - "name": "Claude Sonnet 4.6", - "family": "claude-sonnet", - "attachment": true, - "reasoning": true, - "tool_call": true, "temperature": true, - "knowledge": "2025-08", - "release_date": "2026-02-17", - "last_updated": "2026-02-17", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 200000, "output": 64000 } + "release_date": "2025-07-31", + "last_updated": "2026-02-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0.36, + "output": 1.3 + } }, - "gpt-4.1": { - "id": "gpt-4.1", - "name": "GPT-4.1", - "family": "gpt", - "attachment": true, + "qwen/qwenLM/models/Qwen3-30B-A3B-Instruct-2507": { + "id": "qwen/qwenLM/models/Qwen3-30B-A3B-Instruct-2507", + "name": "Qwen3 30B A3B Instruct 2507", + "family": "qwen", + "attachment": false, "reasoning": false, "tool_call": true, + "structured_output": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 8 }, - "limit": { "context": 1047576, "output": 32768 } - }, - "gpt-5.1-chat-latest": { - "id": "gpt-5.1-chat-latest", - "name": "GPT-5.1 Chat Latest", - "family": "gpt", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 400000, "output": 128000 } + "release_date": "2025-07-30", + "last_updated": "2026-02-25", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.3, + "output": 0.5 + } }, - "gpt-5.3-codex-xhigh": { - "id": "gpt-5.3-codex-xhigh", - "name": "GPT-5.3 Codex XHigh", - "family": "gpt", + "clarifai/main/models/mm-poly-8b": { + "id": "clarifai/main/models/mm-poly-8b", + "name": "MM Poly 8B", + "family": "mm-poly", "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-02-05", - "last_updated": "2026-02-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-06", + "last_updated": "2026-02-25", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 32768, + "output": 4096 + }, + "cost": { + "input": 0.658, + "output": 1.11 + } }, - "gpt-5.4": { - "id": "gpt-5.4", - "name": "GPT-5.4", - "family": "gpt", + "moonshotai/chat-completion/models/Kimi-K2_6": { + "id": "moonshotai/chat-completion/models/Kimi-K2_6", + "name": "Kimi K2.6", + "family": "kimi-k2.6", "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "structured_output": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 15 }, - "limit": { "context": 1050000, "input": 922000, "output": 128000 } - }, - "o3": { - "id": "o3", - "name": "o3", - "family": "o", - "attachment": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 262144 + }, + "cost": { + "input": 0.95, + "output": 4 + } + } + } + }, + "minimax-cn": { + "id": "minimax-cn", + "env": ["MINIMAX_API_KEY"], + "npm": "@ai-sdk/anthropic", + "api": "https://api.minimaxi.com/anthropic/v1", + "name": "MiniMax (minimaxi.com)", + "doc": "https://platform.minimaxi.com/docs/guides/quickstart", + "models": { + "MiniMax-M2": { + "id": "MiniMax-M2", + "name": "MiniMax-M2", + "family": "minimax", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2, "output": 8 }, - "limit": { "context": 200000, "output": 100000 } - }, - "grok-4-1-fast-non-reasoning": { - "id": "grok-4-1-fast-non-reasoning", - "name": "Grok 4.1 Fast (Non-Reasoning)", - "family": "grok", - "attachment": true, - "reasoning": false, - "tool_call": true, "temperature": true, - "release_date": "2025-11-17", - "last_updated": "2025-11-17", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.2, "output": 0.5 }, - "limit": { "context": 2000000, "output": 16384 } + "release_date": "2025-10-27", + "last_updated": "2025-10-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 196608, + "output": 128000 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "gpt-5.3-chat-latest": { - "id": "gpt-5.3-chat-latest", - "name": "GPT-5.3 Chat Latest", - "family": "gpt", - "attachment": true, + "MiniMax-M2.5": { + "id": "MiniMax-M2.5", + "name": "MiniMax-M2.5", + "family": "minimax", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-03-01", - "last_updated": "2026-03-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.75, "output": 14 }, - "limit": { "context": 400000, "output": 128000 } + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03, + "cache_write": 0.375 + } }, - "claude-sonnet-4-20250514": { - "id": "claude-sonnet-4-20250514", - "name": "Claude Sonnet 4", - "family": "claude-sonnet", - "attachment": true, + "MiniMax-M2.7": { + "id": "MiniMax-M2.7", + "name": "MiniMax-M2.7", + "family": "minimax", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-05-14", - "last_updated": "2025-05-14", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 200000, "output": 64000 } + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06, + "cache_write": 0.375 + } }, - "grok-4-0709": { - "id": "grok-4-0709", - "name": "Grok 4", - "family": "grok", - "attachment": true, + "MiniMax-M2.7-highspeed": { + "id": "MiniMax-M2.7-highspeed", + "name": "MiniMax-M2.7-highspeed", + "family": "minimax", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 256000, "output": 16384 } + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 2.4, + "cache_read": 0.06, + "cache_write": 0.375 + } }, - "gemini-3-flash-preview": { - "id": "gemini-3-flash-preview", - "name": "Gemini 3 Flash Preview", - "family": "gemini-flash", - "attachment": true, + "MiniMax-M2.1": { + "id": "MiniMax-M2.1", + "name": "MiniMax-M2.1", + "family": "minimax", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-12-17", - "last_updated": "2025-12-17", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.5, "output": 3 }, - "limit": { "context": 1048576, "output": 65536 } + "release_date": "2025-12-23", + "last_updated": "2025-12-23", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "gemini-2.5-pro": { - "id": "gemini-2.5-pro", - "name": "Gemini 2.5 Pro", - "family": "gemini-pro", - "attachment": true, + "MiniMax-M2.5-highspeed": { + "id": "MiniMax-M2.5-highspeed", + "name": "MiniMax-M2.5-highspeed", + "family": "minimax", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-03-25", - "last_updated": "2025-03-25", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 1048576, "output": 65536 } - }, - "claude-opus-4-1-20250805": { - "id": "claude-opus-4-1-20250805", - "name": "Claude Opus 4.1", - "family": "claude-opus", - "attachment": true, + "release_date": "2026-02-13", + "last_updated": "2026-02-13", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 2.4, + "cache_read": 0.06, + "cache_write": 0.375 + } + } + } + }, + "regolo-ai": { + "id": "regolo-ai", + "env": ["REGOLO_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.regolo.ai/v1", + "name": "Regolo AI", + "doc": "https://docs.regolo.ai/", + "models": { + "mistral-small3.2": { + "id": "mistral-small3.2", + "name": "Mistral Small 3.2", + "family": "mistral-small", + "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, + "release_date": "2025-01-31", + "last_updated": "2025-01-31", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 15, "output": 75 }, - "limit": { "context": 200000, "output": 32000 } + "limit": { + "context": 120000, + "output": 120000 + }, + "cost": { + "input": 0.5, + "output": 2.2 + } }, - "kimi-k2-turbo-preview": { - "id": "kimi-k2-turbo-preview", - "name": "Kimi K2 Turbo Preview", - "family": "kimi", + "qwen3-embedding-8b": { + "id": "qwen3-embedding-8b", + "name": "Qwen3-Embedding-8B", + "family": "qwen", "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2025-07-08", - "last_updated": "2025-07-08", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.15, "output": 8 }, - "limit": { "context": 256000, "output": 8192 } + "tool_call": false, + "temperature": false, + "release_date": "2026-02-01", + "last_updated": "2026-02-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 8192 + }, + "cost": { + "input": 0.1, + "output": 0.1 + } }, - "gemini-2.5-flash": { - "id": "gemini-2.5-flash", - "name": "Gemini 2.5 Flash", - "family": "gemini-flash", - "attachment": true, - "reasoning": true, + "llama-3.3-70b-instruct": { + "id": "llama-3.3-70b-instruct", + "name": "Llama 3.3 70B Instruct", + "family": "llama", + "attachment": false, + "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-03-20", - "last_updated": "2025-06-05", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, + "release_date": "2025-04-28", + "last_updated": "2025-04-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.3, "output": 2.5 }, - "limit": { "context": 1048576, "output": 65536 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.6, + "output": 2.7 + } }, - "gpt-4o-2024-11-20": { - "id": "gpt-4o-2024-11-20", - "name": "GPT-4o (2024-11-20)", - "family": "gpt", - "attachment": true, + "qwen3-reranker-4b": { + "id": "qwen3-reranker-4b", + "name": "Qwen3-Reranker-4B", + "family": "qwen", + "attachment": false, "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-11-20", - "last_updated": "2024-11-20", - "modalities": { "input": ["text", "image", "audio"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 2.5, "output": 10 }, - "limit": { "context": 128000, "output": 16384 } + "tool_call": false, + "temperature": false, + "release_date": "2026-02-01", + "last_updated": "2026-02-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 32768, + "output": 8192 + }, + "cost": { + "input": 0.12, + "output": 0.12 + } }, - "gpt-5.2": { - "id": "gpt-5.2", - "name": "GPT-5.2", - "family": "gpt", + "mistral-small-4-119b": { + "id": "mistral-small-4-119b", + "name": "Mistral Small 4 119B", + "family": "mistral-small", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2025-12-11", - "last_updated": "2025-12-11", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "release_date": "2026-03-15", + "last_updated": "2026-03-15", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.75, "output": 14 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 256000, + "output": 16384 + }, + "cost": { + "input": 0.75, + "output": 3 + } }, - "gpt-5.1": { - "id": "gpt-5.1", - "name": "GPT-5.1", - "family": "gpt", + "qwen3.5-122b": { + "id": "qwen3.5-122b", + "name": "Qwen3.5-122B", + "family": "qwen", "attachment": true, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 400000, "output": 128000 } + "temperature": true, + "release_date": "2026-02-01", + "last_updated": "2026-02-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 16384 + }, + "cost": { + "input": 0.9, + "output": 3.6 + } }, - "gpt-4.1-mini": { - "id": "gpt-4.1-mini", - "name": "GPT-4.1 Mini", - "family": "gpt", - "attachment": true, + "qwen-image": { + "id": "qwen-image", + "name": "Qwen-Image", + "family": "qwen", + "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2026-03-01", + "last_updated": "2026-03-01", + "modalities": { + "input": ["text"], + "output": ["image"] + }, "open_weights": false, - "cost": { "input": 0.4, "output": 1.6 }, - "limit": { "context": 1047576, "output": 32768 } + "limit": { + "context": 8192, + "output": 4096 + }, + "cost": { + "input": 0.5, + "output": 2 + } }, - "qwen-2.5-coder-32b": { - "id": "qwen-2.5-coder-32b", - "name": "Qwen 2.5 Coder 32B", + "qwen3-coder-next": { + "id": "qwen3-coder-next", + "name": "Qwen3-Coder-Next", "family": "qwen", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-11-11", - "last_updated": "2024-11-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "release_date": "2026-03-01", + "last_updated": "2026-03-01", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.79, "output": 0.79 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 262144, + "output": 16384 + }, + "cost": { + "input": 0.3, + "output": 1.2 + } }, - "gpt-5-nano": { - "id": "gpt-5-nano", - "name": "GPT-5 Nano", - "family": "gpt-nano", - "attachment": true, + "minimax-m2.5": { + "id": "minimax-m2.5", + "name": "MiniMax 2.5", + "family": "minimax", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "temperature": true, + "release_date": "2026-03-10", + "last_updated": "2026-03-10", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.05, "output": 0.4 }, - "limit": { "context": 400000, "output": 128000 } + "limit": { + "context": 190000, + "output": 64000 + }, + "cost": { + "input": 0.8, + "output": 3.5 + } }, - "gemini-3.1-flash-lite-preview": { - "id": "gemini-3.1-flash-lite-preview", - "name": "Gemini 3.1 Flash Lite Preview", - "family": "gemini-flash", - "attachment": true, + "gpt-oss-20b": { + "id": "gpt-oss-20b", + "name": "GPT-OSS-20B", + "family": "gpt-oss", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, "temperature": true, "release_date": "2026-03-01", "last_updated": "2026-03-01", - "modalities": { "input": ["text", "image", "audio", "video", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 0.25, "output": 1.5, "cache_read": 0.025, "cache_write": 1 }, - "limit": { "context": 1048576, "output": 65536 } + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 0.4, + "output": 1.8 + } }, - "claude-haiku-4-5-20251001": { - "id": "claude-haiku-4-5-20251001", - "name": "Claude Haiku 4.5", - "family": "claude-haiku", + "qwen3.5-9b": { + "id": "qwen3.5-9b", + "name": "Qwen3.5-9B", + "family": "qwen", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-02-28", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1, "output": 5 }, - "limit": { "context": 200000, "output": 64000 } + "release_date": "2026-02-01", + "last_updated": "2026-02-01", + "modalities": { + "input": ["text", "image"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 8192 + }, + "cost": { + "input": 0.15, + "output": 0.6 + } }, - "qwen3-max": { - "id": "qwen3-max", - "name": "Qwen3 Max", - "family": "qwen", + "gpt-oss-120b": { + "id": "gpt-oss-120b", + "name": "GPT-OSS-120B", + "family": "gpt-oss", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-05-28", - "last_updated": "2025-05-28", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.2, "output": 6 }, - "limit": { "context": 131072, "output": 16384 } - }, - "o3-pro": { - "id": "o3-pro", - "name": "o3-pro", - "family": "o-pro", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2025-06-10", - "last_updated": "2025-06-10", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 20, "output": 40 }, - "limit": { "context": 200000, "output": 100000 } + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 1, + "output": 4.2 + } }, - "gpt-4.1-nano": { - "id": "gpt-4.1-nano", - "name": "GPT-4.1 Nano", - "family": "gpt", - "attachment": true, + "llama-3.1-8b-instruct": { + "id": "llama-3.1-8b-instruct", + "name": "Llama 3.1 8B Instruct", + "family": "llama", + "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "release_date": "2025-04-07", + "last_updated": "2025-04-07", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 0.1, "output": 0.4 }, - "limit": { "context": 1047576, "output": 32768 } - }, - "gpt-5-codex": { - "id": "gpt-5-codex", - "name": "GPT-5 Codex", - "family": "gpt", + "limit": { + "context": 120000, + "output": 120000 + }, + "cost": { + "input": 0.05, + "output": 0.25 + } + } + } + }, + "xiaomi-token-plan-ams": { + "id": "xiaomi-token-plan-ams", + "env": ["XIAOMI_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://token-plan-ams.xiaomimimo.com/v1", + "name": "Xiaomi Token Plan (Europe)", + "doc": "https://platform.xiaomimimo.com/#/docs", + "models": { + "mimo-v2-tts": { + "id": "mimo-v2-tts", + "name": "MiMo-V2-TTS", + "family": "mimo", "attachment": false, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-09-15", - "last_updated": "2025-09-15", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "reasoning": false, + "tool_call": false, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["audio"] + }, + "open_weights": true, + "limit": { + "context": 8192, + "output": 16384 + }, + "cost": { + "input": 0, + "output": 0 + } }, - "o4-mini": { - "id": "o4-mini", - "name": "o4-mini", - "family": "o-mini", - "attachment": true, + "mimo-v2-flash": { + "id": "mimo-v2-flash", + "name": "MiMo-V2-Flash", + "family": "mimo", + "attachment": false, "reasoning": true, "tool_call": true, - "temperature": false, - "knowledge": "2024-05", - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 1.1, "output": 4.4 }, - "limit": { "context": 200000, "output": 100000 } - }, - "route-llm": { - "id": "route-llm", - "name": "Route LLM", - "family": "gpt", - "attachment": true, - "reasoning": false, - "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "knowledge": "2024-10", - "release_date": "2024-01-01", - "last_updated": "2024-01-01", - "modalities": { "input": ["text", "image"], "output": ["text"] }, - "open_weights": false, - "cost": { "input": 3, "output": 15 }, - "limit": { "context": 128000, "output": 16384 } + "knowledge": "2024-12-01", + "release_date": "2025-12-16", + "last_updated": "2026-02-04", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": true, + "limit": { + "context": 262144, + "output": 65536 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } }, - "gpt-5.1-codex": { - "id": "gpt-5.1-codex", - "name": "GPT-5.1 Codex", - "family": "gpt", - "attachment": true, + "mimo-v2-pro": { + "id": "mimo-v2-pro", + "name": "MiMo-V2-Pro", + "family": "mimo", + "attachment": false, "reasoning": true, "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-09-30", - "release_date": "2025-11-13", - "last_updated": "2025-11-13", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": false, - "cost": { "input": 1.25, "output": 10 }, - "limit": { "context": 400000, "input": 272000, "output": 128000 } + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } }, - "openai/gpt-oss-120b": { - "id": "openai/gpt-oss-120b", - "name": "GPT-OSS 120B", - "family": "gpt-oss", + "mimo-v2.5": { + "id": "mimo-v2.5", + "name": "MiMo-V2.5", + "family": "mimo", "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text", "image", "audio", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.08, "output": 0.44 }, - "limit": { "context": 128000, "output": 32768 } + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } }, - "deepseek/deepseek-v3.1": { - "id": "deepseek/deepseek-v3.1", - "name": "DeepSeek V3.1", - "family": "deepseek", - "attachment": false, + "mimo-v2-omni": { + "id": "mimo-v2-omni", + "name": "MiMo-V2-Omni", + "family": "mimo", + "attachment": true, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.55, "output": 1.66 }, - "limit": { "context": 128000, "output": 8192 } + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": ["text", "image", "audio", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 262144, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } }, - "zai-org/glm-4.5": { - "id": "zai-org/glm-4.5", - "name": "GLM-4.5", - "family": "glm", + "mimo-v2.5-pro": { + "id": "mimo-v2.5-pro", + "name": "MiMo-V2.5-Pro", + "family": "mimo", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.6, "output": 2.2 }, - "limit": { "context": 128000, "output": 8192 } - }, - "zai-org/glm-4.6": { - "id": "zai-org/glm-4.6", - "name": "GLM-4.6", + "limit": { + "context": 1048576, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + } + } + }, + "zhipuai": { + "id": "zhipuai", + "env": ["ZHIPU_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://open.bigmodel.cn/api/paas/v4", + "name": "Zhipu AI", + "doc": "https://docs.z.ai/guides/overview/pricing", + "models": { + "glm-5v-turbo": { + "id": "glm-5v-turbo", + "name": "GLM-5V-Turbo", "family": "glm", - "attachment": false, - "reasoning": false, + "attachment": true, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2025-03-01", - "last_updated": "2025-03-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.2 }, - "limit": { "context": 128000, "output": 8192 } + "release_date": "2026-04-01", + "last_updated": "2026-04-01", + "modalities": { + "input": ["text", "image", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 5, + "output": 22, + "cache_read": 1.2, + "cache_write": 0 + } }, - "zai-org/glm-5": { - "id": "zai-org/glm-5", + "glm-5": { + "id": "glm-5", "name": "GLM-5", "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, "release_date": "2026-02-11", "last_updated": "2026-02-11", - "modalities": { "input": ["text"], "output": ["text"] }, + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 1, "output": 3.2 }, - "limit": { "context": 204800, "output": 131072 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 1, + "output": 3.2, + "cache_read": 0.2, + "cache_write": 0 + } }, - "zai-org/glm-4.7": { - "id": "zai-org/glm-4.7", - "name": "GLM-4.7", + "glm-5.1": { + "id": "glm-5.1", + "name": "GLM-5.1", "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, "temperature": true, - "release_date": "2025-06-01", - "last_updated": "2025-06-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.6, "output": 2.2 }, - "limit": { "context": 128000, "output": 8192 } + "release_date": "2026-03-27", + "last_updated": "2026-03-27", + "modalities": { + "input": ["text"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 6, + "output": 24, + "cache_read": 1.3, + "cache_write": 0 + } }, - "meta-llama/Meta-Llama-3.1-8B-Instruct": { - "id": "meta-llama/Meta-Llama-3.1-8B-Instruct", - "name": "Llama 3.1 8B Instruct", - "family": "llama", + "glm-4.7-flash": { + "id": "glm-4.7-flash", + "name": "GLM-4.7-Flash", + "family": "glm-flash", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2026-01-19", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.02, "output": 0.05 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo": { - "id": "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo", - "name": "Llama 3.1 405B Instruct Turbo", - "family": "llama", + "glm-4.5-flash": { + "id": "glm-4.5-flash", + "name": "GLM-4.5-Flash", + "family": "glm-flash", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 3.5, "output": 3.5 }, - "limit": { "context": 128000, "output": 4096 } + "limit": { + "context": 131072, + "output": 98304 + }, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } }, - "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8": { - "id": "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8", - "name": "Llama 4 Maverick 17B 128E Instruct FP8", - "family": "llama", + "glm-4.6v": { + "id": "glm-4.6v", + "name": "GLM-4.6V", + "family": "glm", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-08", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "modalities": { "input": ["text", "image"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-12-08", + "last_updated": "2025-12-08", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.14, "output": 0.59 }, - "limit": { "context": 1000000, "output": 32768 } + "limit": { + "context": 128000, + "output": 32768 + }, + "cost": { + "input": 0.3, + "output": 0.9 + } }, - "deepseek-ai/DeepSeek-R1": { - "id": "deepseek-ai/DeepSeek-R1", - "name": "DeepSeek R1", - "family": "deepseek-thinking", + "glm-4.6": { + "id": "glm-4.6", + "name": "GLM-4.6", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 3, "output": 7 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.11, + "cache_write": 0 + } }, - "deepseek-ai/DeepSeek-V3.1-Terminus": { - "id": "deepseek-ai/DeepSeek-V3.1-Terminus", - "name": "DeepSeek V3.1 Terminus", - "family": "deepseek", - "attachment": false, + "glm-4.5v": { + "id": "glm-4.5v", + "name": "GLM-4.5V", + "family": "glm", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-06-01", - "last_updated": "2025-06-01", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-08-11", + "last_updated": "2025-08-11", + "modalities": { + "input": ["text", "image", "video"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.27, "output": 1 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 64000, + "output": 16384 + }, + "cost": { + "input": 0.6, + "output": 1.8 + } }, - "deepseek-ai/DeepSeek-V3.2": { - "id": "deepseek-ai/DeepSeek-V3.2", - "name": "DeepSeek V3.2", - "family": "deepseek", + "glm-4.5-air": { + "id": "glm-4.5-air", + "name": "GLM-4.5-Air", + "family": "glm-air", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-06-15", - "last_updated": "2025-06-15", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.27, "output": 0.4 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 131072, + "output": 98304 + }, + "cost": { + "input": 0.2, + "output": 1.1, + "cache_read": 0.03, + "cache_write": 0 + } }, - "Qwen/Qwen3-32B": { - "id": "Qwen/Qwen3-32B", - "name": "Qwen3 32B", - "family": "qwen", + "glm-4.5": { + "id": "glm-4.5", + "name": "GLM-4.5", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-04-29", - "last_updated": "2025-04-29", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.09, "output": 0.29 }, - "limit": { "context": 128000, "output": 8192 } + "limit": { + "context": 131072, + "output": 98304 + }, + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.11, + "cache_write": 0 + } }, - "Qwen/qwen3-coder-480b-a35b-instruct": { - "id": "Qwen/qwen3-coder-480b-a35b-instruct", - "name": "Qwen3 Coder 480B A35B Instruct", - "family": "qwen", + "glm-4.7-flashx": { + "id": "glm-4.7-flashx", + "name": "GLM-4.7-FlashX", + "family": "glm-flash", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-07-22", - "last_updated": "2025-07-22", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2026-01-19", + "last_updated": "2026-01-19", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.29, "output": 1.2 }, - "limit": { "context": 262144, "output": 65536 } + "limit": { + "context": 200000, + "output": 131072 + }, + "cost": { + "input": 0.07, + "output": 0.4, + "cache_read": 0.01, + "cache_write": 0 + } }, - "Qwen/QwQ-32B": { - "id": "Qwen/QwQ-32B", - "name": "QwQ 32B", - "family": "qwen", + "glm-4.7": { + "id": "glm-4.7", + "name": "GLM-4.7", + "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, "temperature": true, - "release_date": "2024-11-28", - "last_updated": "2024-11-28", - "modalities": { "input": ["text"], "output": ["text"] }, + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": ["text"], + "output": ["text"] + }, "open_weights": true, - "cost": { "input": 0.4, "output": 0.4 }, - "limit": { "context": 32768, "output": 32768 } - }, - "Qwen/Qwen2.5-72B-Instruct": { - "id": "Qwen/Qwen2.5-72B-Instruct", - "name": "Qwen 2.5 72B Instruct", - "family": "qwen", - "attachment": false, - "reasoning": false, + "limit": { + "context": 204800, + "output": 131072 + }, + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.11, + "cache_write": 0 + } + } + } + }, + "nova": { + "id": "nova", + "env": ["NOVA_API_KEY"], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.nova.amazon.com/v1", + "name": "Nova", + "doc": "https://nova.amazon.com/dev/documentation", + "models": { + "nova-2-lite-v1": { + "id": "nova-2-lite-v1", + "name": "Nova 2 Lite", + "family": "nova-lite", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-09-19", - "last_updated": "2024-09-19", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.11, "output": 0.38 }, - "limit": { "context": 128000, "output": 8192 } + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": ["text", "image", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0, + "reasoning": 0 + } }, - "Qwen/Qwen3-235B-A22B-Instruct-2507": { - "id": "Qwen/Qwen3-235B-A22B-Instruct-2507", - "name": "Qwen3 235B A22B Instruct", - "family": "qwen", - "attachment": false, + "nova-2-pro-v1": { + "id": "nova-2-pro-v1", + "name": "Nova 2 Pro", + "family": "nova-pro", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-07-01", - "last_updated": "2025-07-01", - "modalities": { "input": ["text"], "output": ["text"] }, - "open_weights": true, - "cost": { "input": 0.13, "output": 0.6 }, - "limit": { "context": 262144, "output": 8192 } + "release_date": "2025-12-03", + "last_updated": "2026-01-03", + "modalities": { + "input": ["text", "image", "video", "pdf"], + "output": ["text"] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 64000 + }, + "cost": { + "input": 0, + "output": 0, + "reasoning": 0 + } } } } diff --git a/packages/opencode/test/tool/grep.test.ts b/packages/opencode/test/tool/grep.test.ts index 747639abba1..a0a1a5e6876 100644 --- a/packages/opencode/test/tool/grep.test.ts +++ b/packages/opencode/test/tool/grep.test.ts @@ -1,4 +1,6 @@ import { describe, expect } from "bun:test" +import fs from "fs/promises" +import os from "os" import path from "path" import { Effect, Layer } from "effect" import { GrepTool } from "../../src/tool/grep" @@ -11,6 +13,8 @@ import { Ripgrep } from "../../src/file/ripgrep" import { AppFileSystem } from "@opencode-ai/core/filesystem" import { testEffect } from "../lib/effect" import { Reference } from "@/reference/reference" +import { Permission } from "../../src/permission" +import type * as Tool from "../../src/tool/tool" const it = testEffect( Layer.mergeAll( @@ -110,4 +114,53 @@ describe("tool.grep", () => { expect(result.output).toContain("Line 2: line2") }), ) + + it.instance("does not ask for external_directory when alias path is allowed", () => + Effect.gen(function* () { + if (process.platform === "win32") return + + yield* TestInstance + const tmp = yield* Effect.acquireRelease( + Effect.promise(() => fs.mkdtemp(path.join(os.tmpdir(), "opencode-grep-alias-"))), + (dir) => Effect.promise(() => fs.rm(dir, { recursive: true, force: true })), + ) + const real = path.join(tmp, "real") + const alias = path.join(tmp, "alias") + yield* Effect.promise(() => fs.mkdir(real)) + yield* Effect.promise(() => fs.symlink(real, alias, "dir")) + yield* Effect.promise(() => Bun.write(path.join(real, "test.txt"), "needle")) + + const ruleset = Permission.fromConfig({ + grep: "allow", + external_directory: { + [path.join(alias, "*")]: "allow", + }, + }) + const requests: Array> = [] + const next: Tool.Context = { + ...ctx, + ask: (req) => + Effect.sync(() => { + const needsAsk = req.patterns.some( + (pattern) => Permission.evaluate(req.permission, pattern, ruleset).action !== "allow", + ) + if (needsAsk) requests.push(req) + }), + } + + const info = yield* GrepTool + const grep = yield* info.init() + const result = yield* grep.execute( + { + pattern: "needle", + path: alias, + include: "*.txt", + }, + next, + ) + + expect(result.metadata.matches).toBe(1) + expect(requests.find((req) => req.permission === "external_directory")).toBeUndefined() + }), + ) }) diff --git a/packages/opencode/test/tool/parameters.test.ts b/packages/opencode/test/tool/parameters.test.ts index 9d0f701a127..b385ed8b767 100644 --- a/packages/opencode/test/tool/parameters.test.ts +++ b/packages/opencode/test/tool/parameters.test.ts @@ -1,13 +1,13 @@ import { describe, expect, test } from "bun:test" import { Result, Schema } from "effect" -import { toJsonSchema } from "@opencode-ai/core/effect-zod" +import { ToolJsonSchema } from "../../src/tool/json-schema" // Each tool exports its parameters schema at module scope so this test can // import them without running the tool's Effect-based init. The JSON Schema // snapshot captures what the LLM sees; the parse assertions pin down the -// accepts/rejects contract. `toJsonSchema` is the same helper `session/ +// accepts/rejects contract. `ToolJsonSchema.fromSchema` is the same helper `session/ // prompt.ts` uses to emit tool schemas to the LLM, so the snapshots stay -// byte-identical regardless of whether a tool has migrated from zod to Schema. +// provider-compatible while tools use Effect Schema internally. import { Parameters as ApplyPatch } from "../../src/tool/apply_patch" import { Parameters as Edit } from "../../src/tool/edit" @@ -32,6 +32,8 @@ const parse = >(schema: S, input: unknown): S[ const accepts = (schema: Schema.Decoder, input: unknown): boolean => Result.isSuccess(Schema.decodeUnknownResult(schema)(input)) +const toJsonSchema = ToolJsonSchema.fromSchema + describe("tool parameters", () => { describe("JSON Schema (wire shape)", () => { test("apply_patch", () => expect(toJsonSchema(ApplyPatch)).toMatchSnapshot()) @@ -50,6 +52,36 @@ describe("tool parameters", () => { test("webfetch", () => expect(toJsonSchema(WebFetch)).toMatchSnapshot()) test("websearch", () => expect(toJsonSchema(WebSearch)).toMatchSnapshot()) test("write", () => expect(toJsonSchema(Write)).toMatchSnapshot()) + + test("inlines named child schemas for provider compatibility", () => { + const schema = toJsonSchema(Question) + expect(schema).not.toHaveProperty("$defs") + expect(schema).toMatchObject({ + properties: { + questions: { items: { properties: { options: { items: { properties: { label: { type: "string" } } } } } } }, + }, + }) + }) + + test("preserves required nullable fields", () => { + expect(toJsonSchema(Schema.Struct({ value: Schema.NullOr(Schema.String) }))).toMatchObject({ + properties: { value: { anyOf: expect.arrayContaining([{ type: "null" }]) } }, + }) + }) + + test("keeps repeated allOf constraints instead of dropping duplicates", () => { + expect( + toJsonSchema( + Schema.Struct({ value: Schema.String.check(Schema.isPattern(/^a/)).check(Schema.isPattern(/z$/)) }), + ), + ).toMatchObject({ properties: { value: { allOf: [{ pattern: "^a" }, { pattern: "z$" }] } } }) + }) + + test("bounds bare integer fields to safe integer range", () => { + expect(toJsonSchema(Schema.Struct({ value: Schema.Int }))).toMatchObject({ + properties: { value: { minimum: Number.MIN_SAFE_INTEGER, maximum: Number.MAX_SAFE_INTEGER } }, + }) + }) }) describe("apply_patch", () => { @@ -205,6 +237,10 @@ describe("tool parameters", () => { const parsed = parse(Task, { description: "d", prompt: "p", subagent_type: "general" }) expect(parsed.subagent_type).toBe("general") }) + test("accepts optional background flag", () => { + const parsed = parse(Task, { description: "d", prompt: "p", subagent_type: "general", background: true }) + expect(parsed.background).toBe(true) + }) test("rejects missing prompt", () => { expect(accepts(Task, { description: "d", subagent_type: "general" })).toBe(false) }) diff --git a/packages/opencode/test/tool/question.test.ts b/packages/opencode/test/tool/question.test.ts index da215db7705..854c1f89114 100644 --- a/packages/opencode/test/tool/question.test.ts +++ b/packages/opencode/test/tool/question.test.ts @@ -1,5 +1,5 @@ import { describe, expect } from "bun:test" -import { Effect, Fiber, Layer } from "effect" +import { Effect, Fiber, Layer, Queue } from "effect" import { QuestionTool } from "../../src/tool/question" import { Question } from "../../src/question" import { SessionID, MessageID } from "../../src/session/schema" @@ -7,6 +7,7 @@ import { Agent } from "../../src/agent/agent" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { Truncate } from "@/tool/truncate" import { testEffect } from "../lib/effect" +import { Bus } from "../../src/bus" const ctx = { sessionID: SessionID.make("ses_test-session"), @@ -20,15 +21,25 @@ const ctx = { } const it = testEffect( - Layer.mergeAll(Question.defaultLayer, CrossSpawnSpawner.defaultLayer, Truncate.defaultLayer, Agent.defaultLayer), + Layer.mergeAll( + Question.layer.pipe(Layer.provideMerge(Bus.layer)), + CrossSpawnSpawner.defaultLayer, + Truncate.defaultLayer, + Agent.defaultLayer, + ), ) const pending = Effect.fn("QuestionToolTest.pending")(function* (question: Question.Interface) { + const bus = yield* Bus.Service + const asked = yield* Queue.unbounded() + const off = yield* bus.subscribeCallback(Question.Event.Asked, () => Queue.offerUnsafe(asked, undefined)) + yield* Effect.addFinalizer(() => Effect.sync(off)) + for (;;) { const items = yield* question.list() const item = items[0] if (item) return item - yield* Effect.sleep("10 millis") + yield* Queue.take(asked).pipe(Effect.timeout("2 seconds")) } }) diff --git a/packages/opencode/test/tool/read.test.ts b/packages/opencode/test/tool/read.test.ts index dfd2a1df0a2..1d52601874f 100644 --- a/packages/opencode/test/tool/read.test.ts +++ b/packages/opencode/test/tool/read.test.ts @@ -4,8 +4,10 @@ import path from "path" import { Agent } from "../../src/agent/agent" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { AppFileSystem } from "@opencode-ai/core/filesystem" -import { Flag } from "@opencode-ai/core/flag/flag" import { Global } from "@opencode-ai/core/global" +import { Config } from "@/config/config" +import { RuntimeFlags } from "@/effect/runtime-flags" +import { Git } from "@/git" import { LSP } from "@/lsp/lsp" import { Permission } from "../../src/permission" import { Instance } from "../../src/project/instance" @@ -36,17 +38,27 @@ const ctx = { ask: () => Effect.void, } -const it = testEffect( +const referenceLayer = (flags: Partial = {}) => + Reference.layer.pipe( + Layer.provide(Config.defaultLayer), + Layer.provide(AppFileSystem.defaultLayer), + Layer.provide(Git.defaultLayer), + Layer.provide(RuntimeFlags.layer(flags)), + ) + +const readLayer = (flags: Partial = {}) => Layer.mergeAll( Agent.defaultLayer, AppFileSystem.defaultLayer, CrossSpawnSpawner.defaultLayer, Instruction.defaultLayer, LSP.defaultLayer, - Reference.defaultLayer, + referenceLayer(flags), Truncate.defaultLayer, - ), -) + ) + +const it = testEffect(readLayer()) +const scout = testEffect(readLayer({ experimentalScout: true })) const init = Effect.fn("ReadToolTest.init")(function* () { const info = yield* ReadTool @@ -85,19 +97,6 @@ const fail = Effect.fn("ReadToolTest.fail")(function* ( const full = (p: string) => (process.platform === "win32" ? Filesystem.normalizePath(p) : p) const glob = (p: string) => process.platform === "win32" ? Filesystem.normalizePathPattern(p) : p.replaceAll("\\", "/") -const experimentalScout = (self: Effect.Effect) => - Effect.acquireUseRelease( - Effect.sync(() => { - const previous = Flag.KILO_EXPERIMENTAL_SCOUT - Flag.KILO_EXPERIMENTAL_SCOUT = true - return previous - }), - () => self, - (previous) => - Effect.sync(() => { - Flag.KILO_EXPERIMENTAL_SCOUT = previous - }), - ) const githubBase = (url: string, self: Effect.Effect) => Effect.acquireUseRelease( Effect.sync(() => { @@ -260,44 +259,42 @@ describe("tool.read external_directory permission", () => { }), ) - it.live("does not ask for external_directory permission when reading configured references", () => - experimentalScout( - Effect.gen(function* () { - const fs = yield* AppFileSystem.Service - const cache = path.join(Global.Path.repos, "github.com", "opencode-read-reference", "repo") - yield* fs.remove(cache, { recursive: true }).pipe(Effect.ignore) - yield* Effect.addFinalizer(() => fs.remove(cache, { recursive: true }).pipe(Effect.ignore)) - - const source = yield* tmpdirScoped({ git: true }) - const remoteRoot = yield* tmpdirScoped() - const remoteDir = path.join(remoteRoot, "opencode-read-reference") - const remoteRepo = path.join(remoteDir, "repo.git") - yield* put(path.join(source, "notes.md"), "reference notes") - yield* git(source, ["add", "."]) - yield* git(source, ["commit", "-m", "add notes"]) - yield* fs.makeDirectory(remoteDir, { recursive: true }).pipe(Effect.orDie) - yield* git(remoteRoot, ["clone", "--bare", source, remoteRepo]) - - const dir = yield* tmpdirScoped({ - git: true, - config: { - reference: { - docs: "opencode-read-reference/repo", - }, + scout.live("does not ask for external_directory permission when reading configured references", () => + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const cache = path.join(Global.Path.repos, "github.com", "opencode-read-reference", "repo") + yield* fs.remove(cache, { recursive: true }).pipe(Effect.ignore) + yield* Effect.addFinalizer(() => fs.remove(cache, { recursive: true }).pipe(Effect.ignore)) + + const source = yield* tmpdirScoped({ git: true }) + const remoteRoot = yield* tmpdirScoped() + const remoteDir = path.join(remoteRoot, "opencode-read-reference") + const remoteRepo = path.join(remoteDir, "repo.git") + yield* put(path.join(source, "notes.md"), "reference notes") + yield* git(source, ["add", "."]) + yield* git(source, ["commit", "-m", "add notes"]) + yield* fs.makeDirectory(remoteDir, { recursive: true }).pipe(Effect.orDie) + yield* git(remoteRoot, ["clone", "--bare", source, remoteRepo]) + + const dir = yield* tmpdirScoped({ + git: true, + config: { + reference: { + docs: "opencode-read-reference/repo", }, - }) + }, + }) - const { items, next } = asks() - const result = yield* githubBase( - `file://${remoteRoot}/`, - exec(dir, { filePath: path.join(cache, "notes.md") }, next), - ) - const ext = items.find((item) => item.permission === "external_directory") + const { items, next } = asks() + const result = yield* githubBase( + `file://${remoteRoot}/`, + exec(dir, { filePath: path.join(cache, "notes.md") }, next), + ) + const ext = items.find((item) => item.permission === "external_directory") - expect(result.output).toContain("reference notes") - expect(ext).toBeUndefined() - }), - ), + expect(result.output).toContain("reference notes") + expect(ext).toBeUndefined() + }), ) }) diff --git a/packages/opencode/test/tool/registry.test.ts b/packages/opencode/test/tool/registry.test.ts index 78326c737dd..ec772e1ee79 100644 --- a/packages/opencode/test/tool/registry.test.ts +++ b/packages/opencode/test/tool/registry.test.ts @@ -1,12 +1,12 @@ import { afterEach, describe, expect } from "bun:test" import path from "path" import fs from "fs/promises" -import { Effect, Layer } from "effect" +import { pathToFileURL } from "url" +import { Effect, Layer, Result, Schema } from "effect" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { ToolRegistry } from "@/tool/registry" -import { Command } from "@/command" // kilocode_change -import { Flag } from "@opencode-ai/core/flag/flag" -import { disposeAllInstances, provideTmpdirInstance, TestInstance } from "../fixture/fixture" // kilocode_change +import { Tool } from "@/tool/tool" +import { disposeAllInstances, TestInstance } from "../fixture/fixture" import { testEffect } from "../lib/effect" import { TestConfig } from "../fixture/config" import { AppFileSystem } from "@opencode-ai/core/filesystem" @@ -15,7 +15,9 @@ import { Question } from "@/question" import { Todo } from "@/session/todo" import { Skill } from "@/skill" import { Agent } from "@/agent/agent" +import { BackgroundJob } from "@/background/job" import { Session } from "@/session/session" +import { SessionStatus } from "@/session/status" import { Provider } from "@/provider/provider" import { Git } from "@/git" import { LSP } from "@/lsp/lsp" @@ -26,128 +28,119 @@ import { Format } from "@/format" import { Ripgrep } from "@/file/ripgrep" import * as Truncate from "@/tool/truncate" import { InstanceState } from "@/effect/instance-state" -import { SessionStatus } from "@/session/status" // kilocode_change import { Reference } from "@/reference/reference" +import { ProviderID, ModelID } from "@/provider/schema" +import { ToolJsonSchema } from "@/tool/json-schema" +import { MessageID, SessionID } from "@/session/schema" +import { RuntimeFlags } from "@/effect/runtime-flags" +import { Command } from "@/command" const node = CrossSpawnSpawner.defaultLayer -const originalExperimentalScout = Flag.KILO_EXPERIMENTAL_SCOUT const configLayer = TestConfig.layer({ directories: () => InstanceState.directory.pipe(Effect.map((dir) => [path.join(dir, ".opencode")])), }) -const registryBase = ToolRegistry.layer.pipe( - // kilocode_change - Layer.provide(configLayer), - Layer.provide(Plugin.defaultLayer), - Layer.provide(Question.defaultLayer), - Layer.provide(Todo.defaultLayer), - Layer.provide(Skill.defaultLayer), - Layer.provide(Agent.defaultLayer), - Layer.provide(Session.defaultLayer), - Layer.provide(Provider.defaultLayer), - Layer.provide(Git.defaultLayer), - Layer.provide(Reference.defaultLayer), - Layer.provide(LSP.defaultLayer), - Layer.provide(Instruction.defaultLayer), - Layer.provide(AppFileSystem.defaultLayer), - Layer.provide(Bus.layer), - Layer.provide(FetchHttpClient.layer), - Layer.provide(Format.defaultLayer), - Layer.provide(node), - Layer.provide(Ripgrep.defaultLayer), - Layer.provide(Truncate.defaultLayer), -) - -// kilocode_change start -const registryLayer = registryBase.pipe(Layer.provide(Command.defaultLayer), Layer.provide(SessionStatus.defaultLayer)) -// kilocode_change end +const registryLayer = (flags: Partial = {}) => { + const deps = Layer.mergeAll( + configLayer, + Plugin.defaultLayer, + Question.defaultLayer, + Todo.defaultLayer, + Skill.defaultLayer, + Agent.defaultLayer, + Session.defaultLayer, + Layer.mergeAll(SessionStatus.defaultLayer, BackgroundJob.defaultLayer), + Provider.defaultLayer, + Git.defaultLayer, + Reference.defaultLayer, + LSP.defaultLayer, + Instruction.defaultLayer, + Command.defaultLayer, + AppFileSystem.defaultLayer, + Bus.layer, + FetchHttpClient.layer, + Format.defaultLayer, + node, + Layer.mergeAll(Ripgrep.defaultLayer, Truncate.defaultLayer), + ) + return ToolRegistry.layer.pipe( + Layer.provide(deps), + Layer.provide(RuntimeFlags.layer(flags)), + Layer.provide(node), + Layer.provide(Agent.defaultLayer), + ) +} -const it = testEffect(Layer.mergeAll(registryLayer, node)) +const it = testEffect(Layer.mergeAll(registryLayer(), Agent.defaultLayer, RuntimeFlags.layer(), configLayer)) +const scout = testEffect( + Layer.mergeAll(registryLayer({ experimentalScout: true }), Agent.defaultLayer, RuntimeFlags.layer(), configLayer), +) +const background = testEffect( + Layer.mergeAll( + registryLayer({ experimentalBackgroundSubagents: true }), + Agent.defaultLayer, + RuntimeFlags.layer(), + configLayer, + ), +) afterEach(async () => { - Flag.KILO_EXPERIMENTAL_SCOUT = originalExperimentalScout await disposeAllInstances() }) describe("tool.registry", () => { - // kilocode_change start - plan_exit is always registered - it.live("plan_exit is always registered regardless of client", () => + it.instance("hides repo research tools unless experimental", () => Effect.gen(function* () { - const original = process.env["KILO_CLIENT"] - try { - for (const client of ["cli", "vscode", "desktop", "app"]) { - process.env["KILO_CLIENT"] = client - yield* provideTmpdirInstance( - () => - Effect.gen(function* () { - const registry = yield* ToolRegistry.Service - const ids = yield* registry.ids() - expect(ids).toContain("plan_exit") - }), - { git: true }, - ) - } - } finally { - if (original === undefined) delete process.env["KILO_CLIENT"] - else process.env["KILO_CLIENT"] = original - } + const registry = yield* ToolRegistry.Service + const ids = yield* registry.ids() + + expect(ids).not.toContain("repo_clone") + expect(ids).not.toContain("repo_overview") }), ) - it.live("suggest is registered for cli and vscode only", () => + scout.instance("shows repo research tools when experimental scout is enabled", () => Effect.gen(function* () { - const original = process.env["KILO_CLIENT"] - const originalQuestion = process.env["KILO_ENABLE_QUESTION_TOOL"] - const originalConfig = process.env["KILO_CONFIG_DIR"] - try { - for (const client of ["cli", "vscode", "desktop", "app"]) { - process.env["KILO_CLIENT"] = client - process.env["KILO_ENABLE_QUESTION_TOOL"] = client === "vscode" ? "true" : "false" - yield* provideTmpdirInstance( - (dir) => - Effect.gen(function* () { - process.env["KILO_CONFIG_DIR"] = dir - const registry = yield* ToolRegistry.Service - const ids = yield* registry.ids() - if (client === "cli" || client === "vscode") expect(ids).toContain("suggest") - else expect(ids).not.toContain("suggest") - }), - { git: true }, - ) - } - } finally { - if (original === undefined) delete process.env["KILO_CLIENT"] - else process.env["KILO_CLIENT"] = original - if (originalQuestion === undefined) delete process.env["KILO_ENABLE_QUESTION_TOOL"] - else process.env["KILO_ENABLE_QUESTION_TOOL"] = originalQuestion - if (originalConfig === undefined) delete process.env["KILO_CONFIG_DIR"] - else process.env["KILO_CONFIG_DIR"] = originalConfig - } + const registry = yield* ToolRegistry.Service + const ids = yield* registry.ids() + + expect(ids).toContain("repo_clone") + expect(ids).toContain("repo_overview") }), ) - // kilocode_change end - it.instance("hides repo research tools unless experimental", () => + it.instance("hides task_status unless experimental background subagents are enabled", () => Effect.gen(function* () { - Flag.KILO_EXPERIMENTAL_SCOUT = false const registry = yield* ToolRegistry.Service const ids = yield* registry.ids() - expect(ids).not.toContain("codesearch") - expect(ids).not.toContain("repo_clone") - expect(ids).not.toContain("repo_overview") + expect(ids).not.toContain("task_status") + }), + ) + + it.instance("hides task background parameter unless experimental background subagents are enabled", () => + Effect.gen(function* () { + const registry = yield* ToolRegistry.Service + const agent = yield* Agent.Service + const build = yield* agent.get("build") + if (!build) throw new Error("build agent not found") + const task = (yield* registry.tools({ + providerID: ProviderID.opencode, + modelID: ModelID.make("test"), + agent: build, + })).find((tool) => tool.id === "task") + + expect(task?.jsonSchema).toBeDefined() + expect((task?.jsonSchema?.properties as Record | undefined)?.background).toBeUndefined() }), ) - it.instance("shows repo research tools when experimental scout is enabled", () => + background.instance("shows task_status when experimental background subagents are enabled", () => Effect.gen(function* () { - Flag.KILO_EXPERIMENTAL_SCOUT = true const registry = yield* ToolRegistry.Service const ids = yield* registry.ids() - expect(ids).toContain("codesearch") - expect(ids).toContain("repo_clone") - expect(ids).toContain("repo_overview") + expect(ids).toContain("task_status") }), ) @@ -205,6 +198,134 @@ describe("tool.registry", () => { }), ) + it.instance("loads Zod-schema custom tools with JSON Schema and validation", () => + Effect.gen(function* () { + const test = yield* TestInstance + const customTools = path.join(test.directory, ".opencode", "tools") + const pluginTool = pathToFileURL(path.resolve(import.meta.dir, "../../../plugin/src/tool.ts")).href + yield* Effect.promise(() => fs.mkdir(customTools, { recursive: true })) + yield* Effect.promise(() => + Bun.write( + path.join(customTools, "sql.ts"), + [ + `import { tool } from ${JSON.stringify(pluginTool)}`, + "export default tool({", + " description: 'query database',", + " args: { query: tool.schema.string().describe('SQL query to execute') },", + " execute: async ({ query }) => query,", + "})", + "", + ].join("\n"), + ), + ) + + const registry = yield* ToolRegistry.Service + const loaded = (yield* registry.all()).find((tool) => tool.id === "sql") + if (!loaded) throw new Error("custom sql tool was not loaded") + expect(loaded?.jsonSchema).toMatchObject({ + type: "object", + properties: { + query: { type: "string", description: "SQL query to execute" }, + }, + required: ["query"], + }) + expect(Result.isSuccess(Schema.decodeUnknownResult(loaded.parameters)({ query: "select 1" }))).toBe(true) + expect(Result.isSuccess(Schema.decodeUnknownResult(loaded.parameters)({}))).toBe(false) + + const agents = yield* Agent.Service + const promptTools = yield* registry.tools({ + providerID: ProviderID.opencode, + modelID: ModelID.make("test"), + agent: yield* agents.defaultInfo(), + }) + const promptTool = promptTools.find((tool) => tool.id === "sql") + if (!promptTool) throw new Error("custom sql tool was not returned for prompts") + expect(ToolJsonSchema.fromTool(promptTool)).toMatchObject({ + properties: { + query: { type: "string", description: "SQL query to execute" }, + }, + required: ["query"], + }) + }), + ) + + it.instance("preserves attachments from structured custom tool results", () => + Effect.gen(function* () { + const test = yield* TestInstance + const customTools = path.join(test.directory, ".opencode", "tools") + const pluginTool = pathToFileURL(path.resolve(import.meta.dir, "../../../plugin/src/tool.ts")).href + yield* Effect.promise(() => fs.mkdir(customTools, { recursive: true })) + yield* Effect.promise(() => + Bun.write( + path.join(customTools, "image.ts"), + [ + `import { tool } from ${JSON.stringify(pluginTool)}`, + "export default tool({", + " description: 'image tool',", + " args: {},", + " execute: async () => ({", + " output: 'here is an image',", + " attachments: [{ type: 'file', mime: 'image/png', filename: 'picture.png', url: 'data:image/png;base64,AAAA' }],", + " }),", + "})", + "", + ].join("\n"), + ), + ) + + const registry = yield* ToolRegistry.Service + const loaded = (yield* registry.all()).find((tool) => tool.id === "image") + if (!loaded) throw new Error("custom image tool was not loaded") + const agents = yield* Agent.Service + const result = yield* loaded.execute({}, { + sessionID: SessionID.make("ses_test"), + messageID: MessageID.make("msg_test"), + agent: (yield* agents.defaultInfo()).name, + abort: new AbortController().signal, + messages: [], + metadata: () => Effect.void, + ask: () => Effect.void, + } satisfies Tool.Context) + + expect(result.output).toBe("here is an image") + expect(result.attachments).toEqual([ + { type: "file", mime: "image/png", filename: "picture.png", url: "data:image/png;base64,AAAA" }, + ]) + }), + ) + + it.instance("loads legacy JSON-schema-shaped custom tools with wire schema", () => + Effect.gen(function* () { + const test = yield* TestInstance + const tools = path.join(test.directory, ".opencode", "tools") + yield* Effect.promise(() => fs.mkdir(tools, { recursive: true })) + yield* Effect.promise(() => + Bun.write( + path.join(tools, "legacy.ts"), + [ + "export default {", + " description: 'legacy schema tool',", + " args: { text: { type: 'string', description: 'Text to render' } },", + " execute: async ({ text }) => text,", + "}", + "", + ].join("\n"), + ), + ) + + const registry = yield* ToolRegistry.Service + const loaded = (yield* registry.all()).find((tool) => tool.id === "legacy") + if (!loaded) throw new Error("legacy custom tool was not loaded") + expect(ToolJsonSchema.fromTool(loaded)).toMatchObject({ + type: "object", + properties: { + text: { type: "string", description: "Text to render" }, + }, + required: ["text"], + }) + }), + ) + it.instance("loads tools with external dependencies without crashing", () => Effect.gen(function* () { const test = yield* TestInstance @@ -217,7 +338,7 @@ describe("tool.registry", () => { JSON.stringify({ name: "custom-tools", dependencies: { - "@kilocode/plugin": "^0.0.0", + "@opencode-ai/plugin": "^0.0.0", cowsay: "^1.6.0", }, }), @@ -232,7 +353,7 @@ describe("tool.registry", () => { packages: { "": { dependencies: { - "@kilocode/plugin": "^0.0.0", + "@opencode-ai/plugin": "^0.0.0", cowsay: "^1.6.0", }, }, diff --git a/packages/opencode/test/tool/shell.test.ts b/packages/opencode/test/tool/shell.test.ts index 2fb383f8a2c..e42d084a47e 100644 --- a/packages/opencode/test/tool/shell.test.ts +++ b/packages/opencode/test/tool/shell.test.ts @@ -1,14 +1,13 @@ -import { describe, expect, test } from "bun:test" -import { Effect, Layer, ManagedRuntime } from "effect" +import { describe, expect } from "bun:test" +import { Cause, Effect, Exit, Layer } from "effect" +import type * as Scope from "effect/Scope" import os from "os" import path from "path" import { Config } from "@/config/config" import { Shell } from "../../src/shell/shell" import { ShellTool } from "../../src/tool/shell" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" import { Filesystem } from "@/util/filesystem" -import { tmpdir } from "../fixture/fixture" +import { provideInstance, tmpdirScoped } from "../fixture/fixture" import type { Permission } from "../../src/permission" import { Agent } from "../../src/agent/agent" import { Truncate } from "@/tool/truncate" @@ -16,23 +15,52 @@ import { SessionID, MessageID } from "../../src/session/schema" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { AppFileSystem } from "@opencode-ai/core/filesystem" import { Plugin } from "../../src/plugin" +import { testEffect } from "../lib/effect" +import { Tool } from "@/tool/tool" +import { RuntimeFlags } from "@/effect/runtime-flags" -const runtime = ManagedRuntime.make( - Layer.mergeAll( - CrossSpawnSpawner.defaultLayer, - AppFileSystem.defaultLayer, - Plugin.defaultLayer, - Truncate.defaultLayer, - Config.defaultLayer, - Agent.defaultLayer, - ), +const shellLayer = Layer.mergeAll( + CrossSpawnSpawner.defaultLayer, + AppFileSystem.defaultLayer, + Plugin.defaultLayer, + Truncate.defaultLayer, + Config.defaultLayer, + Agent.defaultLayer, + RuntimeFlags.defaultLayer, ) +const it = testEffect(shellLayer) +type ShellTestServices = + | (typeof shellLayer extends Layer.Layer ? ROut : never) + | Scope.Scope -function initBash() { - return runtime.runPromise(ShellTool.pipe(Effect.flatMap((info) => info.init()))) -} +const initShell = Effect.fn("ShellToolTest.init")(function* () { + const info = yield* ShellTool + return yield* info.init() +}) -const initShell = initBash +const initBash = initShell + +const run = Effect.fn("ShellToolTest.run")(function* ( + args: Tool.InferParameters, + next: Tool.Context = ctx, +) { + const bash = yield* initShell() + return yield* bash.execute(args, next) +}) + +const runIn = (directory: string, self: Effect.Effect) => self.pipe(provideInstance(directory)) + +const fail = Effect.fn("ShellToolTest.fail")(function* ( + args: Tool.InferParameters, + next: Tool.Context = ctx, +) { + const exit = yield* run(args, next).pipe(Effect.exit) + if (Exit.isFailure(exit)) { + const err = Cause.squash(exit.cause) + return err instanceof Error ? err : new Error(String(err)) + } + throw new Error("expected command to fail") +}) const ctx = { sessionID: SessionID.make("ses_test"), @@ -96,27 +124,31 @@ const forms = (dir: string) => { return Array.from(new Set([full, slash, root, root.toLowerCase()])) } -const withShell = (item: { label: string; shell: string }, fn: () => Promise) => async () => { - const prev = process.env.SHELL - process.env.SHELL = item.shell - Shell.acceptable.reset() - Shell.preferred.reset() - try { - await fn() - } finally { - if (prev === undefined) delete process.env.SHELL - else process.env.SHELL = prev - Shell.acceptable.reset() - Shell.preferred.reset() - } -} +const withShell = (item: { label: string; shell: string }, self: Effect.Effect) => + Effect.acquireUseRelease( + Effect.sync(() => { + const prev = process.env.SHELL + process.env.SHELL = item.shell + Shell.acceptable.reset() + Shell.preferred.reset() + return prev + }), + () => self, + (prev) => + Effect.sync(() => { + if (prev === undefined) delete process.env.SHELL + else process.env.SHELL = prev + Shell.acceptable.reset() + Shell.preferred.reset() + }), + ) -const each = (name: string, fn: (item: { label: string; shell: string }) => Promise) => { +const each = ( + name: string, + fn: (item: { label: string; shell: string }) => Effect.Effect, +) => { for (const item of shells) { - test( - `${name} [${item.label}]`, - withShell(item, () => fn(item)), - ) + it.live(`${name} [${item.label}]`, () => withShell(item, fn(item))) } } @@ -140,277 +172,248 @@ const mustTruncate = (result: { } describe("tool.shell", () => { - each("basic", async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initShell() - const result = await Effect.runPromise( - bash.execute( - { - command: "echo test", - description: "Echo test message", - }, - ctx, - ), - ) + each("basic", () => + runIn( + projectRoot, + Effect.gen(function* () { + const result = yield* run({ + command: "echo test", + description: "Echo test message", + }) expect(result.metadata.exit).toBe(0) expect(result.metadata.output).toContain("test") - }, - }) - }) - - test("falls back from terminal-only configured shell", async () => { - await using tmp = await tmpdir({ - config: { shell: "fish" }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const bash = await initBash() - const fallback = Shell.name(Shell.acceptable("fish")) - expect(fallback).not.toBe("fish") - expect(bash.description).toContain(fallback) - - const result = await Effect.runPromise( - bash.execute( + }), + ), + ) + + it.live("falls back from terminal-only configured shell", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped({ config: { shell: "fish" } }) + yield* runIn( + tmp, + Effect.gen(function* () { + const bash = yield* initBash() + const fallback = Shell.name(Shell.acceptable("fish")) + expect(fallback).not.toBe("fish") + expect(bash.description).toContain(fallback) + + const result = yield* bash.execute( { command: "echo fallback", description: "Echo fallback text", }, ctx, - ), - ) - expect(result.metadata.exit).toBe(0) - expect(result.output).toContain("fallback") - }, - }) - }) + ) + expect(result.metadata.exit).toBe(0) + expect(result.output).toContain("fallback") + }), + ) + }), + ) }) describe("tool.shell permissions", () => { - each("asks for bash permission with correct pattern", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const bash = await initShell() - const requests: Array> = [] - await Effect.runPromise( - bash.execute( + each("asks for bash permission with correct pattern", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped() + yield* runIn( + tmp, + Effect.gen(function* () { + const requests: Array> = [] + yield* run( { command: "echo hello", description: "Echo hello", }, capture(requests), - ), - ) - expect(requests.length).toBe(1) - expect(requests[0].permission).toBe("bash") - expect(requests[0].patterns).toContain("echo hello") - }, - }) - }) - - each("asks for bash permission with multiple commands", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const bash = await initShell() - const requests: Array> = [] - await Effect.runPromise( - bash.execute( + ) + expect(requests.length).toBe(1) + expect(requests[0].permission).toBe("bash") + expect(requests[0].patterns).toContain("echo hello") + }), + ) + }), + ) + + each("asks for bash permission with multiple commands", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped() + yield* runIn( + tmp, + Effect.gen(function* () { + const requests: Array> = [] + yield* run( { command: "echo foo && echo bar", description: "Echo twice", }, capture(requests), - ), - ) - expect(requests.length).toBe(1) - expect(requests[0].permission).toBe("bash") - expect(requests[0].patterns).toContain("echo foo") - expect(requests[0].patterns).toContain("echo bar") - }, - }) - }) + ) + expect(requests.length).toBe(1) + expect(requests[0].permission).toBe("bash") + expect(requests[0].patterns).toContain("echo foo") + expect(requests[0].patterns).toContain("echo bar") + }), + ) + }), + ) for (const item of ps) { - test( - `parses PowerShell conditionals for permission prompts [${item.label}]`, - withShell(item, async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initShell() + it.live(`parses PowerShell conditionals for permission prompts [${item.label}]`, () => + withShell( + item, + runIn( + projectRoot, + Effect.gen(function* () { const requests: Array> = [] - await Effect.runPromise( - bash.execute( - { - command: "Write-Host foo; if ($?) { Write-Host bar }", - description: "Check PowerShell conditional", - }, - capture(requests), - ), + yield* run( + { + command: "Write-Host foo; if ($?) { Write-Host bar }", + description: "Check PowerShell conditional", + }, + capture(requests), ) const bashReq = requests.find((r) => r.permission === "bash") expect(bashReq).toBeDefined() expect(bashReq!.patterns).toContain("Write-Host foo") expect(bashReq!.patterns).toContain("Write-Host bar") expect(bashReq!.always).toContain("Write-Host *") - }, - }) - }), + }), + ), + ), ) } for (const item of ps) { - test( - `uses PowerShell cmdlet prefixes for always-allow prompts [${item.label}]`, - withShell(item, async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const bash = await initShell() - const err = new Error("stop after permission") - const requests: Array> = [] - await expect( - Effect.runPromise( - bash.execute( + it.live(`uses PowerShell cmdlet prefixes for always-allow prompts [${item.label}]`, () => + withShell( + item, + Effect.gen(function* () { + const tmp = yield* tmpdirScoped() + yield* runIn( + tmp, + Effect.gen(function* () { + const err = new Error("stop after permission") + const requests: Array> = [] + expect( + yield* fail( { command: "Remove-Item -Recurse tmp", description: "Remove a temp directory", }, capture(requests, err), ), - ), - ).rejects.toThrow(err.message) - const bashReq = requests.find((r) => r.permission === "bash") - expect(bashReq).toBeDefined() - expect(bashReq!.always).toContain("Remove-Item *") - expect(bashReq!.always).not.toContain("Remove-Item -Recurse *") - }, - }) - }), + ).toMatchObject({ message: err.message }) + const bashReq = requests.find((r) => r.permission === "bash") + expect(bashReq).toBeDefined() + expect(bashReq!.always).toContain("Remove-Item *") + expect(bashReq!.always).not.toContain("Remove-Item -Recurse *") + }), + ) + }), + ), ) } - each("asks for external_directory permission for wildcard external paths", async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initShell() + each("asks for external_directory permission for wildcard external paths", () => + runIn( + projectRoot, + Effect.gen(function* () { const err = new Error("stop after permission") const requests: Array> = [] const file = process.platform === "win32" ? `${process.env.WINDIR!.replaceAll("\\", "/")}/*` : "/etc/*" const want = process.platform === "win32" ? glob(path.join(process.env.WINDIR!, "*")) : "/etc/*" - await expect( - Effect.runPromise( - bash.execute( - { - command: `cat ${file}`, - description: "Read wildcard path", - }, - capture(requests, err), - ), + expect( + yield* fail( + { + command: `cat ${file}`, + description: "Read wildcard path", + }, + capture(requests, err), ), - ).rejects.toThrow(err.message) + ).toMatchObject({ message: err.message }) const extDirReq = requests.find((r) => r.permission === "external_directory") expect(extDirReq).toBeDefined() expect(extDirReq!.patterns).toContain(want) - }, - }) - }) + }), + ), + ) if (process.platform === "win32") { if (bash) { - test( - "asks for nested bash command permissions [bash]", - withShell({ label: "bash", shell: bash }, async () => { - await using outerTmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "outside.txt"), "x") - }, - }) - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initShell() - const file = path.join(outerTmp.path, "outside.txt").replaceAll("\\", "/") - const requests: Array> = [] - await Effect.runPromise( - bash.execute( + it.live("asks for nested bash command permissions [bash]", () => + withShell( + { label: "bash", shell: bash }, + Effect.gen(function* () { + const outerTmp = yield* tmpdirScoped() + yield* Effect.promise(() => Bun.write(path.join(outerTmp, "outside.txt"), "x")) + yield* runIn( + projectRoot, + Effect.gen(function* () { + const file = path.join(outerTmp, "outside.txt").replaceAll("\\", "/") + const requests: Array> = [] + yield* run( { command: `echo $(cat "${file}")`, description: "Read nested bash file", }, capture(requests), - ), - ) - const extDirReq = requests.find((r) => r.permission === "external_directory") - const bashReq = requests.find((r) => r.permission === "bash") - expect(extDirReq).toBeDefined() - expect(extDirReq!.patterns).toContain(glob(path.join(outerTmp.path, "*"))) - expect(bashReq).toBeDefined() - expect(bashReq!.patterns).toContain(`cat "${file}"`) - }, - }) - }), + ) + const extDirReq = requests.find((r) => r.permission === "external_directory") + const bashReq = requests.find((r) => r.permission === "bash") + expect(extDirReq).toBeDefined() + expect(extDirReq!.patterns).toContain(glob(path.join(outerTmp, "*"))) + expect(bashReq).toBeDefined() + expect(bashReq!.patterns).toContain(`cat "${file}"`) + }), + ) + }), + ), ) } - } - if (process.platform === "win32") { for (const item of ps) { - test( - `asks for external_directory permission for PowerShell paths after switches [${item.label}]`, - withShell(item, async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initShell() + it.live(`asks for external_directory permission for PowerShell paths after switches [${item.label}]`, () => + withShell( + item, + runIn( + projectRoot, + Effect.gen(function* () { const err = new Error("stop after permission") const requests: Array> = [] - await expect( - Effect.runPromise( - bash.execute( - { - command: `Copy-Item -PassThru "${process.env.WINDIR!.replaceAll("\\", "/")}/win.ini" ./out`, - description: "Copy Windows ini", - }, - capture(requests, err), - ), + expect( + yield* fail( + { + command: `Copy-Item -PassThru "${process.env.WINDIR!.replaceAll("\\", "/")}/win.ini" ./out`, + description: "Copy Windows ini", + }, + capture(requests, err), ), - ).rejects.toThrow(err.message) + ).toMatchObject({ message: err.message }) const extDirReq = requests.find((r) => r.permission === "external_directory") expect(extDirReq).toBeDefined() expect(extDirReq!.patterns).toContain(glob(path.join(process.env.WINDIR!, "*"))) - }, - }) - }), + }), + ), + ), ) } for (const item of ps) { - test( - `asks for nested PowerShell command permissions [${item.label}]`, - withShell(item, async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initShell() + it.live(`asks for nested PowerShell command permissions [${item.label}]`, () => + withShell( + item, + runIn( + projectRoot, + Effect.gen(function* () { const requests: Array> = [] const file = `${process.env.WINDIR!.replaceAll("\\", "/")}/win.ini` - await Effect.runPromise( - bash.execute( - { - command: `Write-Output $(Get-Content ${file})`, - description: "Read nested PowerShell file", - }, - capture(requests), - ), + yield* run( + { + command: `Write-Output $(Get-Content ${file})`, + description: "Read nested PowerShell file", + }, + capture(requests), ) const extDirReq = requests.find((r) => r.permission === "external_directory") const bashReq = requests.find((r) => r.permission === "bash") @@ -418,283 +421,266 @@ describe("tool.shell permissions", () => { expect(extDirReq!.patterns).toContain(glob(path.join(process.env.WINDIR!, "*"))) expect(bashReq).toBeDefined() expect(bashReq!.patterns).toContain(`Get-Content ${file}`) - }, - }) - }), + }), + ), + ), ) } for (const item of ps) { - test( - `asks for external_directory permission for drive-relative PowerShell paths [${item.label}]`, - withShell(item, async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const bash = await initShell() - const err = new Error("stop after permission") - const requests: Array> = [] - await expect( - Effect.runPromise( - bash.execute( + it.live(`asks for external_directory permission for drive-relative PowerShell paths [${item.label}]`, () => + withShell( + item, + Effect.gen(function* () { + const tmp = yield* tmpdirScoped() + yield* runIn( + tmp, + Effect.gen(function* () { + const err = new Error("stop after permission") + const requests: Array> = [] + expect( + yield* fail( { command: 'Get-Content "C:../outside.txt"', description: "Read drive-relative file", }, capture(requests, err), ), - ), - ).rejects.toThrow(err.message) - expect(requests[0]?.permission).toBe("external_directory") - if (requests[0]?.permission !== "external_directory") return - expect(requests[0].patterns).toContain(glob(path.join(path.dirname(tmp.path), "*"))) - }, - }) - }), + ).toMatchObject({ message: err.message }) + expect(requests[0]?.permission).toBe("external_directory") + if (requests[0]?.permission !== "external_directory") return + expect(requests[0].patterns).toContain(glob(path.join(path.dirname(tmp), "*"))) + }), + ) + }), + ), ) } for (const item of ps) { - test( - `asks for external_directory permission for $HOME PowerShell paths [${item.label}]`, - withShell(item, async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initShell() + it.live(`asks for external_directory permission for $HOME PowerShell paths [${item.label}]`, () => + withShell( + item, + runIn( + projectRoot, + Effect.gen(function* () { const err = new Error("stop after permission") const requests: Array> = [] - await expect( - Effect.runPromise( - bash.execute( - { - command: 'Get-Content "$HOME/.ssh/config"', - description: "Read home config", - }, - capture(requests, err), - ), + expect( + yield* fail( + { + command: 'Get-Content "$HOME/.ssh/config"', + description: "Read home config", + }, + capture(requests, err), ), - ).rejects.toThrow(err.message) + ).toMatchObject({ message: err.message }) expect(requests[0]?.permission).toBe("external_directory") if (requests[0]?.permission !== "external_directory") return expect(requests[0].patterns).toContain(glob(path.join(os.homedir(), ".ssh", "*"))) - }, - }) - }), + }), + ), + ), ) } for (const item of ps) { - test( - `asks for external_directory permission for $PWD PowerShell paths [${item.label}]`, - withShell(item, async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const bash = await initBash() - const err = new Error("stop after permission") - const requests: Array> = [] - await expect( - Effect.runPromise( - bash.execute( + it.live(`asks for external_directory permission for $PWD PowerShell paths [${item.label}]`, () => + withShell( + item, + Effect.gen(function* () { + const tmp = yield* tmpdirScoped() + yield* runIn( + tmp, + Effect.gen(function* () { + const err = new Error("stop after permission") + const requests: Array> = [] + expect( + yield* fail( { command: 'Get-Content "$PWD/../outside.txt"', description: "Read pwd-relative file", }, capture(requests, err), ), - ), - ).rejects.toThrow(err.message) - expect(requests[0]?.permission).toBe("external_directory") - if (requests[0]?.permission !== "external_directory") return - expect(requests[0].patterns).toContain(glob(path.join(path.dirname(tmp.path), "*"))) - }, - }) - }), + ).toMatchObject({ message: err.message }) + expect(requests[0]?.permission).toBe("external_directory") + if (requests[0]?.permission !== "external_directory") return + expect(requests[0].patterns).toContain(glob(path.join(path.dirname(tmp), "*"))) + }), + ) + }), + ), ) } for (const item of ps) { - test( - `asks for external_directory permission for $PSHOME PowerShell paths [${item.label}]`, - withShell(item, async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initBash() + it.live(`asks for external_directory permission for $PSHOME PowerShell paths [${item.label}]`, () => + withShell( + item, + runIn( + projectRoot, + Effect.gen(function* () { const err = new Error("stop after permission") const requests: Array> = [] - await expect( - Effect.runPromise( - bash.execute( - { - command: 'Get-Content "$PSHOME/outside.txt"', - description: "Read pshome file", - }, - capture(requests, err), - ), + expect( + yield* fail( + { + command: 'Get-Content "$PSHOME/outside.txt"', + description: "Read pshome file", + }, + capture(requests, err), ), - ).rejects.toThrow(err.message) + ).toMatchObject({ message: err.message }) expect(requests[0]?.permission).toBe("external_directory") if (requests[0]?.permission !== "external_directory") return expect(requests[0].patterns).toContain(glob(path.join(path.dirname(item.shell), "*"))) - }, - }) - }), + }), + ), + ), ) } for (const item of ps) { - test( - `asks for external_directory permission for missing PowerShell env paths [${item.label}]`, - withShell(item, async () => { - const key = "KILO_TEST_MISSING" - const prev = process.env[key] - delete process.env[key] - try { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initShell() - const err = new Error("stop after permission") - const requests: Array> = [] - const root = path.parse(process.env.WINDIR!).root.replace(/[\\/]+$/, "") - await expect( - Effect.runPromise( - bash.execute( + it.live(`asks for external_directory permission for missing PowerShell env paths [${item.label}]`, () => + withShell( + item, + Effect.acquireUseRelease( + Effect.sync(() => { + const key = "KILO_TEST_MISSING" + const prev = process.env[key] + delete process.env[key] + return { key, prev } + }), + ({ key }) => + runIn( + projectRoot, + Effect.gen(function* () { + const err = new Error("stop after permission") + const requests: Array> = [] + const root = path.parse(process.env.WINDIR!).root.replace(/[\\/]+$/, "") + expect( + yield* fail( { command: `Get-Content -Path "${root}$env:${key}\\Windows\\win.ini"`, description: "Read Windows ini with missing env", }, capture(requests, err), ), - ), - ).rejects.toThrow(err.message) - const extDirReq = requests.find((r) => r.permission === "external_directory") - expect(extDirReq).toBeDefined() - expect(extDirReq!.patterns).toContain(glob(path.join(process.env.WINDIR!, "*"))) - }, - }) - } finally { - if (prev === undefined) delete process.env[key] - else process.env[key] = prev - } - }), + ).toMatchObject({ message: err.message }) + const extDirReq = requests.find((r) => r.permission === "external_directory") + expect(extDirReq).toBeDefined() + expect(extDirReq!.patterns).toContain(glob(path.join(process.env.WINDIR!, "*"))) + }), + ), + ({ key, prev }) => + Effect.sync(() => { + if (prev === undefined) delete process.env[key] + else process.env[key] = prev + }), + ), + ), ) } for (const item of ps) { - test( - `asks for external_directory permission for PowerShell env paths [${item.label}]`, - withShell(item, async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initBash() + it.live(`asks for external_directory permission for PowerShell env paths [${item.label}]`, () => + withShell( + item, + runIn( + projectRoot, + Effect.gen(function* () { const requests: Array> = [] - await Effect.runPromise( - bash.execute( - { - command: "Get-Content $env:WINDIR/win.ini", - description: "Read Windows ini from env", - }, - capture(requests), - ), + yield* run( + { + command: "Get-Content $env:WINDIR/win.ini", + description: "Read Windows ini from env", + }, + capture(requests), ) const extDirReq = requests.find((r) => r.permission === "external_directory") expect(extDirReq).toBeDefined() expect(extDirReq!.patterns).toContain( Filesystem.normalizePathPattern(path.join(process.env.WINDIR!, "*")), ) - }, - }) - }), + }), + ), + ), ) } for (const item of ps) { - test( - `asks for external_directory permission for PowerShell FileSystem paths [${item.label}]`, - withShell(item, async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initBash() + it.live(`asks for external_directory permission for PowerShell FileSystem paths [${item.label}]`, () => + withShell( + item, + runIn( + projectRoot, + Effect.gen(function* () { const err = new Error("stop after permission") const requests: Array> = [] - await expect( - Effect.runPromise( - bash.execute( - { - command: `Get-Content -Path FileSystem::${process.env.WINDIR!.replaceAll("\\", "/")}/win.ini`, - description: "Read Windows ini from FileSystem provider", - }, - capture(requests, err), - ), + expect( + yield* fail( + { + command: `Get-Content -Path FileSystem::${process.env.WINDIR!.replaceAll("\\", "/")}/win.ini`, + description: "Read Windows ini from FileSystem provider", + }, + capture(requests, err), ), - ).rejects.toThrow(err.message) + ).toMatchObject({ message: err.message }) expect(requests[0]?.permission).toBe("external_directory") if (requests[0]?.permission !== "external_directory") return expect(requests[0].patterns).toContain( Filesystem.normalizePathPattern(path.join(process.env.WINDIR!, "*")), ) - }, - }) - }), + }), + ), + ), ) } for (const item of ps) { - test( - `asks for external_directory permission for braced PowerShell env paths [${item.label}]`, - withShell(item, async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initBash() + it.live(`asks for external_directory permission for braced PowerShell env paths [${item.label}]`, () => + withShell( + item, + runIn( + projectRoot, + Effect.gen(function* () { const err = new Error("stop after permission") const requests: Array> = [] - await expect( - Effect.runPromise( - bash.execute( - { - command: "Get-Content ${env:WINDIR}/win.ini", - description: "Read Windows ini from braced env", - }, - capture(requests, err), - ), + expect( + yield* fail( + { + command: "Get-Content ${env:WINDIR}/win.ini", + description: "Read Windows ini from braced env", + }, + capture(requests, err), ), - ).rejects.toThrow(err.message) + ).toMatchObject({ message: err.message }) expect(requests[0]?.permission).toBe("external_directory") if (requests[0]?.permission !== "external_directory") return expect(requests[0].patterns).toContain( Filesystem.normalizePathPattern(path.join(process.env.WINDIR!, "*")), ) - }, - }) - }), + }), + ), + ), ) } for (const item of ps) { - test( - `treats Set-Location like cd for permissions [${item.label}]`, - withShell(item, async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initBash() + it.live(`treats Set-Location like cd for permissions [${item.label}]`, () => + withShell( + item, + runIn( + projectRoot, + Effect.gen(function* () { const requests: Array> = [] - await Effect.runPromise( - bash.execute( - { - command: "Set-Location C:/Windows", - description: "Change location", - }, - capture(requests), - ), + yield* run( + { + command: "Set-Location C:/Windows", + description: "Change location", + }, + capture(requests), ) const extDirReq = requests.find((r) => r.permission === "external_directory") const bashReq = requests.find((r) => r.permission === "bash") @@ -703,104 +689,96 @@ describe("tool.shell permissions", () => { Filesystem.normalizePathPattern(path.join(process.env.WINDIR!, "*")), ) expect(bashReq).toBeUndefined() - }, - }) - }), + }), + ), + ), ) } for (const item of ps) { - test( - `does not add nested PowerShell expressions to permission prompts [${item.label}]`, - withShell(item, async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initShell() + it.live(`does not add nested PowerShell expressions to permission prompts [${item.label}]`, () => + withShell( + item, + runIn( + projectRoot, + Effect.gen(function* () { const requests: Array> = [] - await Effect.runPromise( - bash.execute( - { - command: "Write-Output ('a' * 3)", - description: "Write repeated text", - }, - capture(requests), - ), + yield* run( + { + command: "Write-Output ('a' * 3)", + description: "Write repeated text", + }, + capture(requests), ) const bashReq = requests.find((r) => r.permission === "bash") expect(bashReq).toBeDefined() expect(bashReq!.patterns).not.toContain("a * 3") expect(bashReq!.always).not.toContain("a *") - }, - }) - }), + }), + ), + ), ) } } if (process.platform === "win32" && cmdShell) { - test( - "asks for external_directory permission for cmd file commands [cmd]", - withShell(cmdShell, async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initShell() + it.live("asks for external_directory permission for cmd file commands [cmd]", () => + withShell( + cmdShell, + runIn( + projectRoot, + Effect.gen(function* () { const requests: Array> = [] - await Effect.runPromise( - bash.execute( - { - command: `TYPE "${path.join(process.env.WINDIR!, "win.ini")}"`, - description: "Read Windows ini with cmd", - }, - capture(requests), - ), + yield* run( + { + command: `TYPE "${path.join(process.env.WINDIR!, "win.ini")}"`, + description: "Read Windows ini with cmd", + }, + capture(requests), ) const extDirReq = requests.find((r) => r.permission === "external_directory") expect(extDirReq).toBeDefined() expect(extDirReq!.patterns).toContain(Filesystem.normalizePathPattern(path.join(process.env.WINDIR!, "*"))) - }, - }) - }), + }), + ), + ), ) } - each("asks for external_directory permission when cd to parent", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const bash = await initBash() - const err = new Error("stop after permission") - const requests: Array> = [] - await expect( - Effect.runPromise( - bash.execute( + each("asks for external_directory permission when cd to parent", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped() + yield* runIn( + tmp, + Effect.gen(function* () { + const err = new Error("stop after permission") + const requests: Array> = [] + expect( + yield* fail( { command: "cd ../", description: "Change to parent directory", }, capture(requests, err), ), - ), - ).rejects.toThrow(err.message) - const extDirReq = requests.find((r) => r.permission === "external_directory") - expect(extDirReq).toBeDefined() - }, - }) - }) - - each("asks for external_directory permission when workdir is outside project", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const bash = await initBash() - const err = new Error("stop after permission") - const requests: Array> = [] - await expect( - Effect.runPromise( - bash.execute( + ).toMatchObject({ message: err.message }) + const extDirReq = requests.find((r) => r.permission === "external_directory") + expect(extDirReq).toBeDefined() + }), + ) + }), + ) + + each("asks for external_directory permission when workdir is outside project", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped() + yield* runIn( + tmp, + Effect.gen(function* () { + const err = new Error("stop after permission") + const requests: Array> = [] + expect( + yield* fail( { command: "echo ok", workdir: os.tmpdir(), @@ -808,31 +786,30 @@ describe("tool.shell permissions", () => { }, capture(requests, err), ), - ), - ).rejects.toThrow(err.message) - const extDirReq = requests.find((r) => r.permission === "external_directory") - expect(extDirReq).toBeDefined() - expect(extDirReq!.patterns).toContain(glob(path.join(os.tmpdir(), "*"))) - }, - }) - }) + ).toMatchObject({ message: err.message }) + const extDirReq = requests.find((r) => r.permission === "external_directory") + expect(extDirReq).toBeDefined() + expect(extDirReq!.patterns).toContain(glob(path.join(os.tmpdir(), "*"))) + }), + ) + }), + ) if (process.platform === "win32") { - test("normalizes external_directory workdir variants on Windows", async () => { - const err = new Error("stop after permission") - await using outerTmp = await tmpdir() - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const bash = await initBash() - const want = Filesystem.normalizePathPattern(path.join(outerTmp.path, "*")) - - for (const dir of forms(outerTmp.path)) { - const requests: Array> = [] - await expect( - Effect.runPromise( - bash.execute( + it.live("normalizes external_directory workdir variants on Windows", () => + Effect.gen(function* () { + const err = new Error("stop after permission") + const outerTmp = yield* tmpdirScoped() + const tmp = yield* tmpdirScoped() + yield* runIn( + tmp, + Effect.gen(function* () { + const want = Filesystem.normalizePathPattern(path.join(outerTmp, "*")) + + for (const dir of forms(outerTmp)) { + const requests: Array> = [] + expect( + yield* fail( { command: "echo ok", workdir: dir, @@ -840,243 +817,227 @@ describe("tool.shell permissions", () => { }, capture(requests, err), ), - ), - ).rejects.toThrow(err.message) + ).toMatchObject({ message: err.message }) - const extDirReq = requests.find((r) => r.permission === "external_directory") - expect({ dir, patterns: extDirReq?.patterns, always: extDirReq?.always }).toEqual({ - dir, - patterns: [want], - always: [want], - }) - } - }, - }) - }) + const extDirReq = requests.find((r) => r.permission === "external_directory") + expect({ dir, patterns: extDirReq?.patterns, always: extDirReq?.always }).toEqual({ + dir, + patterns: [want], + always: [want], + }) + } + }), + ) + }), + ) if (bash) { - test( - "uses Git Bash /tmp semantics for external workdir", - withShell({ label: "bash", shell: bash }, async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initBash() + it.live("uses Git Bash /tmp semantics for external workdir", () => + withShell( + { label: "bash", shell: bash }, + runIn( + projectRoot, + Effect.gen(function* () { const err = new Error("stop after permission") const requests: Array> = [] const want = glob(path.join(os.tmpdir(), "*")) - await expect( - Effect.runPromise( - bash.execute( - { - command: "echo ok", - workdir: "/tmp", - description: "Echo from Git Bash tmp", - }, - capture(requests, err), - ), + expect( + yield* fail( + { + command: "echo ok", + workdir: "/tmp", + description: "Echo from Git Bash tmp", + }, + capture(requests, err), ), - ).rejects.toThrow(err.message) + ).toMatchObject({ message: err.message }) expect(requests[0]).toMatchObject({ permission: "external_directory", patterns: [want], always: [want], }) - }, - }) - }), + }), + ), + ), ) - test( - "uses Git Bash /tmp semantics for external file paths", - withShell({ label: "bash", shell: bash }, async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initBash() + it.live("uses Git Bash /tmp semantics for external file paths", () => + withShell( + { label: "bash", shell: bash }, + runIn( + projectRoot, + Effect.gen(function* () { const err = new Error("stop after permission") const requests: Array> = [] const want = glob(path.join(os.tmpdir(), "*")) - await expect( - Effect.runPromise( - bash.execute( - { - command: "cat /tmp/opencode-does-not-exist", - description: "Read Git Bash tmp file", - }, - capture(requests, err), - ), + expect( + yield* fail( + { + command: "cat /tmp/opencode-does-not-exist", + description: "Read Git Bash tmp file", + }, + capture(requests, err), ), - ).rejects.toThrow(err.message) + ).toMatchObject({ message: err.message }) expect(requests[0]).toMatchObject({ permission: "external_directory", patterns: [want], always: [want], }) - }, - }) - }), + }), + ), + ), ) } } - each("asks for external_directory permission when file arg is outside project", async () => { - await using outerTmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "outside.txt"), "x") - }, - }) - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const bash = await initBash() - const err = new Error("stop after permission") - const requests: Array> = [] - const filepath = path.join(outerTmp.path, "outside.txt") - await expect( - Effect.runPromise( - bash.execute( + each("asks for external_directory permission when file arg is outside project", () => + Effect.gen(function* () { + const outerTmp = yield* tmpdirScoped() + yield* Effect.promise(() => Bun.write(path.join(outerTmp, "outside.txt"), "x")) + const tmp = yield* tmpdirScoped() + yield* runIn( + tmp, + Effect.gen(function* () { + const err = new Error("stop after permission") + const requests: Array> = [] + const filepath = path.join(outerTmp, "outside.txt") + expect( + yield* fail( { command: `cat ${filepath}`, description: "Read external file", }, capture(requests, err), ), - ), - ).rejects.toThrow(err.message) - const extDirReq = requests.find((r) => r.permission === "external_directory") - const expected = glob(path.join(outerTmp.path, "*")) - expect(extDirReq).toBeDefined() - expect(extDirReq!.patterns).toContain(expected) - expect(extDirReq!.always).toContain(expected) - }, - }) - }) - - each("does not ask for external_directory permission when rm inside project", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "tmpfile"), "x") - }, - }) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const bash = await initBash() - const requests: Array> = [] - await Effect.runPromise( - bash.execute( + ).toMatchObject({ message: err.message }) + const extDirReq = requests.find((r) => r.permission === "external_directory") + const expected = glob(path.join(outerTmp, "*")) + expect(extDirReq).toBeDefined() + expect(extDirReq!.patterns).toContain(expected) + expect(extDirReq!.always).toContain(expected) + }), + ) + }), + ) + + each("does not ask for external_directory permission when rm inside project", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped() + yield* Effect.promise(() => Bun.write(path.join(tmp, "tmpfile"), "x")) + yield* runIn( + tmp, + Effect.gen(function* () { + const requests: Array> = [] + yield* run( { - command: `rm -rf ${path.join(tmp.path, "nested")}`, + command: `rm -rf ${path.join(tmp, "nested")}`, description: "Remove nested dir", }, capture(requests), - ), - ) - const extDirReq = requests.find((r) => r.permission === "external_directory") - expect(extDirReq).toBeUndefined() - }, - }) - }) - - each("includes always patterns for auto-approval", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const bash = await initBash() - const requests: Array> = [] - await Effect.runPromise( - bash.execute( + ) + const extDirReq = requests.find((r) => r.permission === "external_directory") + expect(extDirReq).toBeUndefined() + }), + ) + }), + ) + + each("includes always patterns for auto-approval", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped() + yield* runIn( + tmp, + Effect.gen(function* () { + const requests: Array> = [] + yield* run( { command: "git log --oneline -5", description: "Git log", }, capture(requests), - ), - ) - expect(requests.length).toBe(1) - expect(requests[0].always.length).toBeGreaterThan(0) - expect(requests[0].always.some((item) => item.endsWith("*"))).toBe(true) - }, - }) - }) - - each("does not ask for bash permission when command is cd only", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const bash = await initShell() - const requests: Array> = [] - await Effect.runPromise( - bash.execute( + ) + expect(requests.length).toBe(1) + expect(requests[0].always.length).toBeGreaterThan(0) + expect(requests[0].always.some((item) => item.endsWith("*"))).toBe(true) + }), + ) + }), + ) + + each("does not ask for bash permission when command is cd only", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped() + yield* runIn( + tmp, + Effect.gen(function* () { + const requests: Array> = [] + yield* run( { command: "cd .", description: "Stay in current directory", }, capture(requests), - ), - ) - const bashReq = requests.find((r) => r.permission === "bash") - expect(bashReq).toBeUndefined() - }, - }) - }) - - each("matches redirects in permission pattern", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const bash = await initShell() - const err = new Error("stop after permission") - const requests: Array> = [] - await expect( - Effect.runPromise( - bash.execute( + ) + const bashReq = requests.find((r) => r.permission === "bash") + expect(bashReq).toBeUndefined() + }), + ) + }), + ) + + each("matches redirects in permission pattern", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped() + yield* runIn( + tmp, + Effect.gen(function* () { + const err = new Error("stop after permission") + const requests: Array> = [] + expect( + yield* fail( { command: "echo test > output.txt", description: "Redirect test output" }, capture(requests, err), ), - ), - ).rejects.toThrow(err.message) - const bashReq = requests.find((r) => r.permission === "bash") - expect(bashReq).toBeDefined() - expect(bashReq!.patterns).toContain("echo test > output.txt") - }, - }) - }) - - each("always pattern has space before wildcard to not include different commands", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const bash = await initBash() - const requests: Array> = [] - await Effect.runPromise(bash.execute({ command: "ls -la", description: "List" }, capture(requests))) - const bashReq = requests.find((r) => r.permission === "bash") - expect(bashReq).toBeDefined() - // kilocode_change start — arity prefix produces "ls *" with space before wildcard - expect(bashReq!.always).toContain("ls *") - expect(bashReq!.patterns).toContain("ls -la") - // kilocode_change end - }, - }) - }) + ).toMatchObject({ message: err.message }) + const bashReq = requests.find((r) => r.permission === "bash") + expect(bashReq).toBeDefined() + expect(bashReq!.patterns).toContain("echo test > output.txt") + }), + ) + }), + ) + + each("always pattern has space before wildcard to not include different commands", () => + Effect.gen(function* () { + const tmp = yield* tmpdirScoped() + yield* runIn( + tmp, + Effect.gen(function* () { + const requests: Array> = [] + yield* run({ command: "ls -la", description: "List" }, capture(requests)) + const bashReq = requests.find((r) => r.permission === "bash") + expect(bashReq).toBeDefined() + // kilocode_change start — arity prefix produces "ls *" with space before wildcard + expect(bashReq!.always).toContain("ls *") + expect(bashReq!.patterns).toContain("ls -la") + // kilocode_change end + }), + ) + }), + ) }) describe("tool.shell abort", () => { - test("preserves output when aborted", async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initShell() - const controller = new AbortController() - const collected: string[] = [] - const res = await Effect.runPromise( - bash.execute( + it.live( + "preserves output when aborted", + () => + runIn( + projectRoot, + Effect.gen(function* () { + const controller = new AbortController() + const collected: string[] = [] + const res = yield* run( { command: `echo before && sleep 30`, description: "Long running command", @@ -1093,198 +1054,175 @@ describe("tool.shell abort", () => { } }), }, - ), - ) - expect(res.output).toContain("before") - expect(res.output).toContain("User aborted the command") - expect(collected.length).toBeGreaterThan(0) - }, - }) - }, 15_000) - - test("terminates command on timeout", async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initShell() - const result = await Effect.runPromise( - bash.execute( - { - command: `echo started && sleep 60`, - description: "Timeout test", - timeout: 500, - }, - ctx, - ), - ) - expect(result.output).toContain("started") - expect(result.output).toContain("shell tool terminated command after exceeding timeout") - expect(result.output).toContain("retry with a larger timeout value in milliseconds") - }, - }) - }, 15_000) - - test.skipIf(process.platform === "win32")("captures stderr in output", async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initShell() - const result = await Effect.runPromise( - bash.execute( - { - command: `echo stdout_msg && echo stderr_msg >&2`, - description: "Stderr test", - }, - ctx, - ), - ) - expect(result.output).toContain("stdout_msg") - expect(result.output).toContain("stderr_msg") - expect(result.metadata.exit).toBe(0) - }, - }) - }) - - test("returns non-zero exit code", async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initShell() - const result = await Effect.runPromise( - bash.execute( - { - command: `exit 42`, - description: "Non-zero exit", - }, - ctx, - ), - ) + ) + expect(res.output).toContain("before") + expect(res.output).toContain("User aborted the command") + expect(collected.length).toBeGreaterThan(0) + }), + ), + 15_000, + ) + + it.live( + "terminates command on timeout", + () => + runIn( + projectRoot, + Effect.gen(function* () { + const result = yield* run({ + command: `echo started && sleep 60`, + description: "Timeout test", + timeout: 500, + }) + expect(result.output).toContain("started") + expect(result.output).toContain("shell tool terminated command after exceeding timeout") + expect(result.output).toContain("retry with a larger timeout value in milliseconds") + }), + ), + 15_000, + ) + + it.live( + "uses RuntimeFlags bashDefaultTimeoutMs when timeout is omitted", + () => + runIn( + projectRoot, + Effect.gen(function* () { + const result = yield* run({ + command: `echo started && sleep 60`, + description: "Default timeout test", + }) + expect(result.output).toContain("started") + expect(result.output).toContain("exceeding timeout 500 ms") + }), + ).pipe(Effect.provide(RuntimeFlags.layer({ bashDefaultTimeoutMs: 500 }))), + 15_000, + ) + + if (process.platform !== "win32") { + it.live("captures stderr in output", () => + runIn( + projectRoot, + Effect.gen(function* () { + const result = yield* run({ + command: `echo stdout_msg && echo stderr_msg >&2`, + description: "Stderr test", + }) + expect(result.output).toContain("stdout_msg") + expect(result.output).toContain("stderr_msg") + expect(result.metadata.exit).toBe(0) + }), + ), + ) + } + + it.live("returns non-zero exit code", () => + runIn( + projectRoot, + Effect.gen(function* () { + const result = yield* run({ + command: `exit 42`, + description: "Non-zero exit", + }) expect(result.metadata.exit).toBe(42) - }, - }) - }) - - test("streams metadata updates progressively", async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initBash() + }), + ), + ) + + it.live("streams metadata updates progressively", () => + runIn( + projectRoot, + Effect.gen(function* () { const updates: string[] = [] - const result = await Effect.runPromise( - bash.execute( - { - command: `echo first && sleep 0.1 && echo second`, - description: "Streaming test", - }, - { - ...ctx, - metadata: (input) => - Effect.sync(() => { - const output = (input.metadata as { output?: string })?.output - if (output) updates.push(output) - }), - }, - ), + const result = yield* run( + { + command: `echo first && sleep 0.1 && echo second`, + description: "Streaming test", + }, + { + ...ctx, + metadata: (input) => + Effect.sync(() => { + const output = (input.metadata as { output?: string })?.output + if (output) updates.push(output) + }), + }, ) expect(result.output).toContain("first") expect(result.output).toContain("second") expect(updates.length).toBeGreaterThan(1) - }, - }) - }) + }), + ), + ) }) describe("tool.shell truncation", () => { - test("truncates output exceeding line limit", async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initShell() + it.live("truncates output exceeding line limit", () => + runIn( + projectRoot, + Effect.gen(function* () { const lineCount = Truncate.MAX_LINES + 500 - const result = await Effect.runPromise( - bash.execute( - { - command: fill("lines", lineCount), - description: "Generate lines exceeding limit", - }, - ctx, - ), - ) + const result = yield* run({ + command: fill("lines", lineCount), + description: "Generate lines exceeding limit", + }) mustTruncate(result) expect(result.output).toMatch(/\.\.\.output truncated\.\.\./) expect(result.output).toMatch(/Full output saved to:\s+\S+/) - }, - }) - }) - - test("truncates output exceeding byte limit", async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initShell() + }), + ), + ) + + it.live("truncates output exceeding byte limit", () => + runIn( + projectRoot, + Effect.gen(function* () { const byteCount = Truncate.MAX_BYTES + 10000 - const result = await Effect.runPromise( - bash.execute( - { - command: fill("bytes", byteCount), - description: "Generate bytes exceeding limit", - }, - ctx, - ), - ) + const result = yield* run({ + command: fill("bytes", byteCount), + description: "Generate bytes exceeding limit", + }) mustTruncate(result) expect(result.output).toMatch(/\.\.\.output truncated\.\.\./) expect(result.output).toMatch(/Full output saved to:\s+\S+/) - }, - }) - }) - - test("does not truncate small output", async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initShell() - const result = await Effect.runPromise( - bash.execute( - { - command: "echo hello", - description: "Echo hello", - }, - ctx, - ), - ) + }), + ), + ) + + it.live("does not truncate small output", () => + runIn( + projectRoot, + Effect.gen(function* () { + const result = yield* run({ + command: fill("lines", 1), + description: "Generate one line", + }) expect((result.metadata as { truncated?: boolean }).truncated).toBe(false) - expect(result.output).toContain("hello") - }, - }) - }) - - test("full output is saved to file when truncated", async () => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const bash = await initShell() + expect(result.output).toContain("1") + }), + ), + ) + + it.live("full output is saved to file when truncated", () => + runIn( + projectRoot, + Effect.gen(function* () { const lineCount = Truncate.MAX_LINES + 100 - const result = await Effect.runPromise( - bash.execute( - { - command: fill("lines", lineCount), - description: "Generate lines for file check", - }, - ctx, - ), - ) + const result = yield* run({ + command: fill("lines", lineCount), + description: "Generate lines for file check", + }) mustTruncate(result) const filepath = (result.metadata as { outputPath?: string }).outputPath expect(filepath).toBeTruthy() - const saved = await Filesystem.readText(filepath!) + const saved = yield* (yield* AppFileSystem.Service).readFileString(filepath!) const lines = saved.trim().split(/\r?\n/) expect(lines.length).toBe(lineCount) expect(lines[0]).toBe("1") expect(lines[lineCount - 1]).toBe(String(lineCount)) - }, - }) - }) + }), + ), + ) }) diff --git a/packages/opencode/test/tool/task.test.ts b/packages/opencode/test/tool/task.test.ts index df991a56af9..32e9d18d623 100644 --- a/packages/opencode/test/tool/task.test.ts +++ b/packages/opencode/test/tool/task.test.ts @@ -1,17 +1,23 @@ import { afterEach, describe, expect } from "bun:test" import { Effect, Exit, Fiber, Layer } from "effect" import { Agent } from "../../src/agent/agent" +import { BackgroundJob } from "@/background/job" +import { Bus } from "@/bus" import { Config } from "@/config/config" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { Session } from "@/session/session" import { MessageV2 } from "../../src/session/message-v2" import type { SessionPrompt } from "../../src/session/prompt" import { MessageID, PartID, SessionID } from "../../src/session/schema" // kilocode_change - SessionID used by cost propagation tests +import { SessionRunState } from "@/session/run-state" +import { SessionStatus } from "@/session/status" import { ModelID, ProviderID } from "../../src/provider/schema" import { Provider } from "../../src/provider/provider" // kilocode_change +import { KiloSession } from "../../src/kilocode/session" // kilocode_change import { TaskTool, type TaskPromptOps } from "../../src/tool/task" import { Truncate } from "@/tool/truncate" import { ToolRegistry } from "@/tool/registry" +import { RuntimeFlags } from "@/effect/runtime-flags" import { disposeAllInstances, provideTmpdirInstance } from "../fixture/fixture" import { testEffect } from "../lib/effect" @@ -24,17 +30,24 @@ const ref = { modelID: ModelID.make("test-model"), } -const it = testEffect( +const layer = (flags: Partial = {}) => Layer.mergeAll( Agent.defaultLayer, + BackgroundJob.defaultLayer, + Bus.defaultLayer, Config.defaultLayer, CrossSpawnSpawner.defaultLayer, Session.defaultLayer, + SessionRunState.defaultLayer, + SessionStatus.defaultLayer, Truncate.defaultLayer, Provider.defaultLayer, // kilocode_change ToolRegistry.defaultLayer, - ), -) + RuntimeFlags.layer(flags), + ) + +const it = testEffect(layer()) +const background = testEffect(layer({ experimentalBackgroundSubagents: true })) function defer() { let resolve!: (value: T | PromiseLike) => void @@ -92,6 +105,7 @@ function stubOps(opts?: { } return rep }), + loop: (input) => Effect.succeed(reply({ sessionID: input.sessionID, parts: [] }, opts?.text ?? "done")), } } // kilocode_change end @@ -242,6 +256,42 @@ describe("tool.task", () => { }), ) + // kilocode_change start - resumed children rebuild parent platform attribution after restart + it.instance("execute preserves platform attribution when resuming a task", () => + Effect.gen(function* () { + const sessions = yield* Session.Service + const { chat, assistant } = yield* seed() + KiloSession.setPlatformOverride(chat.id, "agent-manager") + const child = yield* sessions.create({ parentID: chat.id, title: "Existing child" }) + KiloSession.clearPlatformOverride(child.id) + const tool = yield* TaskTool + const def = yield* tool.init() + + yield* def.execute( + { + description: "inspect bug", + prompt: "continue", + subagent_type: "general", + task_id: child.id, + }, + { + sessionID: chat.id, + messageID: assistant.id, + agent: "build", + abort: new AbortController().signal, + extra: { promptOps: stubOps() }, + messages: [], + metadata: () => Effect.void, + ask: () => Effect.void, + }, + ) + + expect(KiloSession.resolvePlatform(child.id)).toBe("agent-manager") + expect(KiloSession.resolveRoot(child.id)).toBe(chat.id) + }), + ) + // kilocode_change end + it.instance("execute asks by default and skips checks when bypassed", () => Effect.gen(function* () { const { chat, assistant } = yield* seed() @@ -307,6 +357,7 @@ describe("tool.task", () => { ready.resolve(input) return cancelled.promise }).pipe(Effect.as(reply(input, "cancelled"))), + loop: (input) => Effect.succeed(reply({ sessionID: input.sessionID, parts: [] }, "done")), } const fiber = yield* def @@ -478,6 +529,7 @@ describe("tool.task", () => { }, } }), + loop: (input) => Effect.succeed(reply({ sessionID: input.sessionID, parts: [] }, "done")), } const exit = yield* def @@ -504,6 +556,347 @@ describe("tool.task", () => { }), ) // kilocode_change end + it.instance("rejects background execution when the experiment is disabled", () => + Effect.gen(function* () { + const { chat, assistant } = yield* seed() + const tool = yield* TaskTool + const def = yield* tool.init() + + const exit = yield* def + .execute( + { + description: "inspect bug", + prompt: "look into the cache key path", + subagent_type: "general", + background: true, + }, + { + sessionID: chat.id, + messageID: assistant.id, + agent: "build", + abort: new AbortController().signal, + extra: { promptOps: stubOps() }, + messages: [], + metadata: () => Effect.void, + ask: () => Effect.void, + }, + ) + .pipe(Effect.exit) + + expect(Exit.isFailure(exit)).toBe(true) + }), + ) + + background.instance("execute launches background tasks without waiting for completion", () => + Effect.gen(function* () { + const jobs = yield* BackgroundJob.Service + const { chat, assistant } = yield* seed() + const tool = yield* TaskTool + const def = yield* tool.init() + + const result = yield* def.execute( + { + description: "inspect bug", + prompt: "look into the cache key path", + subagent_type: "general", + background: true, + }, + { + sessionID: chat.id, + messageID: assistant.id, + agent: "build", + abort: new AbortController().signal, + extra: { + promptOps: { + ...stubOps(), + prompt: () => Effect.never, + } satisfies TaskPromptOps, + }, + messages: [], + metadata: () => Effect.void, + ask: () => Effect.void, + }, + ) + + const job = yield* jobs.get(result.metadata.sessionId) + expect(result.metadata.background).toBe(true) + expect(result.output).toContain("state: running") + expect(job?.status).toBe("running") + }), + ) + + // kilocode_change start - completed background tasks propagate their invocation cost delta + background.instance("background tasks propagate child cost to the parent", () => + Effect.gen(function* () { + const jobs = yield* BackgroundJob.Service + const sessions = yield* Session.Service + const { chat, assistant } = yield* seed() + const tool = yield* TaskTool + const def = yield* tool.init() + + const result = yield* def.execute( + { + description: "inspect bug", + prompt: "look into the cache key path", + subagent_type: "general", + background: true, + }, + { + sessionID: chat.id, + messageID: assistant.id, + agent: "build", + abort: new AbortController().signal, + extra: { promptOps: stubOps({ sessions, childCost: 0.2 }) }, + messages: [], + metadata: () => Effect.void, + ask: () => Effect.void, + }, + ) + + yield* jobs.wait({ id: result.metadata.sessionId, timeout: 1_000 }) + const parent = yield* MessageV2.get({ sessionID: chat.id, messageID: assistant.id }) + expect(parent.info.role === "assistant" ? parent.info.cost : 0).toBeCloseTo(0.2, 6) + }), + ) + // kilocode_change end + + background.instance("background tasks complete through the background job service", () => + Effect.gen(function* () { + const jobs = yield* BackgroundJob.Service + const { chat, assistant } = yield* seed() + const tool = yield* TaskTool + const def = yield* tool.init() + + const result = yield* def.execute( + { + description: "inspect bug", + prompt: "look into the cache key path", + subagent_type: "general", + background: true, + }, + { + sessionID: chat.id, + messageID: assistant.id, + agent: "build", + abort: new AbortController().signal, + extra: { promptOps: stubOps({ text: "background done" }) }, + messages: [], + metadata: () => Effect.void, + ask: () => Effect.void, + }, + ) + + const waited = yield* jobs.wait({ id: result.metadata.sessionId, timeout: 1_000 }) + expect(waited.timedOut).toBe(false) + expect(waited.info?.status).toBe("completed") + expect(waited.info?.output).toBe("background done") + }), + ) + + background.instance("background task completion does not wait for the parent resume loop", () => + Effect.gen(function* () { + const jobs = yield* BackgroundJob.Service + const sessions = yield* Session.Service + const { chat, assistant } = yield* seed() + const tool = yield* TaskTool + const def = yield* tool.init() + + const result = yield* def.execute( + { + description: "inspect bug", + prompt: "look into the cache key path", + subagent_type: "general", + background: true, + }, + { + sessionID: chat.id, + messageID: assistant.id, + agent: "build", + abort: new AbortController().signal, + extra: { + promptOps: { + ...stubOps({ text: "background done" }), + prompt: (input) => + input.noReply + ? Effect.gen(function* () { + const user = yield* sessions.updateMessage({ + id: input.messageID ?? MessageID.ascending(), + role: "user", + sessionID: input.sessionID, + agent: input.agent ?? "build", + model: input.model ?? ref, + time: { created: Date.now() }, + }) + const parts = input.parts.map((part) => ({ + ...part, + id: part.id ?? PartID.ascending(), + messageID: user.id, + sessionID: input.sessionID, + })) + yield* Effect.forEach(parts, (part) => sessions.updatePart(part), { discard: true }) + return { info: user, parts } + }) + : Effect.succeed(reply(input, "background done")), + loop: () => Effect.never, + } satisfies TaskPromptOps, + }, + messages: [], + metadata: () => Effect.void, + ask: () => Effect.void, + }, + ) + + const waited = yield* jobs.wait({ id: result.metadata.sessionId, timeout: 1_000 }) + expect(waited.timedOut).toBe(false) + expect(waited.info?.status).toBe("completed") + }), + ) + + background.instance("removing the parent session cancels running background tasks", () => + Effect.gen(function* () { + const jobs = yield* BackgroundJob.Service + const sessions = yield* Session.Service + const { chat, assistant } = yield* seed() + const tool = yield* TaskTool + const def = yield* tool.init() + + const result = yield* def.execute( + { + description: "inspect bug", + prompt: "look into the cache key path", + subagent_type: "general", + background: true, + }, + { + sessionID: chat.id, + messageID: assistant.id, + agent: "build", + abort: new AbortController().signal, + extra: { + promptOps: { + ...stubOps(), + prompt: () => Effect.never, + } satisfies TaskPromptOps, + }, + messages: [], + metadata: () => Effect.void, + ask: () => Effect.void, + }, + ) + + yield* sessions.remove(chat.id) + const waited = yield* jobs.wait({ id: result.metadata.sessionId, timeout: 1_000 }) + expect(waited.timedOut).toBe(false) + expect(waited.info?.status).toBe("cancelled") + }), + ) + + background.instance("removing the child task session cancels its running background task", () => + Effect.gen(function* () { + const jobs = yield* BackgroundJob.Service + const sessions = yield* Session.Service + const { chat, assistant } = yield* seed() + const tool = yield* TaskTool + const def = yield* tool.init() + + const result = yield* def.execute( + { + description: "inspect bug", + prompt: "look into the cache key path", + subagent_type: "general", + background: true, + }, + { + sessionID: chat.id, + messageID: assistant.id, + agent: "build", + abort: new AbortController().signal, + extra: { + promptOps: { + ...stubOps(), + prompt: () => Effect.never, + } satisfies TaskPromptOps, + }, + messages: [], + metadata: () => Effect.void, + ask: () => Effect.void, + }, + ) + + yield* sessions.remove(result.metadata.sessionId) + const waited = yield* jobs.wait({ id: result.metadata.sessionId, timeout: 1_000 }) + expect(waited.timedOut).toBe(false) + expect(waited.info?.status).toBe("cancelled") + }), + ) + + background.instance("cancelling the parent run cancels running background tasks", () => + Effect.gen(function* () { + const jobs = yield* BackgroundJob.Service + const runState = yield* SessionRunState.Service + const { chat, assistant } = yield* seed() + const tool = yield* TaskTool + const def = yield* tool.init() + + const result = yield* def.execute( + { + description: "inspect bug", + prompt: "look into the cache key path", + subagent_type: "general", + background: true, + }, + { + sessionID: chat.id, + messageID: assistant.id, + agent: "build", + abort: new AbortController().signal, + extra: { + promptOps: { + ...stubOps(), + prompt: () => Effect.never, + } satisfies TaskPromptOps, + }, + messages: [], + metadata: () => Effect.void, + ask: () => Effect.void, + }, + ) + + yield* runState.cancel(chat.id) + const waited = yield* jobs.wait({ id: result.metadata.sessionId, timeout: 1_000 }) + expect(waited.timedOut).toBe(false) + expect(waited.info?.status).toBe("cancelled") + }), + ) + + it.instance("cancelling a parent run recursively cancels descendant background tasks", () => + Effect.gen(function* () { + const jobs = yield* BackgroundJob.Service + const runState = yield* SessionRunState.Service + const sessions = yield* Session.Service + const { chat } = yield* seed() + const child = yield* sessions.create({ parentID: chat.id, title: "child" }) + const grandchild = yield* sessions.create({ parentID: child.id, title: "grandchild" }) + + yield* jobs.start({ + id: child.id, + type: "task", + metadata: { parentSessionId: chat.id, sessionId: child.id }, + run: Effect.never, + }) + yield* jobs.start({ + id: grandchild.id, + type: "task", + metadata: { parentSessionId: child.id, sessionId: grandchild.id }, + run: Effect.never, + }) + + yield* runState.cancel(chat.id) + + expect((yield* jobs.get(child.id))?.status).toBe("cancelled") + expect((yield* jobs.get(grandchild.id))?.status).toBe("cancelled") + }), + ) }) // kilocode_change start - subagent cost propagation coverage (#6321) @@ -540,7 +933,7 @@ describe("tool.task cost propagation", () => { ask: () => Effect.void, }, ) - const parent = yield* Effect.sync(() => MessageV2.get({ sessionID: chat.id, messageID: assistant.id })) + const parent = yield* MessageV2.get({ sessionID: chat.id, messageID: assistant.id }) expect(parent.info.role).toBe("assistant") if (parent.info.role !== "assistant") return expect(parent.info.cost).toBeCloseTo(0.25, 6) @@ -593,7 +986,7 @@ describe("tool.task cost propagation", () => { ask: () => Effect.void, }, ) - const parent = yield* Effect.sync(() => MessageV2.get({ sessionID: chat.id, messageID: assistant.id })) + const parent = yield* MessageV2.get({ sessionID: chat.id, messageID: assistant.id }) if (parent.info.role !== "assistant") return // Only the delta since the start of this invocation propagates. expect(parent.info.cost).toBeCloseTo(0.15, 6) @@ -646,7 +1039,7 @@ describe("tool.task cost propagation", () => { ask: () => Effect.void, }, ) - const parent = yield* Effect.sync(() => MessageV2.get({ sessionID: chat.id, messageID: assistant.id })) + const parent = yield* MessageV2.get({ sessionID: chat.id, messageID: assistant.id }) if (parent.info.role !== "assistant") return // Delta-only: only the 0.05 from this run, not 0.15 including the pre-existing 0.10. expect(parent.info.cost).toBeCloseTo(0.05, 6) @@ -686,6 +1079,7 @@ describe("tool.task cost propagation", () => { abort.abort() return yield* Effect.interrupt }), + loop: (input) => Effect.succeed(reply({ sessionID: input.sessionID, parts: [] }, "done")), } yield* def @@ -708,7 +1102,7 @@ describe("tool.task cost propagation", () => { ) .pipe(Effect.exit) - const parent = yield* Effect.sync(() => MessageV2.get({ sessionID: chat.id, messageID: assistant.id })) + const parent = yield* MessageV2.get({ sessionID: chat.id, messageID: assistant.id }) if (parent.info.role !== "assistant") return expect(parent.info.cost).toBeCloseTo(0.07, 6) }), diff --git a/packages/opencode/test/tool/task_status.test.ts b/packages/opencode/test/tool/task_status.test.ts new file mode 100644 index 00000000000..5a46c0b96da --- /dev/null +++ b/packages/opencode/test/tool/task_status.test.ts @@ -0,0 +1,124 @@ +import { afterEach, describe, expect } from "bun:test" +import { Effect, Layer } from "effect" +import { Agent } from "@/agent/agent" +import { BackgroundJob } from "@/background/job" +import { Bus } from "@/bus" +import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" +import { Session } from "@/session/session" +import { MessageID } from "@/session/schema" +import { SessionStatus } from "@/session/status" +import { TaskStatusTool } from "@/tool/task_status" +import { Truncate } from "@/tool/truncate" +import { RuntimeFlags } from "@/effect/runtime-flags" +import { disposeAllInstances } from "../fixture/fixture" +import { testEffect } from "../lib/effect" + +afterEach(async () => { + await disposeAllInstances() +}) + +const layer = (flags: Partial = {}) => + Layer.mergeAll( + Agent.defaultLayer, + BackgroundJob.defaultLayer, + Bus.defaultLayer, + CrossSpawnSpawner.defaultLayer, + Session.defaultLayer, + SessionStatus.defaultLayer, + Truncate.defaultLayer, + RuntimeFlags.layer(flags), + ) + +const it = testEffect(layer({ experimentalBackgroundSubagents: true })) + +describe("tool.task_status", () => { + it.instance("returns completed background job output", () => + Effect.gen(function* () { + const jobs = yield* BackgroundJob.Service + const sessions = yield* Session.Service + const tool = yield* TaskStatusTool + const def = yield* tool.init() + const chat = yield* sessions.create({}) + const task = yield* sessions.create({ parentID: chat.id }) + + yield* jobs.start({ id: task.id, type: "task", run: Effect.succeed("all done") }) + + const result = yield* def.execute( + { task_id: task.id, wait: true, timeout_ms: 1_000 }, + { + sessionID: chat.id, + messageID: MessageID.ascending(), + agent: "build", + abort: new AbortController().signal, + messages: [], + metadata: () => Effect.void, + ask: () => Effect.void, + }, + ) + + expect(result.output).toContain("state: completed") + expect(result.output).toContain("all done") + expect(result.metadata.timed_out).toBe(false) + }), + ) + + it.instance("wait=true times out while the background job is running", () => + Effect.gen(function* () { + const jobs = yield* BackgroundJob.Service + const sessions = yield* Session.Service + const tool = yield* TaskStatusTool + const def = yield* tool.init() + const chat = yield* sessions.create({}) + const task = yield* sessions.create({ parentID: chat.id }) + + yield* jobs.start({ id: task.id, type: "task", run: Effect.never }) + + const result = yield* def.execute( + { task_id: task.id, wait: true, timeout_ms: 50 }, + { + sessionID: chat.id, + messageID: MessageID.ascending(), + agent: "build", + abort: new AbortController().signal, + messages: [], + metadata: () => Effect.void, + ask: () => Effect.void, + }, + ) + + expect(result.output).toContain("state: running") + expect(result.output).toContain("Timed out after 50ms") + expect(result.metadata.timed_out).toBe(true) + }), + ) + + // kilocode_change start - task status cannot inspect another session's child + it.instance("rejects cross-session task inspection", () => + Effect.gen(function* () { + const sessions = yield* Session.Service + const tool = yield* TaskStatusTool + const def = yield* tool.init() + const owner = yield* sessions.create({}) + const other = yield* sessions.create({}) + const task = yield* sessions.create({ parentID: owner.id }) + + const exit = yield* def + .execute( + { task_id: task.id }, + { + sessionID: other.id, + messageID: MessageID.ascending(), + agent: "build", + abort: new AbortController().signal, + messages: [], + metadata: () => Effect.void, + ask: () => Effect.void, + }, + ) + .pipe(Effect.exit) + + expect(exit._tag).toBe("Failure") + }), + ) + // kilocode_change end +}) diff --git a/packages/opencode/test/tool/tool-define.test.ts b/packages/opencode/test/tool/tool-define.test.ts index a291b9f7f97..ca351cca486 100644 --- a/packages/opencode/test/tool/tool-define.test.ts +++ b/packages/opencode/test/tool/tool-define.test.ts @@ -1,11 +1,12 @@ -import { describe, test, expect } from "bun:test" -import { Effect, Layer, ManagedRuntime, Schema } from "effect" +import { describe, expect } from "bun:test" +import { Effect, Layer, Schema } from "effect" import { Agent } from "../../src/agent/agent" import { MessageID, SessionID } from "../../src/session/schema" import { Tool } from "@/tool/tool" import { Truncate } from "@/tool/truncate" +import { testEffect } from "../lib/effect" -const runtime = ManagedRuntime.make(Layer.mergeAll(Truncate.defaultLayer, Agent.defaultLayer)) +const it = testEffect(Layer.mergeAll(Truncate.defaultLayer, Agent.defaultLayer)) const params = Schema.Struct({ input: Schema.String }) @@ -21,49 +22,53 @@ function makeTool(id: string, executeFn?: () => void) { } describe("Tool.define", () => { - test("object-defined tool does not mutate the original init object", async () => { - const original = makeTool("test") - const originalExecute = original.execute + it.effect("object-defined tool does not mutate the original init object", () => + Effect.gen(function* () { + const original = makeTool("test") + const originalExecute = original.execute - const info = await runtime.runPromise(Tool.define("test-tool", Effect.succeed(original))) + const info = yield* Tool.define("test-tool", Effect.succeed(original)) - await Effect.runPromise(info.init()) - await Effect.runPromise(info.init()) - await Effect.runPromise(info.init()) + yield* info.init() + yield* info.init() + yield* info.init() - expect(original.execute).toBe(originalExecute) - }) + expect(original.execute).toBe(originalExecute) + }), + ) - test("effect-defined tool returns fresh objects and is unaffected", async () => { - const info = await runtime.runPromise( - Tool.define( + it.effect("effect-defined tool returns fresh objects and is unaffected", () => + Effect.gen(function* () { + const info = yield* Tool.define( "test-fn-tool", Effect.succeed(() => Effect.succeed(makeTool("test"))), - ), - ) + ) - const first = await Effect.runPromise(info.init()) - const second = await Effect.runPromise(info.init()) + const first = yield* info.init() + const second = yield* info.init() - expect(first).not.toBe(second) - }) + expect(first).not.toBe(second) + }), + ) - test("object-defined tool returns distinct objects per init() call", async () => { - const info = await runtime.runPromise(Tool.define("test-copy", Effect.succeed(makeTool("test")))) + it.effect("object-defined tool returns distinct objects per init() call", () => + Effect.gen(function* () { + const info = yield* Tool.define("test-copy", Effect.succeed(makeTool("test"))) - const first = await Effect.runPromise(info.init()) - const second = await Effect.runPromise(info.init()) + const first = yield* info.init() + const second = yield* info.init() - expect(first).not.toBe(second) - }) + expect(first).not.toBe(second) + }), + ) - test("execute receives decoded parameters", async () => { - const parameters = Schema.Struct({ - count: Schema.NumberFromString.pipe(Schema.optional, Schema.withDecodingDefaultType(Effect.succeed(5))), - }) - const calls: Array> = [] - const info = await runtime.runPromise( - Tool.define( + it.effect("execute receives decoded parameters", () => + Effect.gen(function* () { + const parameters = Schema.Struct({ + count: Schema.NumberFromString.pipe(Schema.optional, Schema.withDecodingDefaultType(Effect.succeed(5))), + }) + const calls: Array> = [] + const info = yield* Tool.define( "test-decoded", Effect.succeed({ description: "test tool", @@ -73,27 +78,27 @@ describe("Tool.define", () => { return Effect.succeed({ title: "test", output: "ok", metadata: { truncated: false } }) }, }), - ), - ) - const ctx: Tool.Context = { - sessionID: SessionID.descending(), - messageID: MessageID.ascending(), - agent: "build", - abort: new AbortController().signal, - messages: [], - metadata() { - return Effect.void - }, - ask() { - return Effect.void - }, - } - const tool = await Effect.runPromise(info.init()) - const execute = tool.execute as unknown as (args: unknown, ctx: Tool.Context) => ReturnType - - await Effect.runPromise(execute({}, ctx)) - await Effect.runPromise(execute({ count: "7" }, ctx)) - - expect(calls).toEqual([{ count: 5 }, { count: 7 }]) - }) + ) + const ctx: Tool.Context = { + sessionID: SessionID.descending(), + messageID: MessageID.ascending(), + agent: "build", + abort: new AbortController().signal, + messages: [], + metadata() { + return Effect.void + }, + ask() { + return Effect.void + }, + } + const tool = yield* info.init() + const execute = tool.execute as unknown as (args: unknown, ctx: Tool.Context) => ReturnType + + yield* execute({}, ctx) + yield* execute({ count: "7" }, ctx) + + expect(calls).toEqual([{ count: 5 }, { count: 7 }]) + }), + ) }) diff --git a/packages/opencode/test/tool/truncation.test.ts b/packages/opencode/test/tool/truncation.test.ts index e948a6dcb31..804bbd67266 100644 --- a/packages/opencode/test/tool/truncation.test.ts +++ b/packages/opencode/test/tool/truncation.test.ts @@ -1,11 +1,11 @@ import { describe, test, expect } from "bun:test" import { NodeFileSystem } from "@effect/platform-node" +import { AppFileSystem } from "@opencode-ai/core/filesystem" import { Effect, FileSystem, Layer } from "effect" import { Truncate } from "@/tool/truncate" import { Config } from "@/config/config" import { Identifier } from "../../src/id/id" import { Process } from "@/util/process" -import { Filesystem } from "@/util/filesystem" import path from "path" import { testEffect } from "../lib/effect" import { writeFileStringScoped } from "../lib/filesystem" @@ -14,10 +14,15 @@ import { TestConfig } from "../fixture/config" const FIXTURES_DIR = path.join(import.meta.dir, "fixtures") const ROOT = path.resolve(import.meta.dir, "..", "..") -const it = testEffect(Layer.mergeAll(Truncate.defaultLayer, NodeFileSystem.layer)) +const it = testEffect(Layer.mergeAll(Truncate.defaultLayer, NodeFileSystem.layer, AppFileSystem.defaultLayer)) const configuredLayer = (cfg: Config.Info) => - Layer.mergeAll(Truncate.defaultLayer, NodeFileSystem.layer, TestConfig.layer({ get: () => Effect.succeed(cfg) })) + Layer.mergeAll( + Truncate.defaultLayer, + NodeFileSystem.layer, + AppFileSystem.defaultLayer, + TestConfig.layer({ get: () => Effect.succeed(cfg) }), + ) const configuredIt = (cfg: Config.Info) => testEffect(configuredLayer(cfg)) describe("Truncate", () => { @@ -25,7 +30,8 @@ describe("Truncate", () => { it.live("truncates large json file by bytes", () => Effect.gen(function* () { const svc = yield* Truncate.Service - const content = yield* Effect.promise(() => Filesystem.readText(path.join(FIXTURES_DIR, "models-api.json"))) + const fsys = yield* AppFileSystem.Service + const content = yield* fsys.readFileString(path.join(FIXTURES_DIR, "models-api.json")) const result = yield* svc.output(content) expect(result.truncated).toBe(true) @@ -158,7 +164,8 @@ describe("Truncate", () => { it.live("large single-line file truncates with byte message", () => Effect.gen(function* () { const svc = yield* Truncate.Service - const content = yield* Effect.promise(() => Filesystem.readText(path.join(FIXTURES_DIR, "models-api.json"))) + const fsys = yield* AppFileSystem.Service + const content = yield* fsys.readFileString(path.join(FIXTURES_DIR, "models-api.json")) const result = yield* svc.output(content) expect(result.truncated).toBe(true) @@ -180,7 +187,8 @@ describe("Truncate", () => { expect(result.outputPath).toBeDefined() expect(result.outputPath).toContain("tool_") - const written = yield* Effect.promise(() => Filesystem.readText(result.outputPath!)) + const fsys = yield* AppFileSystem.Service + const written = yield* fsys.readFileString(result.outputPath!) expect(written).toBe(lines) }), ) diff --git a/packages/opencode/test/tool/webfetch.test.ts b/packages/opencode/test/tool/webfetch.test.ts index c6b46dc7c7c..fdf5210b9c1 100644 --- a/packages/opencode/test/tool/webfetch.test.ts +++ b/packages/opencode/test/tool/webfetch.test.ts @@ -1,15 +1,14 @@ -import { describe, expect, test } from "bun:test" -import path from "path" +import { describe, expect } from "bun:test" import { Effect, Layer } from "effect" import { FetchHttpClient } from "effect/unstable/http" import { Agent } from "../../src/agent/agent" import { Truncate } from "@/tool/truncate" -import { Instance } from "../../src/project/instance" -import { WithInstance } from "../../src/project/with-instance" import { WebFetchTool } from "../../src/tool/webfetch" import { SessionID, MessageID } from "../../src/session/schema" +import { Tool } from "@/tool/tool" +import { testEffect } from "../lib/effect" -const projectRoot = path.join(import.meta.dir, "../..") +const it = testEffect(Layer.mergeAll(FetchHttpClient.layer, Truncate.defaultLayer, Agent.defaultLayer)) const ctx = { sessionID: SessionID.make("ses_test"), @@ -22,30 +21,31 @@ const ctx = { ask: () => Effect.void, } -async function withFetch(fetch: (req: Request) => Response | Promise, fn: (url: URL) => Promise) { - using server = Bun.serve({ port: 0, fetch }) - await fn(server.url) -} - -function exec(args: { url: string; format: "text" | "markdown" | "html" }) { - return WebFetchTool.pipe( - Effect.flatMap((info) => info.init()), - Effect.flatMap((tool) => tool.execute(args, ctx)), - Effect.provide(Layer.mergeAll(FetchHttpClient.layer, Truncate.defaultLayer, Agent.defaultLayer)), - Effect.runPromise, +const withFetch = ( + fetch: (req: Request) => Response | Promise, + fn: (url: URL) => Effect.Effect, +) => + Effect.acquireUseRelease( + Effect.sync(() => Bun.serve({ port: 0, fetch })), + (server) => fn(server.url), + (server) => Effect.sync(() => server.stop(true)), ) -} + +const exec = Effect.fn("WebFetchToolTest.exec")(function* (args: Tool.InferParameters) { + const info = yield* WebFetchTool + const tool = yield* info.init() + return yield* tool.execute(args, ctx) +}) describe("tool.webfetch", () => { - test("returns image responses as file attachments", async () => { - const bytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]) - await withFetch( - () => new Response(bytes, { status: 200, headers: { "content-type": "IMAGE/PNG; charset=binary" } }), - async (url) => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const result = await exec({ url: new URL("/image.png", url).toString(), format: "markdown" }) + it.instance("returns image responses as file attachments", () => + Effect.gen(function* () { + const bytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]) + yield* withFetch( + () => new Response(bytes, { status: 200, headers: { "content-type": "IMAGE/PNG; charset=binary" } }), + (url) => + Effect.gen(function* () { + const result = yield* exec({ url: new URL("/image.png", url).toString(), format: "markdown" }) expect(result.output).toBe("Image fetched successfully") expect(result.attachments).toBeDefined() expect(result.attachments?.length).toBe(1) @@ -55,102 +55,59 @@ describe("tool.webfetch", () => { expect(result.attachments?.[0]).not.toHaveProperty("id") expect(result.attachments?.[0]).not.toHaveProperty("sessionID") expect(result.attachments?.[0]).not.toHaveProperty("messageID") - }, - }) - }, - ) - }) + }), + ) + }), + ) - test("keeps svg as text output", async () => { - const svg = 'hello' - await withFetch( + it.instance("keeps svg as text output", () => + withFetch( () => - new Response(svg, { + new Response('hello', { status: 200, headers: { "content-type": "image/svg+xml; charset=UTF-8" }, }), - async (url) => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const result = await exec({ url: new URL("/image.svg", url).toString(), format: "html" }) - expect(result.output).toContain(" { - const bytes = new Uint8Array([0, 0, 1, 0]) - await withFetch( - () => new Response(bytes, { status: 200, headers: { "content-type": mime } }), - async (url) => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - await expect(exec({ url: new URL("/favicon.ico", url).toString(), format: "markdown" })).rejects.toThrow( - `Unsupported image format: ${mime}`, - ) - }, - }) - }, - ) - }) - - test("returns non-icon image responses as file attachments", async () => { - const bytes = new Uint8Array([0, 0, 0, 0]) - await withFetch( - () => new Response(bytes, { status: 200, headers: { "content-type": "image/avif" } }), - async (url) => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const result = await exec({ url: new URL("/image.avif", url).toString(), format: "markdown" }) - expect(result.output).toBe("Image fetched successfully") - expect(result.attachments?.[0].mime).toBe("image/avif") - }, - }) - }, - ) - }) - - test("keeps fastbidsheet responses as text output", async () => { - await withFetch( - () => new Response("sheet", { status: 200, headers: { "content-type": "image/vnd.fastbidsheet" } }), - async (url) => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const result = await exec({ url: new URL("/sheet", url).toString(), format: "text" }) - expect(result.output).toBe("sheet") - expect(result.attachments).toBeUndefined() - }, - }) - }, - ) - }) - // kilocode_change end + (url) => + Effect.gen(function* () { + const result = yield* exec({ url: new URL("/image.svg", url).toString(), format: "html" }) + expect(result.output).toContain(" { - await withFetch( + it.instance("keeps text responses as text output", () => + withFetch( () => new Response("hello from webfetch", { status: 200, headers: { "content-type": "text/plain; charset=utf-8" }, }), - async (url) => { - await WithInstance.provide({ - directory: projectRoot, - fn: async () => { - const result = await exec({ url: new URL("/file.txt", url).toString(), format: "text" }) - expect(result.output).toBe("hello from webfetch") - expect(result.attachments).toBeUndefined() + (url) => + Effect.gen(function* () { + const result = yield* exec({ url: new URL("/file.txt", url).toString(), format: "text" }) + expect(result.output).toBe("hello from webfetch") + expect(result.attachments).toBeUndefined() + }), + ), + ) + + it.instance("extracts text from html without scripts or styles", () => + withFetch( + () => + new Response( + "Hello world", + { + status: 200, + headers: { "content-type": "text/html; charset=utf-8" }, }, - }) - }, - ) - }) + ), + (url) => + Effect.gen(function* () { + const result = yield* exec({ url: new URL("/page.html", url).toString(), format: "text" }) + expect(result.output).toBe("Hello world") + expect(result.attachments).toBeUndefined() + }), + ), + ) }) diff --git a/packages/opencode/test/tool/websearch.test.ts b/packages/opencode/test/tool/websearch.test.ts index 27bc78c21be..a621d30a334 100644 --- a/packages/opencode/test/tool/websearch.test.ts +++ b/packages/opencode/test/tool/websearch.test.ts @@ -4,6 +4,7 @@ import { parseResponse } from "../../src/tool/mcp-websearch" import { selectWebSearchProvider, webSearchModelName, webSearchProviderLabel } from "../../src/tool/websearch" import { ProviderID } from "../../src/provider/schema" import { webSearchEnabled } from "../../src/tool/registry" +import { it } from "../lib/effect" const SESSION_ID = "ses_0196aabbccddeeff001122334455" @@ -76,17 +77,24 @@ describe("websearch MCP response parser", () => { }, }) - test("parses plain JSON-RPC responses", async () => { - await expect(Effect.runPromise(parseResponse(payload))).resolves.toBe("search results") - }) + it.effect("parses plain JSON-RPC responses", () => + Effect.gen(function* () { + const result = yield* parseResponse(payload) + expect(result).toBe("search results") + }), + ) - test("parses SSE JSON-RPC responses", async () => { - await expect(Effect.runPromise(parseResponse(`event: message\ndata: ${payload}\n\n`))).resolves.toBe( - "search results", - ) - }) + it.effect("parses SSE JSON-RPC responses", () => + Effect.gen(function* () { + const result = yield* parseResponse(`event: message\ndata: ${payload}\n\n`) + expect(result).toBe("search results") + }), + ) - test("ignores non-JSON SSE data frames", async () => { - await expect(Effect.runPromise(parseResponse(`data: [DONE]\ndata: ${payload}\n\n`))).resolves.toBe("search results") - }) + it.effect("ignores non-JSON SSE data frames", () => + Effect.gen(function* () { + const result = yield* parseResponse(`data: [DONE]\ndata: ${payload}\n\n`) + expect(result).toBe("search results") + }), + ) }) diff --git a/packages/opencode/test/util/error.test.ts b/packages/opencode/test/util/error.test.ts index e7a02d6151e..fdb559a231f 100644 --- a/packages/opencode/test/util/error.test.ts +++ b/packages/opencode/test/util/error.test.ts @@ -1,5 +1,7 @@ import { describe, expect, test } from "bun:test" +import { NamedError } from "@opencode-ai/core/util/error" import { errorData, errorFormat, errorMessage } from "../../src/util/error" +import { MessageError } from "../../src/session/message-error" describe("util.error", () => { test("formats native Error instances", () => { @@ -48,4 +50,15 @@ describe("util.error", () => { expect(data.message).toBe("ResolveMessage: Cannot resolve module") expect(String(data.formatted)).toContain("ResolveMessage") }) + + test("schema-backed named errors are real NamedError instances", () => { + const error = new MessageError.AuthError({ providerID: "anthropic", message: "boom" }) + + expect(error).toBeInstanceOf(NamedError) + expect(error.toObject()).toEqual({ name: "ProviderAuthError", data: { providerID: "anthropic", message: "boom" } }) + }) + + test("named errors without fields serialize data", () => { + expect(new MessageError.OutputLengthError({}).toObject()).toEqual({ name: "MessageOutputLengthError", data: {} }) + }) }) diff --git a/packages/opencode/test/util/lock.test.ts b/packages/opencode/test/util/lock.test.ts deleted file mode 100644 index 79fbb583162..00000000000 --- a/packages/opencode/test/util/lock.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { describe, expect, test } from "bun:test" -import { Lock } from "@/util/lock" - -function tick() { - return new Promise((r) => queueMicrotask(r)) -} - -async function flush(n = 5) { - for (let i = 0; i < n; i++) await tick() -} - -describe("util.lock", () => { - test("writer exclusivity: blocks reads and other writes while held", async () => { - const key = "lock:" + Math.random().toString(36).slice(2) - - const state = { - writer2: false, - reader: false, - writers: 0, - } - - // Acquire writer1 - using writer1 = await Lock.write(key) - state.writers++ - expect(state.writers).toBe(1) - - // Start writer2 candidate (should block) - const writer2Task = (async () => { - const w = await Lock.write(key) - state.writers++ - expect(state.writers).toBe(1) - state.writer2 = true - // Hold for a tick so reader cannot slip in - await tick() - return w - })() - - // Start reader candidate (should block) - const readerTask = (async () => { - const r = await Lock.read(key) - state.reader = true - return r - })() - - // Flush microtasks and assert neither acquired - await flush() - expect(state.writer2).toBe(false) - expect(state.reader).toBe(false) - - // Release writer1 - writer1[Symbol.dispose]() - state.writers-- - - // writer2 should acquire next - const writer2 = await writer2Task - expect(state.writer2).toBe(true) - - // Reader still blocked while writer2 held - await flush() - expect(state.reader).toBe(false) - - // Release writer2 - writer2[Symbol.dispose]() - state.writers-- - - // Reader should now acquire - const reader = await readerTask - expect(state.reader).toBe(true) - - reader[Symbol.dispose]() - }) -}) diff --git a/packages/opencode/test/util/log.test.ts b/packages/opencode/test/util/log.test.ts index 99846c27dfc..da42c6d0221 100644 --- a/packages/opencode/test/util/log.test.ts +++ b/packages/opencode/test/util/log.test.ts @@ -1,89 +1,77 @@ -import { afterEach, expect, test } from "bun:test" +import { expect } from "bun:test" +import { Effect } from "effect" import fs from "fs/promises" import path from "path" +import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { Global } from "@opencode-ai/core/global" import * as Log from "@opencode-ai/core/util/log" -import * as Process from "../../src/util/process" // kilocode_change -import { tmpdir } from "../fixture/fixture" - -const log = Global.Path.log - -afterEach(() => { - Global.Path.log = log -}) - -async function files(dir: string) { - let last = "" - let same = 0 - - for (let i = 0; i < 50; i++) { - const list = (await fs.readdir(dir)).sort() - const next = JSON.stringify(list) - same = next === last ? same + 1 : 0 - if (same >= 2 && list.length === 11) return list - last = next - await Bun.sleep(10) - } - - return (await fs.readdir(dir)).sort() -} - -test("init cleanup keeps the newest timestamped logs", async () => { - await using tmp = await tmpdir() - Global.Path.log = tmp.path - - const list = Array.from({ length: 12 }, (_, i) => `2000-01-${String(i + 1).padStart(2, "0")}T000000.log`) - - await Promise.all(list.map((file) => fs.writeFile(path.join(tmp.path, file), file))) - - await Log.init({ print: false, dev: false }) - - const next = await files(tmp.path) - - expect(next).not.toContain(list[0]!) - expect(next).toContain(list.at(-1)!) -}) - -// kilocode_change start -const root = path.join(import.meta.dir, "../..") -const worker = path.join(import.meta.dir, "../fixture/log-init-worker.ts") - -test("uses single log directory for rotation history", async () => { - await using tmp = await tmpdir() - - const out = await Process.run([process.execPath, "--conditions=browser", worker, tmp.path], { - cwd: root, - nothrow: true, +import { tmpdirScoped } from "../fixture/fixture" +import { testEffect } from "../lib/effect" + +const it = testEffect(CrossSpawnSpawner.defaultLayer) + +function files(dir: string) { + return Effect.gen(function* () { + let last = "" + let same = 0 + + for (let i = 0; i < 50; i++) { + const list = yield* Effect.promise(() => fs.readdir(dir).then((files) => files.sort())) + const next = JSON.stringify(list) + same = next === last ? same + 1 : 0 + if (same >= 2 && list.length === 11) return list + last = next + yield* Effect.sleep("10 millis") + } + + return yield* Effect.promise(() => fs.readdir(dir).then((files) => files.sort())) }) +} - const dir = path.join(tmp.path, "share", "kilo", "log") - const history = path.join(dir, ".log-history") - - expect(out.code).toBe(0) - expect(out.stderr.toString()).not.toContain("log stream error:") - expect(out.stdout.toString()).toBe(path.join(dir, "dev.log")) - - const stat = await fs.stat(history) - expect(stat.isFile()).toBe(true) -}) - -test("skips rotation rename when active log file is missing", async () => { - await using tmp = await tmpdir() - - const out = await Process.run([process.execPath, "--conditions=browser", worker, tmp.path, "missing"], { - cwd: root, - nothrow: true, - }) - - const dir = path.join(tmp.path, "share", "kilo", "log") - const list = (await fs.readdir(dir)).sort() - const next = list.filter((file) => /^\d{8}-\d{4}-\d{2}-dev\.log$/.test(file)) - const stat = await fs.stat(path.join(dir, next[0]!)) - - expect(out.code).toBe(0) - expect(out.stderr.toString()).not.toContain("log stream error:") - expect(list).toContain("dev.log") - expect(next).toHaveLength(1) - expect(stat.size).toBe(0) -}) -// kilocode_change end +it.live("init cleanup keeps the newest timestamped logs", () => + Effect.gen(function* () { + const log = Global.Path.log + yield* Effect.addFinalizer(() => Effect.sync(() => (Global.Path.log = log))) + const dir = yield* tmpdirScoped() + Global.Path.log = dir + + const list = Array.from({ length: 12 }, (_, i) => `2000-01-${String(i + 1).padStart(2, "0")}T000000.log`) + + yield* Effect.all(list.map((file) => Effect.promise(() => fs.writeFile(path.join(dir, file), file)))) + + yield* Effect.promise(() => Log.init({ print: false, dev: false })) + + const next = yield* files(dir) + + expect(next).not.toContain(list[0]!) + expect(next).toContain(list.at(-1)!) + }), +) + +it.live("local dev log is not truncated twice for the same run", () => + Effect.gen(function* () { + const log = Global.Path.log + const runID = process.env.KILO_RUN_ID + const initialized = process.env.KILO_LOG_INITIALIZED_RUN_ID + yield* Effect.addFinalizer(() => + Effect.sync(() => { + Global.Path.log = log + if (runID === undefined) delete process.env.KILO_RUN_ID + else process.env.KILO_RUN_ID = runID + if (initialized === undefined) delete process.env.KILO_LOG_INITIALIZED_RUN_ID + else process.env.KILO_LOG_INITIALIZED_RUN_ID = initialized + }), + ) + + const dir = yield* tmpdirScoped() + Global.Path.log = dir + process.env.KILO_RUN_ID = "run-1" + delete process.env.KILO_LOG_INITIALIZED_RUN_ID + + yield* Effect.promise(() => Log.init({ print: false, dev: true })) + yield* Effect.promise(() => fs.writeFile(path.join(dir, "dev.log"), "main startup\n")) + yield* Effect.promise(() => Log.init({ print: false, dev: true })) + + expect(yield* Effect.promise(() => fs.readFile(path.join(dir, "dev.log"), "utf8"))).toContain("main startup") + }), +) diff --git a/packages/opencode/test/v2/session-message-updater.test.ts b/packages/opencode/test/v2/session-message-updater.test.ts index 44ac031edab..180483937c5 100644 --- a/packages/opencode/test/v2/session-message-updater.test.ts +++ b/packages/opencode/test/v2/session-message-updater.test.ts @@ -2,7 +2,8 @@ import { expect, test } from "bun:test" import * as DateTime from "effect/DateTime" import { SessionID } from "../../src/session/schema" import { EventV2 } from "../../src/v2/event" -import { Modelv2 } from "../../src/v2/model" +import { ModelV2 } from "@opencode-ai/core/model" +import { ProviderV2 } from "@opencode-ai/core/provider" import { SessionEvent } from "../../src/v2/session-event" import { SessionMessageUpdater } from "../../src/v2/session-message-updater" @@ -18,9 +19,9 @@ test("step snapshots carry over to assistant messages", () => { timestamp: DateTime.makeUnsafe(1), agent: "build", model: { - id: Modelv2.ID.make("model"), - providerID: Modelv2.ProviderID.make("provider"), - variant: Modelv2.VariantID.make("default"), + id: ModelV2.ID.make("model"), + providerID: ProviderV2.ID.make("provider"), + variant: ModelV2.VariantID.make("default"), }, snapshot: "before", }, @@ -62,9 +63,9 @@ test("text ended populates assistant text content", () => { timestamp: DateTime.makeUnsafe(1), agent: "build", model: { - id: Modelv2.ID.make("model"), - providerID: Modelv2.ProviderID.make("provider"), - variant: Modelv2.VariantID.make("default"), + id: ModelV2.ID.make("model"), + providerID: ProviderV2.ID.make("provider"), + variant: ModelV2.VariantID.make("default"), }, }, } satisfies SessionEvent.Event) @@ -106,9 +107,9 @@ test("tool completion stores completed timestamp", () => { timestamp: DateTime.makeUnsafe(1), agent: "build", model: { - id: Modelv2.ID.make("model"), - providerID: Modelv2.ProviderID.make("provider"), - variant: Modelv2.VariantID.make("default"), + id: ModelV2.ID.make("model"), + providerID: ProviderV2.ID.make("provider"), + variant: ModelV2.VariantID.make("default"), }, }, } satisfies SessionEvent.Event) diff --git a/packages/plugin-atomic-chat/src/cache/model-status-cache.ts b/packages/plugin-atomic-chat/src/cache/model-status-cache.ts index 58b6dea806b..e2bcc77e561 100644 --- a/packages/plugin-atomic-chat/src/cache/model-status-cache.ts +++ b/packages/plugin-atomic-chat/src/cache/model-status-cache.ts @@ -1,5 +1,5 @@ -import type { CacheStats } from '../types' -import { LOG_PREFIX } from '../constants' +import type { CacheStats } from "../types" +import { LOG_PREFIX } from "../constants" export class ModelStatusCache { private cache = new Map< @@ -91,9 +91,7 @@ export class ModelStatusCache { return } - const sorted = Array.from(this.cache.entries()).sort( - (a, b) => a[1].timestamp - b[1].timestamp - ) + const sorted = Array.from(this.cache.entries()).sort((a, b) => a[1].timestamp - b[1].timestamp) const excess = this.cache.size - this.MAX_CACHE_SIZE for (let i = 0; i < excess; i++) { this.cache.delete(sorted[i]![0]) diff --git a/packages/plugin-atomic-chat/src/cache/shared-model-status-cache.ts b/packages/plugin-atomic-chat/src/cache/shared-model-status-cache.ts index 5a3fea0bea3..f27baf46ef2 100644 --- a/packages/plugin-atomic-chat/src/cache/shared-model-status-cache.ts +++ b/packages/plugin-atomic-chat/src/cache/shared-model-status-cache.ts @@ -1,4 +1,4 @@ -import { ModelStatusCache } from './model-status-cache' +import { ModelStatusCache } from "./model-status-cache" /** Single shared cache for model list lookups across plugin hooks. */ export const sharedModelStatusCache = new ModelStatusCache() diff --git a/packages/plugin-atomic-chat/src/constants.ts b/packages/plugin-atomic-chat/src/constants.ts index e7fa6aa9f7e..dade5f1b068 100644 --- a/packages/plugin-atomic-chat/src/constants.ts +++ b/packages/plugin-atomic-chat/src/constants.ts @@ -1,11 +1,11 @@ /** Provider id and `kilo.json` key (see https://atomic.chat and models.dev). */ -export const ATOMIC_CHAT_PROVIDER_KEY = 'atomic-chat' as const +export const ATOMIC_CHAT_PROVIDER_KEY = "atomic-chat" as const -export const DEFAULT_ATOMIC_CHAT_ORIGIN = 'http://127.0.0.1:1337' +export const DEFAULT_ATOMIC_CHAT_ORIGIN = "http://127.0.0.1:1337" /** Ports tried when auto-detecting a running Atomic Chat API (default is 1337). */ export const ATOMIC_CHAT_PROBE_PORTS = [1337, 1338] as const -export const LOG_PREFIX = '[@kilocode/plugin-atomic-chat]' as const +export const LOG_PREFIX = "[@kilocode/plugin-atomic-chat]" as const -export const ATOMIC_CHAT_PLUGIN = '@kilocode/plugin-atomic-chat' as const +export const ATOMIC_CHAT_PLUGIN = "@kilocode/plugin-atomic-chat" as const diff --git a/packages/plugin-atomic-chat/src/index.ts b/packages/plugin-atomic-chat/src/index.ts index 375dabcbfa5..19f3966b342 100644 --- a/packages/plugin-atomic-chat/src/index.ts +++ b/packages/plugin-atomic-chat/src/index.ts @@ -1,4 +1,4 @@ -import { AtomicChatPlugin } from './plugin' +import { AtomicChatPlugin } from "./plugin" export { AtomicChatPlugin } export { @@ -7,6 +7,6 @@ export { DEFAULT_ATOMIC_CHAT_ORIGIN, ATOMIC_CHAT_PROBE_PORTS, LOG_PREFIX, -} from './constants' +} from "./constants" export default AtomicChatPlugin diff --git a/packages/plugin-atomic-chat/src/plugin/auth-hook.ts b/packages/plugin-atomic-chat/src/plugin/auth-hook.ts index 5ccc0bb1a4e..3fdcd796f43 100644 --- a/packages/plugin-atomic-chat/src/plugin/auth-hook.ts +++ b/packages/plugin-atomic-chat/src/plugin/auth-hook.ts @@ -1,13 +1,13 @@ -import type { Hooks } from '@kilocode/plugin' -import { ATOMIC_CHAT_PROVIDER_KEY } from '../constants' +import type { Hooks } from "@kilocode/plugin" +import { ATOMIC_CHAT_PROVIDER_KEY } from "../constants" -export function createAuthHook(): NonNullable { +export function createAuthHook(): NonNullable { return { provider: ATOMIC_CHAT_PROVIDER_KEY, methods: [ { - type: 'api', - label: 'Local server', + type: "api", + label: "Local server", }, ], } diff --git a/packages/plugin-atomic-chat/src/plugin/chat-params-hook.ts b/packages/plugin-atomic-chat/src/plugin/chat-params-hook.ts index c68fdc9278b..409a28b376b 100644 --- a/packages/plugin-atomic-chat/src/plugin/chat-params-hook.ts +++ b/packages/plugin-atomic-chat/src/plugin/chat-params-hook.ts @@ -1,10 +1,10 @@ -import { sharedModelStatusCache } from '../cache/shared-model-status-cache' -import { ToastNotifier } from '../ui/toast-notifier' -import { findSimilarModels, retryWithBackoff, categorizeError, generateAutoFixSuggestions } from '../utils' -import { getLoadedModels } from './get-loaded-models' -import { normalizeBaseURL } from '../utils/atomic-chat-api' -import { isPluginHookInput, isAtomicChatProvider, isValidModel } from '../utils/validation' -import { DEFAULT_ATOMIC_CHAT_ORIGIN, LOG_PREFIX } from '../constants' +import { sharedModelStatusCache } from "../cache/shared-model-status-cache" +import { ToastNotifier } from "../ui/toast-notifier" +import { findSimilarModels, retryWithBackoff, categorizeError, generateAutoFixSuggestions } from "../utils" +import { getLoadedModels } from "./get-loaded-models" +import { normalizeBaseURL } from "../utils/atomic-chat-api" +import { isPluginHookInput, isAtomicChatProvider, isValidModel } from "../utils/validation" +import { DEFAULT_ATOMIC_CHAT_ORIGIN, LOG_PREFIX } from "../constants" export function createChatParamsHook(toastNotifier: ToastNotifier) { return async (input: any, output: any) => { @@ -40,11 +40,11 @@ export function createChatParamsHook(toastNotifier: ToastNotifier) { return loadedModels }, 3, - 500 + 500, ) if (!validationResult.success || !validationResult.result) { - const errorCategory = categorizeError(validationResult.error || 'Validation operation failed', { + const errorCategory = categorizeError(validationResult.error || "Validation operation failed", { baseURL, modelId: model.id, }) @@ -58,21 +58,21 @@ export function createChatParamsHook(toastNotifier: ToastNotifier) { }) const availableModels = - errorCategory.type === 'offline' ? [] : lastLoadedModels.length > 0 ? lastLoadedModels : [] + errorCategory.type === "offline" ? [] : lastLoadedModels.length > 0 ? lastLoadedModels : [] const similarModels = findSimilarModels(model.id, availableModels) await toastNotifier.error( `Model '${model.id}' not ready: ${errorCategory.message}`, - 'Model Validation Failed', - 8000 + "Model Validation Failed", + 8000, ) if (!output.options) { output.options = {} } output.options.atomicChatValidation = { - status: 'error', + status: "error", model: model.id, availableModels, errorCategory: errorCategory.type, @@ -82,17 +82,17 @@ export function createChatParamsHook(toastNotifier: ToastNotifier) { autoFixAvailable: errorCategory.autoFixAvailable, autoFixSuggestions, steps: - errorCategory.type === 'not_found' + errorCategory.type === "not_found" ? [ - '1. Open Atomic Chat', - '2. Load the model you want to use', - '3. Confirm curl http://127.0.0.1:1337/v1/models lists that model id', - '4. Retry in Kilo Code', + "1. Open Atomic Chat", + "2. Load the model you want to use", + "3. Confirm curl http://127.0.0.1:1337/v1/models lists that model id", + "4. Retry in Kilo Code", ] : [ - '1. Ensure Atomic Chat is running', - '2. Verify the API URL in kilo.json matches Atomic Chat settings', - '3. Retry your request', + "1. Ensure Atomic Chat is running", + "2. Verify the API URL in kilo.json matches Atomic Chat settings", + "3. Retry your request", ], similarModels: similarModels.map((item) => ({ model: item.model, @@ -110,7 +110,7 @@ export function createChatParamsHook(toastNotifier: ToastNotifier) { output.options = {} } output.options.atomicChatValidation = { - status: 'success', + status: "success", model: model.id, availableModels: loadedModels, message: `Model '${model.id}' is listed by Atomic Chat and ready.`, diff --git a/packages/plugin-atomic-chat/src/plugin/config-hook.ts b/packages/plugin-atomic-chat/src/plugin/config-hook.ts index 15bf5d2f6a9..f8b0cd1e25c 100644 --- a/packages/plugin-atomic-chat/src/plugin/config-hook.ts +++ b/packages/plugin-atomic-chat/src/plugin/config-hook.ts @@ -1,12 +1,12 @@ -import { ToastNotifier } from '../ui/toast-notifier' -import { validateConfig } from '../utils/validation' -import { enhanceConfig, shouldProbeAtomicChat } from './enhance-config' -import type { PluginInput } from '@kilocode/plugin' -import { ATOMIC_CHAT_PROVIDER_KEY, LOG_PREFIX } from '../constants' +import { ToastNotifier } from "../ui/toast-notifier" +import { validateConfig } from "../utils/validation" +import { enhanceConfig, shouldProbeAtomicChat } from "./enhance-config" +import type { PluginInput } from "@kilocode/plugin" +import { ATOMIC_CHAT_PROVIDER_KEY, LOG_PREFIX } from "../constants" const CONFIG_DISCOVERY_TIMEOUT_MS = 5000 -export function createConfigHook(client: PluginInput['client'], toastNotifier: ToastNotifier) { +export function createConfigHook(client: PluginInput["client"], toastNotifier: ToastNotifier) { return async (config: any) => { const section = config?.provider?.[ATOMIC_CHAT_PROVIDER_KEY] const initialModelCount = section?.models ? Object.keys(section.models).length : 0 @@ -19,7 +19,7 @@ export function createConfigHook(client: PluginInput['client'], toastNotifier: T const validation = validateConfig(config) if (!validation.isValid) { console.error(`${LOG_PREFIX} Invalid config provided:`, validation.errors) - toastNotifier.error('Plugin configuration is invalid', 'Configuration Error').catch((err) => { + toastNotifier.error("Plugin configuration is invalid", "Configuration Error").catch((err) => { console.warn(`${LOG_PREFIX} Failed to show configuration error toast`, { error: err instanceof Error ? err.message : String(err), }) diff --git a/packages/plugin-atomic-chat/src/plugin/enhance-config.ts b/packages/plugin-atomic-chat/src/plugin/enhance-config.ts index d5d07807468..9c86497ebb3 100644 --- a/packages/plugin-atomic-chat/src/plugin/enhance-config.ts +++ b/packages/plugin-atomic-chat/src/plugin/enhance-config.ts @@ -1,18 +1,18 @@ -import { sharedModelStatusCache } from '../cache/shared-model-status-cache' -import { ToastNotifier } from '../ui/toast-notifier' -import { categorizeModel, formatModelName, extractModelOwner } from '../utils' -import { normalizeBaseURL, fetchModelsEndpoint, autoDetectAtomicChat } from '../utils/atomic-chat-api' +import { sharedModelStatusCache } from "../cache/shared-model-status-cache" +import { ToastNotifier } from "../ui/toast-notifier" +import { categorizeModel, formatModelName, extractModelOwner } from "../utils" +import { normalizeBaseURL, fetchModelsEndpoint, autoDetectAtomicChat } from "../utils/atomic-chat-api" import { getAtomicSection, hasAtomicChatProviderSection, isAtomicChatAutoDetectEnabled, shouldProbeAtomicChat, -} from '../utils/should-probe-atomic-chat' -import type { PluginInput } from '@kilocode/plugin' -import type { AtomicChatModel } from '../types' -import { ATOMIC_CHAT_PROVIDER_KEY, DEFAULT_ATOMIC_CHAT_ORIGIN, LOG_PREFIX } from '../constants' +} from "../utils/should-probe-atomic-chat" +import type { PluginInput } from "@kilocode/plugin" +import type { AtomicChatModel } from "../types" +import { ATOMIC_CHAT_PROVIDER_KEY, DEFAULT_ATOMIC_CHAT_ORIGIN, LOG_PREFIX } from "../constants" -export { shouldProbeAtomicChat } from '../utils/should-probe-atomic-chat' +export { shouldProbeAtomicChat } from "../utils/should-probe-atomic-chat" function setAtomicSection(config: any, value: Record) { if (!config.provider) { @@ -23,9 +23,9 @@ function setAtomicSection(config: any, value: Record) { export async function enhanceConfig( config: any, - _client: PluginInput['client'], + _client: PluginInput["client"], toastNotifier: ToastNotifier, - signal?: AbortSignal + signal?: AbortSignal, ): Promise { if (!shouldProbeAtomicChat(config) || signal?.aborted) { return @@ -46,8 +46,8 @@ export async function enhanceConfig( baseURL = detected.baseURL models = detected.models setAtomicSection(config, { - npm: '@ai-sdk/openai-compatible', - name: 'Atomic Chat (local)', + npm: "@ai-sdk/openai-compatible", + name: "Atomic Chat (local)", options: { baseURL: `${baseURL}/v1`, }, @@ -57,8 +57,8 @@ export async function enhanceConfig( } else { baseURL = normalizeBaseURL(DEFAULT_ATOMIC_CHAT_ORIGIN) setAtomicSection(config, { - npm: '@ai-sdk/openai-compatible', - name: 'Atomic Chat (local)', + npm: "@ai-sdk/openai-compatible", + name: "Atomic Chat (local)", options: { baseURL: `${baseURL}/v1`, }, @@ -101,7 +101,7 @@ export async function enhanceConfig( for (const model of models) { let modelKey = model.id if (!/^[a-zA-Z0-9_-]+$/.test(modelKey)) { - modelKey = model.id.replace(/[^a-zA-Z0-9_-]/g, '_') + modelKey = model.id.replace(/[^a-zA-Z0-9_-]/g, "_") } if (!existingModels[modelKey] && !existingModels[model.id]) { @@ -116,17 +116,17 @@ export async function enhanceConfig( modelConfig.organizationOwner = owner } - if (modelType === 'embedding') { + if (modelType === "embedding") { embeddingModelsCount++ modelConfig.modalities = { - input: ['text'], - output: ['embedding'], + input: ["text"], + output: ["embedding"], } - } else if (modelType === 'chat') { + } else if (modelType === "chat") { chatModelsCount++ modelConfig.modalities = { - input: ['text', 'image'], - output: ['text'], + input: ["text", "image"], + output: ["text"], } } @@ -146,7 +146,7 @@ export async function enhanceConfig( if (chatModelsCount === 0 && embeddingModelsCount > 0) { console.warn( - `${LOG_PREFIX} Only embedding-style models detected; load a chat model in Atomic Chat for coding agents.` + `${LOG_PREFIX} Only embedding-style models detected; load a chat model in Atomic Chat for coding agents.`, ) } } @@ -170,12 +170,10 @@ export async function enhanceConfig( } } catch (error) { console.error(`${LOG_PREFIX} Unexpected error in enhanceConfig:`, error) - toastNotifier - .warning('Plugin configuration failed', 'Configuration Error') - .catch((err) => { - console.warn(`${LOG_PREFIX} Failed to show configuration warning toast`, { - error: err instanceof Error ? err.message : String(err), - }) + toastNotifier.warning("Plugin configuration failed", "Configuration Error").catch((err) => { + console.warn(`${LOG_PREFIX} Failed to show configuration warning toast`, { + error: err instanceof Error ? err.message : String(err), }) + }) } } diff --git a/packages/plugin-atomic-chat/src/plugin/event-hook.ts b/packages/plugin-atomic-chat/src/plugin/event-hook.ts index 87309d0679a..b86dc856c12 100644 --- a/packages/plugin-atomic-chat/src/plugin/event-hook.ts +++ b/packages/plugin-atomic-chat/src/plugin/event-hook.ts @@ -1,15 +1,15 @@ -import { validateHookInput } from '../utils/validation' -import { LOG_PREFIX } from '../constants' +import { validateHookInput } from "../utils/validation" +import { LOG_PREFIX } from "../constants" export function createEventHook() { return async ({ event }: { event: any }) => { - const validation = validateHookInput('event', { event }) + const validation = validateHookInput("event", { event }) if (!validation.isValid) { console.error(`${LOG_PREFIX} Invalid event input:`, validation.errors) return } - if (event.type === 'session.created' || event.type === 'session.updated') { + if (event.type === "session.created" || event.type === "session.updated") { // reserved for future health hooks } } diff --git a/packages/plugin-atomic-chat/src/plugin/get-loaded-models.ts b/packages/plugin-atomic-chat/src/plugin/get-loaded-models.ts index 179613145ce..3e304b4ecc1 100644 --- a/packages/plugin-atomic-chat/src/plugin/get-loaded-models.ts +++ b/packages/plugin-atomic-chat/src/plugin/get-loaded-models.ts @@ -1,6 +1,6 @@ -import { sharedModelStatusCache } from '../cache/shared-model-status-cache' -import { fetchModelsDirect } from '../utils/atomic-chat-api' -import { DEFAULT_ATOMIC_CHAT_ORIGIN } from '../constants' +import { sharedModelStatusCache } from "../cache/shared-model-status-cache" +import { fetchModelsDirect } from "../utils/atomic-chat-api" +import { DEFAULT_ATOMIC_CHAT_ORIGIN } from "../constants" async function fetchLoadedModelIds(baseURL: string): Promise { return await fetchModelsDirect(baseURL) @@ -8,7 +8,7 @@ async function fetchLoadedModelIds(baseURL: string): Promise { export function getLoadedModels( baseURL: string = DEFAULT_ATOMIC_CHAT_ORIGIN, - options?: { refresh?: boolean } + options?: { refresh?: boolean }, ): Promise { const fetchFn = () => fetchLoadedModelIds(baseURL) if (options?.refresh) { diff --git a/packages/plugin-atomic-chat/src/plugin/index.ts b/packages/plugin-atomic-chat/src/plugin/index.ts index 2d661fe8af0..59d090d611b 100644 --- a/packages/plugin-atomic-chat/src/plugin/index.ts +++ b/packages/plugin-atomic-chat/src/plugin/index.ts @@ -1,22 +1,22 @@ -import type { Plugin, PluginInput } from '@kilocode/plugin' -import { ToastNotifier } from '../ui/toast-notifier' -import { createConfigHook } from './config-hook' -import { createEventHook } from './event-hook' -import { createChatParamsHook } from './chat-params-hook' -import { createAuthHook } from './auth-hook' -import { LOG_PREFIX } from '../constants' +import type { Plugin, PluginInput } from "@kilocode/plugin" +import { ToastNotifier } from "../ui/toast-notifier" +import { createConfigHook } from "./config-hook" +import { createEventHook } from "./event-hook" +import { createChatParamsHook } from "./chat-params-hook" +import { createAuthHook } from "./auth-hook" +import { LOG_PREFIX } from "../constants" export const AtomicChatPlugin: Plugin = async (input: PluginInput) => { console.log(`${LOG_PREFIX} Atomic Chat plugin initialized`) const { client } = input - if (!client || typeof client !== 'object') { + if (!client || typeof client !== "object") { console.error(`${LOG_PREFIX} Invalid client provided to plugin`) return { config: async () => {}, event: async () => {}, - 'chat.params': async () => {}, + "chat.params": async () => {}, } } @@ -26,6 +26,6 @@ export const AtomicChatPlugin: Plugin = async (input: PluginInput) => { auth: createAuthHook(), config: createConfigHook(client, toastNotifier), event: createEventHook(), - 'chat.params': createChatParamsHook(toastNotifier), + "chat.params": createChatParamsHook(toastNotifier), } } diff --git a/packages/plugin-atomic-chat/src/types/index.ts b/packages/plugin-atomic-chat/src/types/index.ts index deeabde4547..29bc83f0a2c 100644 --- a/packages/plugin-atomic-chat/src/types/index.ts +++ b/packages/plugin-atomic-chat/src/types/index.ts @@ -11,13 +11,13 @@ export interface AtomicChatModelsResponse { data: AtomicChatModel[] } -export type ModelType = 'chat' | 'embedding' | 'unknown' +export type ModelType = "chat" | "embedding" | "unknown" -export type LoadingStatus = 'not_loaded' | 'loading' | 'loaded' | 'error' +export type LoadingStatus = "not_loaded" | "loading" | "loaded" | "error" export interface ModelValidationError { - type: 'offline' | 'not_found' | 'network' | 'permission' | 'timeout' | 'unknown' - severity: 'low' | 'medium' | 'high' | 'critical' + type: "offline" | "not_found" | "network" | "permission" | "timeout" | "unknown" + severity: "low" | "medium" | "high" | "critical" message: string canRetry: boolean autoFixAvailable: boolean diff --git a/packages/plugin-atomic-chat/src/ui/toast-notifier.ts b/packages/plugin-atomic-chat/src/ui/toast-notifier.ts index 57afe86d8eb..22feb8aa9f8 100644 --- a/packages/plugin-atomic-chat/src/ui/toast-notifier.ts +++ b/packages/plugin-atomic-chat/src/ui/toast-notifier.ts @@ -1,4 +1,4 @@ -import { LOG_PREFIX } from '../constants' +import { LOG_PREFIX } from "../constants" export class ToastNotifier { constructor(private readonly client: any) {} @@ -13,7 +13,7 @@ export class ToastNotifier { body: { title, message, - variant: 'success', + variant: "success", duration: duration || 3000, }, }) @@ -32,7 +32,7 @@ export class ToastNotifier { body: { title, message, - variant: 'error', + variant: "error", duration: duration || 5000, }, }) @@ -51,7 +51,7 @@ export class ToastNotifier { body: { title, message, - variant: 'warning', + variant: "warning", duration: duration || 4000, }, }) @@ -70,7 +70,7 @@ export class ToastNotifier { body: { title, message: progress !== undefined ? `${message} (${progress}%)` : message, - variant: 'info', + variant: "info", duration: progress !== undefined ? 0 : 2000, }, }) diff --git a/packages/plugin-atomic-chat/src/utils/atomic-chat-api.ts b/packages/plugin-atomic-chat/src/utils/atomic-chat-api.ts index 8e15ee0125b..e1a8e136e98 100644 --- a/packages/plugin-atomic-chat/src/utils/atomic-chat-api.ts +++ b/packages/plugin-atomic-chat/src/utils/atomic-chat-api.ts @@ -1,12 +1,12 @@ -import type { AtomicChatModel, AtomicChatModelsResponse } from '../types' -import { ATOMIC_CHAT_PROBE_PORTS, DEFAULT_ATOMIC_CHAT_ORIGIN, LOG_PREFIX } from '../constants' +import type { AtomicChatModel, AtomicChatModelsResponse } from "../types" +import { ATOMIC_CHAT_PROBE_PORTS, DEFAULT_ATOMIC_CHAT_ORIGIN, LOG_PREFIX } from "../constants" -const MODELS_ENDPOINT = '/v1/models' +const MODELS_ENDPOINT = "/v1/models" const FETCH_TIMEOUT_MS = 3000 export function normalizeBaseURL(baseURL: string = DEFAULT_ATOMIC_CHAT_ORIGIN): string { - let normalized = baseURL.replace(/\/+$/, '') - if (normalized.endsWith('/v1')) { + let normalized = baseURL.replace(/\/+$/, "") + if (normalized.endsWith("/v1")) { normalized = normalized.slice(0, -3) } return normalized @@ -36,14 +36,11 @@ function fetchSignal(outer?: AbortSignal): AbortSignal { } /** Single GET /v1/models — shared by discovery, health, auto-detect, and chat validation. */ -export async function fetchModelsEndpoint( - baseURL: string, - signal?: AbortSignal -): Promise { +export async function fetchModelsEndpoint(baseURL: string, signal?: AbortSignal): Promise { const url = buildAPIURL(baseURL) const response = await fetch(url, { - method: 'GET', - headers: { 'Content-Type': 'application/json' }, + method: "GET", + headers: { "Content-Type": "application/json" }, signal: fetchSignal(signal), }) if (!response.ok) { @@ -55,7 +52,7 @@ export async function fetchModelsEndpoint( export async function checkAtomicChatHealth( baseURL: string = DEFAULT_ATOMIC_CHAT_ORIGIN, - signal?: AbortSignal + signal?: AbortSignal, ): Promise { try { const { ok } = await fetchModelsEndpoint(baseURL, signal) @@ -71,7 +68,7 @@ export async function checkAtomicChatHealth( export async function discoverAtomicChatModels( baseURL: string = DEFAULT_ATOMIC_CHAT_ORIGIN, - signal?: AbortSignal + signal?: AbortSignal, ): Promise { try { const { ok, models } = await fetchModelsEndpoint(baseURL, signal) @@ -87,11 +84,11 @@ export async function discoverAtomicChatModels( export async function fetchModelsDirect( baseURL: string = DEFAULT_ATOMIC_CHAT_ORIGIN, - signal?: AbortSignal + signal?: AbortSignal, ): Promise { const { ok, models } = await fetchModelsEndpoint(baseURL, signal) if (!ok) { - throw new Error('Atomic Chat models endpoint returned a non-success status') + throw new Error("Atomic Chat models endpoint returned a non-success status") } return models.map((model) => model.id) } diff --git a/packages/plugin-atomic-chat/src/utils/format-model-name.ts b/packages/plugin-atomic-chat/src/utils/format-model-name.ts index d24fc37a14a..c405685c3e2 100644 --- a/packages/plugin-atomic-chat/src/utils/format-model-name.ts +++ b/packages/plugin-atomic-chat/src/utils/format-model-name.ts @@ -1,7 +1,7 @@ -import type { AtomicChatModel } from '../types' +import type { AtomicChatModel } from "../types" export function extractModelOwner(modelId: string): string | undefined { - const parts = modelId.split('/') + const parts = modelId.split("/") if (parts.length > 1) { return parts[0] } @@ -10,9 +10,9 @@ export function extractModelOwner(modelId: string): string | undefined { export function formatModelName(model: AtomicChatModel): string { const { id } = model - const parts = id.split('/') + const parts = id.split("/") const modelPart = parts.length > 1 ? parts[1] : parts[0] - const acronyms = new Set(['gpt', 'oss', 'api', 'gguf', 'ggml', 'nomic', 'vl', 'it', 'mlx']) + const acronyms = new Set(["gpt", "oss", "api", "gguf", "ggml", "nomic", "vl", "it", "mlx"]) const tokens = modelPart .split(/[-_]/) @@ -36,7 +36,7 @@ export function formatModelName(model: AtomicChatModel): string { } return token.charAt(0).toUpperCase() + token.slice(1).toLowerCase() }) - .join(' ') + .join(" ") return tokens } diff --git a/packages/plugin-atomic-chat/src/utils/index.ts b/packages/plugin-atomic-chat/src/utils/index.ts index 9c6a32402a6..82afbdf35c0 100644 --- a/packages/plugin-atomic-chat/src/utils/index.ts +++ b/packages/plugin-atomic-chat/src/utils/index.ts @@ -1,27 +1,27 @@ -import type { ModelValidationError, AutoFixSuggestion, SimilarModel } from '../types' -import { LOG_PREFIX } from '../constants' +import type { ModelValidationError, AutoFixSuggestion, SimilarModel } from "../types" +import { LOG_PREFIX } from "../constants" -export { formatModelName, extractModelOwner } from './format-model-name' +export { formatModelName, extractModelOwner } from "./format-model-name" -export function categorizeModel(modelId: string): 'chat' | 'embedding' | 'unknown' { +export function categorizeModel(modelId: string): "chat" | "embedding" | "unknown" { const lowerId = modelId.toLowerCase() - if (lowerId.includes('embedding') || lowerId.includes('embed')) { - return 'embedding' + if (lowerId.includes("embedding") || lowerId.includes("embed")) { + return "embedding" } if ( - lowerId.includes('gpt') || - lowerId.includes('llama') || - lowerId.includes('claude') || - lowerId.includes('qwen') || - lowerId.includes('mistral') || - lowerId.includes('gemma') || - lowerId.includes('phi') || - lowerId.includes('falcon') || - lowerId.includes('deepseek') + lowerId.includes("gpt") || + lowerId.includes("llama") || + lowerId.includes("claude") || + lowerId.includes("qwen") || + lowerId.includes("mistral") || + lowerId.includes("gemma") || + lowerId.includes("phi") || + lowerId.includes("falcon") || + lowerId.includes("deepseek") ) { - return 'chat' + return "chat" } - return 'unknown' + return "unknown" } export function findSimilarModels(targetModel: string, availableModels: string[]): SimilarModel[] { @@ -37,7 +37,7 @@ export function findSimilarModels(targetModel: string, availableModels: string[] if (candidate === target) { similarity = 1.0 - reasons.push('Exact match') + reasons.push("Exact match") } const targetPrefix = targetTokens[0] @@ -47,7 +47,7 @@ export function findSimilarModels(targetModel: string, availableModels: string[] reasons.push(`Same family: ${targetPrefix}`) } - const commonSuffixes = ['3b', '7b', '13b', '70b', 'q4', 'q8', 'instruct', 'chat', 'base'] + const commonSuffixes = ["3b", "7b", "13b", "70b", "q4", "q8", "instruct", "chat", "base"] for (const suffix of commonSuffixes) { if (target.includes(suffix) && candidate.includes(suffix)) { similarity += 0.2 @@ -58,13 +58,13 @@ export function findSimilarModels(targetModel: string, availableModels: string[] const commonTokens = targetTokens.filter((token) => candidateTokens.includes(token)) if (commonTokens.length > 0) { similarity += (commonTokens.length / Math.max(targetTokens.length, candidateTokens.length)) * 0.3 - reasons.push(`Common tokens: ${commonTokens.join(', ')}`) + reasons.push(`Common tokens: ${commonTokens.join(", ")}`) } return { model, similarity: Math.min(similarity, 1.0), - reason: reasons.join(', '), + reason: reasons.join(", "), } }) .filter((item) => item.similarity > 0.1) @@ -75,7 +75,7 @@ export function findSimilarModels(targetModel: string, availableModels: string[] export async function retryWithBackoff( operation: () => Promise, maxAttempts: number = 3, - baseDelay: number = 1000 + baseDelay: number = 1000, ): Promise<{ success: boolean; result?: T; error?: string }> { for (let attempt = 0; attempt < maxAttempts; attempt++) { try { @@ -97,7 +97,7 @@ export async function retryWithBackoff( await new Promise((resolve) => setTimeout(resolve, delay)) } } - return { success: false, error: 'Max attempts exceeded' } + return { success: false, error: "Max attempts exceeded" } } export function categorizeError(error: unknown, context: { baseURL: string; modelId: string }): ModelValidationError { @@ -105,48 +105,44 @@ export function categorizeError(error: unknown, context: { baseURL: string; mode const { baseURL, modelId } = context if ( - errorStr.includes('econnrefused') || - errorStr.includes('fetch failed') || - errorStr.includes('failed to fetch') || - errorStr.includes('network') + errorStr.includes("econnrefused") || + errorStr.includes("fetch failed") || + errorStr.includes("failed to fetch") || + errorStr.includes("network") ) { return { - type: 'offline', - severity: 'critical', + type: "offline", + severity: "critical", message: `Cannot reach Atomic Chat at ${baseURL}. Start Atomic Chat and enable the local OpenAI-compatible server.`, canRetry: true, autoFixAvailable: true, } } - if (errorStr.includes('timeout') || errorStr.includes('aborted')) { + if (errorStr.includes("timeout") || errorStr.includes("aborted")) { return { - type: 'timeout', - severity: 'medium', + type: "timeout", + severity: "medium", message: `Request to Atomic Chat timed out.`, canRetry: true, autoFixAvailable: false, } } - if ( - errorStr.includes('404') || - errorStr.includes('not found') || - errorStr.includes('not loaded') - ) { + if (errorStr.includes("404") || errorStr.includes("not found") || errorStr.includes("not loaded")) { return { - type: 'not_found', - severity: 'high', + type: "not_found", + severity: "high", message: `Model '${modelId}' is not loaded in Atomic Chat. Load it and confirm GET /v1/models lists it.`, canRetry: false, autoFixAvailable: false, } } - if (errorStr.includes('401') || errorStr.includes('403') || errorStr.includes('unauthorized')) { + if (errorStr.includes("401") || errorStr.includes("403") || errorStr.includes("unauthorized")) { return { - type: 'permission', - severity: 'high', + type: "permission", + severity: "high", message: `Authentication or permission issue with Atomic Chat.`, canRetry: false, autoFixAvailable: false, @@ -154,8 +150,8 @@ export function categorizeError(error: unknown, context: { baseURL: string; mode } return { - type: 'unknown', - severity: 'medium', + type: "unknown", + severity: "medium", message: `Unexpected error: ${errorStr}`, canRetry: true, autoFixAvailable: false, @@ -166,37 +162,33 @@ export function generateAutoFixSuggestions(errorCategory: ModelValidationError): const suggestions: AutoFixSuggestion[] = [] switch (errorCategory.type) { - case 'offline': + case "offline": suggestions.push({ - action: 'Start Atomic Chat', + action: "Start Atomic Chat", steps: [ - '1. Open the Atomic Chat application', - '2. Ensure the local API server is running (default http://127.0.0.1:1337/v1)', - '3. Check firewall settings if the port is blocked', + "1. Open the Atomic Chat application", + "2. Ensure the local API server is running (default http://127.0.0.1:1337/v1)", + "3. Check firewall settings if the port is blocked", ], automated: false, }) break - case 'not_found': + case "not_found": suggestions.push({ - action: 'Load a model in Atomic Chat', + action: "Load a model in Atomic Chat", steps: [ - '1. Open Atomic Chat', - '2. Download or select a model and load it', - '3. Run curl http://127.0.0.1:1337/v1/models to verify the model id', - '4. Retry in Kilo Code', + "1. Open Atomic Chat", + "2. Download or select a model and load it", + "3. Run curl http://127.0.0.1:1337/v1/models to verify the model id", + "4. Retry in Kilo Code", ], automated: false, }) break - case 'timeout': + case "timeout": suggestions.push({ - action: 'Retry or reduce load', - steps: [ - '1. Try a smaller / faster model', - '2. Close other heavy apps', - '3. Retry the request', - ], + action: "Retry or reduce load", + steps: ["1. Try a smaller / faster model", "2. Close other heavy apps", "3. Retry the request"], automated: false, }) break diff --git a/packages/plugin-atomic-chat/src/utils/should-probe-atomic-chat.ts b/packages/plugin-atomic-chat/src/utils/should-probe-atomic-chat.ts index 611c2feb3c2..2c41eb8086f 100644 --- a/packages/plugin-atomic-chat/src/utils/should-probe-atomic-chat.ts +++ b/packages/plugin-atomic-chat/src/utils/should-probe-atomic-chat.ts @@ -1,4 +1,4 @@ -import { ATOMIC_CHAT_PROVIDER_KEY } from '../constants' +import { ATOMIC_CHAT_PROVIDER_KEY } from "../constants" export function getAtomicSection(config: any) { return config?.provider?.[ATOMIC_CHAT_PROVIDER_KEY] @@ -15,10 +15,10 @@ export function isAtomicChatAutoDetectEnabled(config: any): boolean { } function modelRefUsesAtomicChat(ref: unknown): boolean { - if (typeof ref === 'string') { + if (typeof ref === "string") { return ref.startsWith(`${ATOMIC_CHAT_PROVIDER_KEY}/`) } - if (!ref || typeof ref !== 'object') { + if (!ref || typeof ref !== "object") { return false } const record = ref as Record @@ -31,7 +31,7 @@ export function isAtomicChatModelSelected(config: any): boolean { return true } const modes = config?.model - if (!modes || typeof modes !== 'object' || Array.isArray(modes)) { + if (!modes || typeof modes !== "object" || Array.isArray(modes)) { return false } for (const value of Object.values(modes)) { @@ -48,8 +48,6 @@ export function isAtomicChatModelSelected(config: any): boolean { */ export function shouldProbeAtomicChat(config: any): boolean { return ( - hasAtomicChatProviderSection(config) || - isAtomicChatAutoDetectEnabled(config) || - isAtomicChatModelSelected(config) + hasAtomicChatProviderSection(config) || isAtomicChatAutoDetectEnabled(config) || isAtomicChatModelSelected(config) ) } diff --git a/packages/plugin-atomic-chat/src/utils/validation/index.ts b/packages/plugin-atomic-chat/src/utils/validation/index.ts index 69af870d5ee..8835a01855b 100644 --- a/packages/plugin-atomic-chat/src/utils/validation/index.ts +++ b/packages/plugin-atomic-chat/src/utils/validation/index.ts @@ -1,5 +1,5 @@ -export type { ValidationResult } from './validation-result' -export { validateConfig } from './validate-config' -export { validateHookInput } from './validate-hook-input' -export { isPluginHookInput, isAtomicChatProvider, isValidModel } from './type-guards' -export { safeAsyncOperation } from './safe-operations' +export type { ValidationResult } from "./validation-result" +export { validateConfig } from "./validate-config" +export { validateHookInput } from "./validate-hook-input" +export { isPluginHookInput, isAtomicChatProvider, isValidModel } from "./type-guards" +export { safeAsyncOperation } from "./safe-operations" diff --git a/packages/plugin-atomic-chat/src/utils/validation/safe-operations.ts b/packages/plugin-atomic-chat/src/utils/validation/safe-operations.ts index 0891cc0a1c2..016e4cf94a0 100644 --- a/packages/plugin-atomic-chat/src/utils/validation/safe-operations.ts +++ b/packages/plugin-atomic-chat/src/utils/validation/safe-operations.ts @@ -1,7 +1,7 @@ export async function safeAsyncOperation( operation: () => Promise, fallback?: T, - onError?: (error: Error) => void + onError?: (error: Error) => void, ): Promise { try { return await operation() diff --git a/packages/plugin-atomic-chat/src/utils/validation/type-guards.ts b/packages/plugin-atomic-chat/src/utils/validation/type-guards.ts index 5bfc29598a3..d6cc48efe3d 100644 --- a/packages/plugin-atomic-chat/src/utils/validation/type-guards.ts +++ b/packages/plugin-atomic-chat/src/utils/validation/type-guards.ts @@ -1,4 +1,4 @@ -import { ATOMIC_CHAT_PROVIDER_KEY } from '../../constants' +import { ATOMIC_CHAT_PROVIDER_KEY } from "../../constants" export function isPluginHookInput(input: any): input is { sessionID?: string @@ -8,18 +8,13 @@ export function isPluginHookInput(input: any): input is { message?: any event?: any } { - return input && typeof input === 'object' + return input && typeof input === "object" } export function isAtomicChatProvider(provider: any): boolean { - return ( - provider && - typeof provider === 'object' && - provider.info && - provider.info.id === ATOMIC_CHAT_PROVIDER_KEY - ) + return provider && typeof provider === "object" && provider.info && provider.info.id === ATOMIC_CHAT_PROVIDER_KEY } export function isValidModel(model: any): model is { id: string; [key: string]: any } { - return model && typeof model === 'object' && typeof model.id === 'string' && model.id.length > 0 + return model && typeof model === "object" && typeof model.id === "string" && model.id.length > 0 } diff --git a/packages/plugin-atomic-chat/src/utils/validation/validate-config.ts b/packages/plugin-atomic-chat/src/utils/validation/validate-config.ts index b94e2335117..52ab23e87d5 100644 --- a/packages/plugin-atomic-chat/src/utils/validation/validate-config.ts +++ b/packages/plugin-atomic-chat/src/utils/validation/validate-config.ts @@ -1,41 +1,41 @@ -import type { ValidationResult } from './validation-result' -import { ATOMIC_CHAT_PROVIDER_KEY } from '../../constants' +import type { ValidationResult } from "./validation-result" +import { ATOMIC_CHAT_PROVIDER_KEY } from "../../constants" export function validateConfig(config: any): ValidationResult { const errors: string[] = [] const warnings: string[] = [] - if (!config || typeof config !== 'object') { - errors.push('Config must be an object') + if (!config || typeof config !== "object") { + errors.push("Config must be an object") return { isValid: false, errors, warnings } } - if (config.provider && typeof config.provider === 'object') { + if (config.provider && typeof config.provider === "object") { const atomic = config.provider[ATOMIC_CHAT_PROVIDER_KEY] if (atomic) { if (!atomic.npm) { - atomic.npm = '@ai-sdk/openai-compatible' + atomic.npm = "@ai-sdk/openai-compatible" warnings.push(`Atomic Chat provider missing npm field, auto-set to @ai-sdk/openai-compatible`) } if (!atomic.name) { - atomic.name = 'Atomic Chat (local)' + atomic.name = "Atomic Chat (local)" warnings.push('Atomic Chat provider missing name field, auto-set to "Atomic Chat (local)"') } if (!atomic.options) { atomic.options = {} - warnings.push('Atomic Chat provider missing options field, auto-created empty options') + warnings.push("Atomic Chat provider missing options field, auto-created empty options") } else { if (!atomic.options.baseURL) { - warnings.push('Atomic Chat provider missing baseURL, will use default') - } else if (typeof atomic.options.baseURL !== 'string') { - errors.push('Atomic Chat provider baseURL must be a string') + warnings.push("Atomic Chat provider missing baseURL, will use default") + } else if (typeof atomic.options.baseURL !== "string") { + errors.push("Atomic Chat provider baseURL must be a string") } else if (!isValidURL(atomic.options.baseURL)) { - warnings.push('Atomic Chat provider baseURL may be invalid') + warnings.push("Atomic Chat provider baseURL may be invalid") } } - if (atomic.models && typeof atomic.models !== 'object') { - errors.push('Atomic Chat provider models must be an object') + if (atomic.models && typeof atomic.models !== "object") { + errors.push("Atomic Chat provider models must be an object") } } } diff --git a/packages/plugin-atomic-chat/src/utils/validation/validate-hook-input.ts b/packages/plugin-atomic-chat/src/utils/validation/validate-hook-input.ts index f659e507343..6610c26c442 100644 --- a/packages/plugin-atomic-chat/src/utils/validation/validate-hook-input.ts +++ b/packages/plugin-atomic-chat/src/utils/validation/validate-hook-input.ts @@ -1,40 +1,40 @@ -import type { ValidationResult } from './validation-result' +import type { ValidationResult } from "./validation-result" export function validateHookInput(hookName: string, input: any): ValidationResult { const errors: string[] = [] const warnings: string[] = [] - if (!input || typeof input !== 'object') { + if (!input || typeof input !== "object") { errors.push(`${hookName}: Input must be an object`) return { isValid: false, errors, warnings } } switch (hookName) { - case 'chat.params': - if (!input.sessionID || typeof input.sessionID !== 'string') { - errors.push('chat.params: sessionID is required and must be a string') + case "chat.params": + if (!input.sessionID || typeof input.sessionID !== "string") { + errors.push("chat.params: sessionID is required and must be a string") } - if (!input.model || typeof input.model !== 'object') { - errors.push('chat.params: model is required and must be an object') + if (!input.model || typeof input.model !== "object") { + errors.push("chat.params: model is required and must be an object") } else { - if (!input.model.id || typeof input.model.id !== 'string') { - errors.push('chat.params: model.id is required and must be a string') + if (!input.model.id || typeof input.model.id !== "string") { + errors.push("chat.params: model.id is required and must be a string") } } - if (!input.provider || typeof input.provider !== 'object') { - errors.push('chat.params: provider is required and must be an object') + if (!input.provider || typeof input.provider !== "object") { + errors.push("chat.params: provider is required and must be an object") } else { if (!input.provider.info || !input.provider.info.id) { - warnings.push('chat.params: provider.info.id is missing') + warnings.push("chat.params: provider.info.id is missing") } } break - case 'event': - if (!input.event || typeof input.event !== 'object') { - errors.push('event: event is required and must be an object') + case "event": + if (!input.event || typeof input.event !== "object") { + errors.push("event: event is required and must be an object") } else if (!input.event.type) { - warnings.push('event: event.type is missing') + warnings.push("event: event.type is missing") } break } diff --git a/packages/plugin-atomic-chat/test/plugin.test.ts b/packages/plugin-atomic-chat/test/plugin.test.ts index a5411df6707..61d91f0569b 100644 --- a/packages/plugin-atomic-chat/test/plugin.test.ts +++ b/packages/plugin-atomic-chat/test/plugin.test.ts @@ -1,7 +1,7 @@ -import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest' -import { AtomicChatPlugin } from '../src/index' -import { ATOMIC_CHAT_PROVIDER_KEY } from '../src/constants' -import { sharedModelStatusCache } from '../src/cache/shared-model-status-cache' +import { describe, it, expect, beforeEach, vi, afterEach } from "vitest" +import { AtomicChatPlugin } from "../src/index" +import { ATOMIC_CHAT_PROVIDER_KEY } from "../src/constants" +import { sharedModelStatusCache } from "../src/cache/shared-model-status-cache" const mockFetch = vi.fn() global.fetch = mockFetch @@ -14,7 +14,7 @@ if (!global.AbortSignal.timeout) { }) } -describe('AtomicChatPlugin', () => { +describe("AtomicChatPlugin", () => { let mockClient: any let pluginHooks: any @@ -29,14 +29,14 @@ describe('AtomicChatPlugin', () => { const mockInput: any = { client: mockClient, project: { - id: 'test-project', - name: 'test', - path: '/tmp', - worktree: '', + id: "test-project", + name: "test", + path: "/tmp", + worktree: "", time: { created: Date.now() }, }, - directory: '/tmp', - worktree: '', + directory: "/tmp", + worktree: "", $: vi.fn(), } pluginHooks = await AtomicChatPlugin(mockInput) @@ -46,49 +46,49 @@ describe('AtomicChatPlugin', () => { vi.restoreAllMocks() }) - it('initializes hooks', async () => { + it("initializes hooks", async () => { const mockInput: any = { client: mockClient, project: { - id: 'test-project', - name: 'test', - path: '/tmp', - worktree: '', + id: "test-project", + name: "test", + path: "/tmp", + worktree: "", time: { created: Date.now() }, }, - directory: '/tmp', - worktree: '', + directory: "/tmp", + worktree: "", $: vi.fn(), } const hooks = await AtomicChatPlugin(mockInput) - expect(hooks.config).toBeTypeOf('function') - expect(hooks.event).toBeTypeOf('function') - expect(hooks['chat.params']).toBeTypeOf('function') + expect(hooks.config).toBeTypeOf("function") + expect(hooks.event).toBeTypeOf("function") + expect(hooks["chat.params"]).toBeTypeOf("function") }) - it('registers optional local-server auth (no API key required)', async () => { + it("registers optional local-server auth (no API key required)", async () => { expect(pluginHooks.auth?.provider).toBe(ATOMIC_CHAT_PROVIDER_KEY) - expect(pluginHooks.auth?.methods[0]?.type).toBe('api') - expect(pluginHooks.auth?.methods[0]?.label).toBe('Local server') + expect(pluginHooks.auth?.methods[0]?.type).toBe("api") + expect(pluginHooks.auth?.methods[0]?.label).toBe("Local server") }) - it('handles invalid client', async () => { - const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) + it("handles invalid client", async () => { + const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {}) const hooks = await AtomicChatPlugin({ client: null } as any) - expect(hooks.config).toBeTypeOf('function') - expect(consoleSpy).toHaveBeenCalledWith('[@kilocode/plugin-atomic-chat] Invalid client provided to plugin') + expect(hooks.config).toBeTypeOf("function") + expect(consoleSpy).toHaveBeenCalledWith("[@kilocode/plugin-atomic-chat] Invalid client provided to plugin") consoleSpy.mockRestore() }) - describe('config hook', () => { - it('rejects invalid config', async () => { - const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) + describe("config hook", () => { + it("rejects invalid config", async () => { + const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {}) await pluginHooks.config(null) expect(consoleSpy).toHaveBeenCalled() consoleSpy.mockRestore() }) - it('does not probe localhost when Atomic Chat is not configured', async () => { + it("does not probe localhost when Atomic Chat is not configured", async () => { const config: any = {} await pluginHooks.config(config) @@ -96,11 +96,11 @@ describe('AtomicChatPlugin', () => { expect(config.provider?.[ATOMIC_CHAT_PROVIDER_KEY]).toBeUndefined() }) - it('auto-detects only when atomicChat.autoDetect is enabled', async () => { + it("auto-detects only when atomicChat.autoDetect is enabled", async () => { mockFetch.mockResolvedValue({ ok: true, json: async () => ({ - data: [{ id: 'm1', object: 'model', created: 1, owned_by: 'local' }], + data: [{ id: "m1", object: "model", created: 1, owned_by: "local" }], }), }) @@ -109,25 +109,25 @@ describe('AtomicChatPlugin', () => { expect(mockFetch).toHaveBeenCalled() expect(config.provider?.[ATOMIC_CHAT_PROVIDER_KEY]).toBeDefined() - expect(config.provider[ATOMIC_CHAT_PROVIDER_KEY].options.baseURL).toBe('http://127.0.0.1:1337/v1') + expect(config.provider[ATOMIC_CHAT_PROVIDER_KEY].options.baseURL).toBe("http://127.0.0.1:1337/v1") }) - it('merges discovered models', async () => { + it("merges discovered models", async () => { mockFetch.mockResolvedValue({ ok: true, json: async () => ({ - data: [{ id: 'new-model', object: 'model', created: 1, owned_by: 'local' }], + data: [{ id: "new-model", object: "model", created: 1, owned_by: "local" }], }), }) const config: any = { provider: { [ATOMIC_CHAT_PROVIDER_KEY]: { - npm: '@ai-sdk/openai-compatible', - name: 'Atomic Chat (local)', - options: { baseURL: 'http://127.0.0.1:1337/v1' }, + npm: "@ai-sdk/openai-compatible", + name: "Atomic Chat (local)", + options: { baseURL: "http://127.0.0.1:1337/v1" }, models: { - 'existing-model': { name: 'Existing Model' }, + "existing-model": { name: "Existing Model" }, }, }, }, @@ -136,23 +136,23 @@ describe('AtomicChatPlugin', () => { await pluginHooks.config(config) expect(config.provider[ATOMIC_CHAT_PROVIDER_KEY].models).toEqual({ - 'existing-model': { name: 'Existing Model' }, - 'new-model': expect.objectContaining({ - id: 'new-model', - name: 'New Model', + "existing-model": { name: "Existing Model" }, + "new-model": expect.objectContaining({ + id: "new-model", + name: "New Model", }), }) }) - it('handles offline API', async () => { - mockFetch.mockRejectedValue(new Error('Connection refused')) - const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) + it("handles offline API", async () => { + mockFetch.mockRejectedValue(new Error("Connection refused")) + const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {}) const config: any = { provider: { [ATOMIC_CHAT_PROVIDER_KEY]: { - npm: '@ai-sdk/openai-compatible', - name: 'Atomic Chat (local)', - options: { baseURL: 'http://127.0.0.1:1337/v1' }, + npm: "@ai-sdk/openai-compatible", + name: "Atomic Chat (local)", + options: { baseURL: "http://127.0.0.1:1337/v1" }, }, }, } @@ -162,89 +162,89 @@ describe('AtomicChatPlugin', () => { }) }) - describe('event hook', () => { - it('validates event', async () => { - const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) + describe("event hook", () => { + it("validates event", async () => { + const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {}) await pluginHooks.event({ event: null }) expect(consoleSpy).toHaveBeenCalled() consoleSpy.mockRestore() }) - it('accepts session events', async () => { - await pluginHooks.event({ event: { type: 'session.created' } }) + it("accepts session events", async () => { + await pluginHooks.event({ event: { type: "session.created" } }) expect(true).toBe(true) }) }) - describe('chat.params hook', () => { - it('rejects invalid input', async () => { - const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) - await pluginHooks['chat.params'](null, {}) + describe("chat.params hook", () => { + it("rejects invalid input", async () => { + const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {}) + await pluginHooks["chat.params"](null, {}) expect(consoleSpy).toHaveBeenCalled() consoleSpy.mockRestore() }) - it('skips other providers', async () => { + it("skips other providers", async () => { const output: any = {} - await pluginHooks['chat.params']( + await pluginHooks["chat.params"]( { - model: { id: 'x' }, - provider: { info: { id: 'anthropic' } }, + model: { id: "x" }, + provider: { info: { id: "anthropic" } }, }, - output + output, ) expect(output).toEqual({}) expect(mockClient.tui.showToast).not.toHaveBeenCalled() }) - it('validates model availability', async () => { + it("validates model availability", async () => { mockFetch.mockResolvedValue({ ok: true, json: async () => ({ - data: [{ id: 'test-model', object: 'model', created: 1, owned_by: 'local' }], + data: [{ id: "test-model", object: "model", created: 1, owned_by: "local" }], }), }) const output: any = {} - await pluginHooks['chat.params']( + await pluginHooks["chat.params"]( { - sessionID: 's1', - model: { id: 'test-model' }, + sessionID: "s1", + model: { id: "test-model" }, provider: { info: { id: ATOMIC_CHAT_PROVIDER_KEY }, - options: { baseURL: 'http://127.0.0.1:1337/v1' }, + options: { baseURL: "http://127.0.0.1:1337/v1" }, }, }, - output + output, ) expect(mockClient.tui.showToast).not.toHaveBeenCalled() expect(output.options?.atomicChatValidation).toEqual( - expect.objectContaining({ status: 'success', model: 'test-model' }) + expect.objectContaining({ status: "success", model: "test-model" }), ) }) - it('handles missing model', async () => { + it("handles missing model", async () => { mockFetch.mockResolvedValue({ ok: true, json: async () => ({ data: [] }), }) const output: any = {} - await pluginHooks['chat.params']( + await pluginHooks["chat.params"]( { - sessionID: 's1', - model: { id: 'missing' }, + sessionID: "s1", + model: { id: "missing" }, provider: { info: { id: ATOMIC_CHAT_PROVIDER_KEY }, - options: { baseURL: 'http://127.0.0.1:1337/v1' }, + options: { baseURL: "http://127.0.0.1:1337/v1" }, }, }, - output + output, ) expect(output.options?.atomicChatValidation).toEqual( - expect.objectContaining({ status: 'error', model: 'missing' }) + expect.objectContaining({ status: "error", model: "missing" }), ) }) }) diff --git a/packages/plugin-atomic-chat/test/should-probe-atomic-chat.test.ts b/packages/plugin-atomic-chat/test/should-probe-atomic-chat.test.ts index 106f31d64b2..c11e948d870 100644 --- a/packages/plugin-atomic-chat/test/should-probe-atomic-chat.test.ts +++ b/packages/plugin-atomic-chat/test/should-probe-atomic-chat.test.ts @@ -1,45 +1,45 @@ -import { describe, it, expect } from 'vitest' +import { describe, it, expect } from "vitest" import { shouldProbeAtomicChat, isAtomicChatAutoDetectEnabled, hasAtomicChatProviderSection, -} from '../src/utils/should-probe-atomic-chat' -import { ATOMIC_CHAT_PROVIDER_KEY } from '../src/constants' +} from "../src/utils/should-probe-atomic-chat" +import { ATOMIC_CHAT_PROVIDER_KEY } from "../src/constants" -describe('shouldProbeAtomicChat', () => { - it('returns false for empty config (no localhost HTTP)', () => { +describe("shouldProbeAtomicChat", () => { + it("returns false for empty config (no localhost HTTP)", () => { expect(shouldProbeAtomicChat({})).toBe(false) expect(shouldProbeAtomicChat({ provider: {} })).toBe(false) }) - it('returns true when provider.atomic-chat is configured', () => { + it("returns true when provider.atomic-chat is configured", () => { expect( shouldProbeAtomicChat({ - provider: { [ATOMIC_CHAT_PROVIDER_KEY]: { options: { baseURL: 'http://127.0.0.1:1337/v1' } } }, - }) + provider: { [ATOMIC_CHAT_PROVIDER_KEY]: { options: { baseURL: "http://127.0.0.1:1337/v1" } } }, + }), ).toBe(true) }) - it('returns true when atomicChat.autoDetect is enabled', () => { + it("returns true when atomicChat.autoDetect is enabled", () => { expect(shouldProbeAtomicChat({ atomicChat: { autoDetect: true } })).toBe(true) expect(isAtomicChatAutoDetectEnabled({ atomicChat: { autoDetect: true } })).toBe(true) }) - it('returns true when default model uses atomic-chat', () => { - expect(shouldProbeAtomicChat({ model: 'atomic-chat/gemma-4-E4B-it-IQ4_XS' })).toBe(true) + it("returns true when default model uses atomic-chat", () => { + expect(shouldProbeAtomicChat({ model: "atomic-chat/gemma-4-E4B-it-IQ4_XS" })).toBe(true) }) - it('returns true when per-agent model uses atomic-chat', () => { + it("returns true when per-agent model uses atomic-chat", () => { expect( shouldProbeAtomicChat({ model: { - code: { providerID: ATOMIC_CHAT_PROVIDER_KEY, modelID: 'gemma-4-E4B-it-IQ4_XS' }, + code: { providerID: ATOMIC_CHAT_PROVIDER_KEY, modelID: "gemma-4-E4B-it-IQ4_XS" }, }, - }) + }), ).toBe(true) }) - it('hasAtomicChatProviderSection reflects provider block only', () => { + it("hasAtomicChatProviderSection reflects provider block only", () => { expect(hasAtomicChatProviderSection({})).toBe(false) expect(hasAtomicChatProviderSection({ provider: { [ATOMIC_CHAT_PROVIDER_KEY]: {} } })).toBe(true) }) diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 195c68581b7..0ed2ad33df4 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -22,9 +22,9 @@ "zod": "catalog:" }, "peerDependencies": { - "@opentui/core": ">=0.2.6", - "@opentui/solid": ">=0.2.6", - "@opentui/keymap": ">=0.2.6" + "@opentui/core": ">=0.2.10", + "@opentui/solid": ">=0.2.10", + "@opentui/keymap": ">=0.2.10" }, "peerDependenciesMeta": { "@opentui/core": { diff --git a/packages/plugin/src/index.ts b/packages/plugin/src/index.ts index f7c3a1d60c3..8248c3e7056 100644 --- a/packages/plugin/src/index.ts +++ b/packages/plugin/src/index.ts @@ -8,10 +8,9 @@ import type { UserMessage, Message, Part, - Auth, Config as SDKConfig, } from "@kilocode/sdk" -import type { Provider as ProviderV2, Model as ModelV2 } from "@kilocode/sdk/v2" +import type { Provider as ProviderV2, Model as ModelV2, Auth } from "@kilocode/sdk/v2" import type { BunShell } from "./shell.js" import { type ToolDefinition } from "./tool.js" @@ -153,6 +152,7 @@ export type AuthHook = { type: "success" key: string provider?: string + metadata?: Record } | { type: "failed" @@ -177,7 +177,7 @@ export type AuthOAuthResult = { url: string; instructions: string } & ( accountId?: string enterpriseUrl?: string } - | { key: string } + | { key: string; metadata?: Record } )) | { type: "failed" @@ -198,7 +198,7 @@ export type AuthOAuthResult = { url: string; instructions: string } & ( accountId?: string enterpriseUrl?: string } - | { key: string } + | { key: string; metadata?: Record } )) | { type: "failed" diff --git a/packages/plugin/src/tool.ts b/packages/plugin/src/tool.ts index 3105bf534be..b8a634c7967 100644 --- a/packages/plugin/src/tool.ts +++ b/packages/plugin/src/tool.ts @@ -27,7 +27,21 @@ type AskInput = { metadata: { [key: string]: any } } -export type ToolResult = string | { output: string; metadata?: { [key: string]: any } } +export type ToolAttachment = { + type: "file" + mime: string + url: string + filename?: string +} + +export type ToolResult = + | string + | { + title?: string + output: string + metadata?: { [key: string]: any } + attachments?: ToolAttachment[] + } export function tool(input: { description: string diff --git a/packages/plugin/src/tui.ts b/packages/plugin/src/tui.ts index 78a4cc9a94c..46fae98bd8e 100644 --- a/packages/plugin/src/tui.ts +++ b/packages/plugin/src/tui.ts @@ -11,6 +11,7 @@ import type { Provider, PermissionRequest, QuestionRequest, + Session, SessionStatus, BackgroundProcessInfo, TextPart, @@ -226,6 +227,76 @@ export type TuiToast = { duration?: number } +export type TuiAttentionWhen = "always" | "focused" | "blurred" + +export const TuiAttentionSoundNames = ["default", "question", "permission", "error", "done", "subagent_done"] as const +export type TuiAttentionSoundName = (typeof TuiAttentionSoundNames)[number] + +export type TuiAttentionSound = + | boolean + | { + name?: TuiAttentionSoundName + volume?: number + when?: TuiAttentionWhen + } + +export type TuiAttentionNotification = + | boolean + | { + when?: TuiAttentionWhen + } + +export type TuiAttentionSoundPack = { + id: string + name?: string + sounds: Partial> +} + +export type TuiAttentionSoundPackInfo = { + id: string + name?: string + active: boolean + builtin: boolean +} + +export type TuiAttentionSoundboardActivateOptions = { + persist?: boolean +} + +export type TuiAttentionSoundboard = { + registerPack(pack: TuiAttentionSoundPack): () => void + activate(id: string, options?: TuiAttentionSoundboardActivateOptions): boolean + current(): string + list(): ReadonlyArray +} + +export type TuiAttentionNotifyInput = { + title?: string + message: string + notification?: TuiAttentionNotification + sound?: TuiAttentionSound +} + +export type TuiAttentionNotifySkipReason = + | "attention_disabled" + | "empty_message" + | "blurred" + | "focused" + | "focus_unknown" + | "renderer_destroyed" + +export type TuiAttentionNotifyResult = { + ok: boolean + notification: boolean + sound: boolean + skipped?: TuiAttentionNotifySkipReason +} + +export type TuiAttention = { + notify(input: TuiAttentionNotifyInput): Promise + soundboard: TuiAttentionSoundboard +} + export type TuiThemeCurrent = { readonly primary: RGBA readonly secondary: RGBA @@ -311,6 +382,7 @@ export type TuiState = { readonly vcs: { branch?: string } | undefined session: { count: () => number + get: (sessionID: string) => Session | undefined diff: (sessionID: string) => ReadonlyArray todo: (sessionID: string) => ReadonlyArray processes: (sessionID: string) => ReadonlyArray @@ -333,9 +405,19 @@ type TuiBindingLookupView = { omit: (name: string, commands: readonly string[]) => Binding[] } +type TuiAttentionConfigView = { + enabled: boolean + notifications: boolean + sound: boolean + volume: number + sound_pack: string + sounds: Partial> +} + type TuiConfigView = Pick & NonNullable & { leader_timeout: number + attention: TuiAttentionConfigView plugin_enabled?: Record keybinds: TuiBindingLookupView } @@ -504,6 +586,7 @@ export type TuiWorkspace = { export type TuiPluginApi = { app: TuiApp + attention: TuiAttention /** * Legacy `api.command` API kept so v1 plugins can initialize. Remove in v2. * diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index 420146ae36b..065541638e4 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -1666,6 +1666,9 @@ export type OAuth = { export type ApiAuth = { type: "api" key: string + metadata?: { + [key: string]: string + } } export type WellKnownAuth = { diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index 5e3fd7db189..97ce46a04e3 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -287,6 +287,10 @@ import type { TuiSelectSessionResponses, TuiShowToastResponses, TuiSubmitPromptResponses, + V2ModelListResponses, + V2ProviderGetErrors, + V2ProviderGetResponses, + V2ProviderListResponses, V2SessionCompactResponses, V2SessionContextResponses, V2SessionListErrors, @@ -310,6 +314,7 @@ import type { WorktreeDiffResponses, WorktreeDiffSummaryErrors, WorktreeDiffSummaryResponses, + WorktreeListErrors, WorktreeListResponses, WorktreeRemoveErrors, WorktreeRemoveInput, @@ -1662,7 +1667,7 @@ export class Worktree extends HeyApiClient { }, ], ) - return (options?.client ?? this.client).get({ + return (options?.client ?? this.client).get({ url: "/experimental/worktree", ...options, ...params, @@ -5031,11 +5036,102 @@ export class Session3 extends HeyApiClient { } } +export class Model extends HeyApiClient { + /** + * List v2 models + * + * Retrieve available v2 models ordered by release date. + */ + public list( + parameters?: { + instance?: { + directory?: string + workspace?: string + } + }, + options?: Options, + ) { + const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "instance" }] }]) + return (options?.client ?? this.client).get({ + url: "/api/model", + ...options, + ...params, + }) + } +} + +export class Provider2 extends HeyApiClient { + /** + * List v2 providers + * + * Retrieve active v2 AI providers so clients can show provider availability and configuration. + */ + public list( + parameters?: { + instance?: { + directory?: string + workspace?: string + } + }, + options?: Options, + ) { + const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "instance" }] }]) + return (options?.client ?? this.client).get({ + url: "/api/provider", + ...options, + ...params, + }) + } + + /** + * Get v2 provider + * + * Retrieve a single v2 AI provider so clients can inspect its availability and endpoint settings. + */ + public get( + parameters: { + providerID: string + instance?: { + directory?: string + workspace?: string + } + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "path", key: "providerID" }, + { in: "query", key: "instance" }, + ], + }, + ], + ) + return (options?.client ?? this.client).get({ + url: "/api/provider/{providerID}", + ...options, + ...params, + }) + } +} + export class V2 extends HeyApiClient { private _session?: Session3 get session(): Session3 { return (this._session ??= new Session3({ client: this.client })) } + + private _model?: Model + get model(): Model { + return (this._model ??= new Model({ client: this.client })) + } + + private _provider?: Provider2 + get provider(): Provider2 { + return (this._provider ??= new Provider2({ client: this.client })) + } } export class Control extends HeyApiClient { diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 90de7fa3010..5d2a7d48ff4 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -37,8 +37,6 @@ export type Event = | EventSessionTurnClose | EventSessionDiff | EventSessionError - | EventInstallationUpdated - | EventInstallationUpdateAvailable | EventTodoUpdated | EventSessionStatus | EventSessionIdle @@ -59,39 +57,8 @@ export type Event = | EventPtyUpdated | EventPtyExited | EventPtyDeleted - | EventMessageUpdated - | EventMessageRemoved - | EventMessagePartUpdated - | EventMessagePartRemoved - | EventSessionCreated - | EventSessionUpdated - | EventSessionDeleted - | EventSessionNextAgentSwitched - | EventSessionNextModelSwitched - | EventSessionNextPrompted - | EventSessionNextSynthetic - | EventSessionNextShellStarted - | EventSessionNextShellEnded - | EventSessionNextStepStarted - | EventSessionNextStepEnded - | EventSessionNextStepFailed - | EventSessionNextTextStarted - | EventSessionNextTextDelta - | EventSessionNextTextEnded - | EventSessionNextReasoningStarted - | EventSessionNextReasoningDelta - | EventSessionNextReasoningEnded - | EventSessionNextToolInputStarted - | EventSessionNextToolInputDelta - | EventSessionNextToolInputEnded - | EventSessionNextToolCalled - | EventSessionNextToolProgress - | EventSessionNextToolSuccess - | EventSessionNextToolFailed - | EventSessionNextRetried - | EventSessionNextCompactionStarted - | EventSessionNextCompactionDelta - | EventSessionNextCompactionEnded + | EventInstallationUpdated + | EventInstallationUpdateAvailable export type OAuth = { type: "oauth" @@ -844,6 +811,16 @@ export type Session = { files: number diffs?: Array } + cost?: number + tokens?: { + input: number + output: number + reasoning: number + cache: { + read: number + write: number + } + } share?: { url: string } @@ -874,6 +851,7 @@ export type Prompt = { text: string files?: Array agents?: Array + references?: Array } export type GlobalEvent = { @@ -913,8 +891,6 @@ export type GlobalEvent = { | EventSessionTurnClose | EventSessionDiff | EventSessionError - | EventInstallationUpdated - | EventInstallationUpdateAvailable | EventTodoUpdated | EventSessionStatus | EventSessionIdle @@ -935,39 +911,8 @@ export type GlobalEvent = { | EventPtyUpdated | EventPtyExited | EventPtyDeleted - | EventMessageUpdated - | EventMessageRemoved - | EventMessagePartUpdated - | EventMessagePartRemoved - | EventSessionCreated - | EventSessionUpdated - | EventSessionDeleted - | EventSessionNextAgentSwitched - | EventSessionNextModelSwitched - | EventSessionNextPrompted - | EventSessionNextSynthetic - | EventSessionNextShellStarted - | EventSessionNextShellEnded - | EventSessionNextStepStarted - | EventSessionNextStepEnded - | EventSessionNextStepFailed - | EventSessionNextTextStarted - | EventSessionNextTextDelta - | EventSessionNextTextEnded - | EventSessionNextReasoningStarted - | EventSessionNextReasoningDelta - | EventSessionNextReasoningEnded - | EventSessionNextToolInputStarted - | EventSessionNextToolInputDelta - | EventSessionNextToolInputEnded - | EventSessionNextToolCalled - | EventSessionNextToolProgress - | EventSessionNextToolSuccess - | EventSessionNextToolFailed - | EventSessionNextRetried - | EventSessionNextCompactionStarted - | EventSessionNextCompactionDelta - | EventSessionNextCompactionEnded + | EventInstallationUpdated + | EventInstallationUpdateAvailable | SyncEventMessageUpdated | SyncEventMessageRemoved | SyncEventMessagePartUpdated @@ -1126,7 +1071,6 @@ export type PermissionConfig = question?: PermissionActionConfig webfetch?: PermissionActionConfig websearch?: PermissionActionConfig - codesearch?: PermissionActionConfig repo_clone?: PermissionRuleConfig repo_overview?: PermissionRuleConfig lsp?: PermissionRuleConfig @@ -1525,6 +1469,18 @@ export type Model = { read: number write: number } + tiers?: Array<{ + input: number + output: number + cache: { + read: number + write: number + } + tier: { + type: "context" + size: number + } + }> experimentalOver200K?: { input: number output: number @@ -1596,6 +1552,20 @@ export type ToolList = Array export type ToolIds = Array +export type WorktreeError = { + name: + | "WorktreeNotGitError" + | "WorktreeNameGenerationFailedError" + | "WorktreeCreateFailedError" + | "WorktreeStartCommandFailedError" + | "WorktreeRemoveFailedError" + | "WorktreeResetFailedError" + | "WorktreeListFailedError" + data: { + message: string + } +} + export type WorktreeCreateInput = { name?: string /** @@ -1606,7 +1576,7 @@ export type WorktreeCreateInput = { export type Worktree = { name: string - branch: string + branch?: string directory: string } @@ -1652,6 +1622,16 @@ export type GlobalSession = { files: number diffs?: Array } + cost?: number + tokens?: { + input: number + output: number + reasoning: number + cache: { + read: number + write: number + } + } share?: { url: string } @@ -1901,6 +1881,21 @@ export type ProviderAuthAuthorization = { instructions: string } +export type ProviderAuthError1 = { + name: + | "BadRequest" + | "ProviderAuthOauthMissing" + | "ProviderAuthOauthCodeMissing" + | "ProviderAuthOauthCallbackFailed" + | "ProviderAuthValidationFailed" + data: { + providerID?: string + field?: string + message?: string + kind?: string + } +} + export type TextPartInput = { id?: string type: "text" @@ -2020,9 +2015,9 @@ export type Workspace = { id: string type: string name: string - branch: string | null - directory: string | null - extra: unknown | null + branch?: string | null + directory?: string | null + extra?: unknown | null projectID: string timeUsed: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN" } @@ -2279,6 +2274,16 @@ export type SyncEventSessionUpdated = { files: number diffs?: Array } | null + cost?: number | null + tokens?: { + input: number + output: number + reasoning: number + cache: { + read: number + write: number + } + } | null share?: { url?: string | null } @@ -2963,22 +2968,6 @@ export type EventSessionError = { } } -export type EventInstallationUpdated = { - id: string - type: "installation.updated" - properties: { - version: string - } -} - -export type EventInstallationUpdateAvailable = { - id: string - type: "installation.update-available" - properties: { - version: string - } -} - export type EventTodoUpdated = { id: string type: "todo.updated" @@ -3113,7 +3102,7 @@ export type EventWorktreeReady = { type: "worktree.ready" properties: { name: string - branch: string + branch?: string } } @@ -3158,92 +3147,19 @@ export type EventPtyDeleted = { } } -export type EventMessageUpdated = { - id: string - type: "message.updated" - properties: { - sessionID: string - info: Message - } -} - -export type EventMessageRemoved = { - id: string - type: "message.removed" - properties: { - sessionID: string - messageID: string - } -} - -export type EventMessagePartUpdated = { - id: string - type: "message.part.updated" - properties: { - sessionID: string - part: Part - time: number - } -} - -export type EventMessagePartRemoved = { - id: string - type: "message.part.removed" - properties: { - sessionID: string - messageID: string - partID: string - } -} - -export type EventSessionCreated = { - id: string - type: "session.created" - properties: { - sessionID: string - info: Session - } -} - -export type EventSessionUpdated = { - id: string - type: "session.updated" - properties: { - sessionID: string - info: Session - } -} - -export type EventSessionDeleted = { - id: string - type: "session.deleted" - properties: { - sessionID: string - info: Session - } -} - -export type EventSessionNextAgentSwitched = { +export type EventInstallationUpdated = { id: string - type: "session.next.agent.switched" + type: "installation.updated" properties: { - timestamp: number - sessionID: string - agent: string + version: string } } -export type EventSessionNextModelSwitched = { +export type EventInstallationUpdateAvailable = { id: string - type: "session.next.model.switched" + type: "installation.update-available" properties: { - timestamp: number - sessionID: string - model: { - id: string - providerID: string - variant: string - } + version: string } } @@ -3266,83 +3182,16 @@ export type PromptAgentAttachment = { source?: PromptSource } -export type EventSessionNextPrompted = { - id: string - type: "session.next.prompted" - properties: { - timestamp: number - sessionID: string - prompt: Prompt - } -} - -export type EventSessionNextSynthetic = { - id: string - type: "session.next.synthetic" - properties: { - timestamp: number - sessionID: string - text: string - } -} - -export type EventSessionNextShellStarted = { - id: string - type: "session.next.shell.started" - properties: { - timestamp: number - sessionID: string - callID: string - command: string - } -} - -export type EventSessionNextShellEnded = { - id: string - type: "session.next.shell.ended" - properties: { - timestamp: number - sessionID: string - callID: string - output: string - } -} - -export type EventSessionNextStepStarted = { - id: string - type: "session.next.step.started" - properties: { - timestamp: number - sessionID: string - agent: string - model: { - id: string - providerID: string - variant: string - } - snapshot?: string - } -} - -export type EventSessionNextStepEnded = { - id: string - type: "session.next.step.ended" - properties: { - timestamp: number - sessionID: string - finish: string - cost: number - tokens: { - input: number - output: number - reasoning: number - cache: { - read: number - write: number - } - } - snapshot?: string - } +export type PromptReferenceAttachment = { + name: string + kind: "local" | "git" | "invalid" + uri?: string + repository?: string + branch?: string + target?: string + targetUri?: string + problem?: string + source?: PromptSource } export type SessionErrorUnknown = { @@ -3350,191 +3199,16 @@ export type SessionErrorUnknown = { message: string } -export type EventSessionNextStepFailed = { - id: string - type: "session.next.step.failed" - properties: { - timestamp: number - sessionID: string - error: SessionErrorUnknown - } -} - -export type EventSessionNextTextStarted = { - id: string - type: "session.next.text.started" - properties: { - timestamp: number - sessionID: string - } -} - -export type EventSessionNextTextDelta = { - id: string - type: "session.next.text.delta" - properties: { - timestamp: number - sessionID: string - delta: string - } -} - -export type EventSessionNextTextEnded = { - id: string - type: "session.next.text.ended" - properties: { - timestamp: number - sessionID: string - text: string - } -} - -export type EventSessionNextReasoningStarted = { - id: string - type: "session.next.reasoning.started" - properties: { - timestamp: number - sessionID: string - reasoningID: string - } -} - -export type EventSessionNextReasoningDelta = { - id: string - type: "session.next.reasoning.delta" - properties: { - timestamp: number - sessionID: string - reasoningID: string - delta: string - } -} - -export type EventSessionNextReasoningEnded = { - id: string - type: "session.next.reasoning.ended" - properties: { - timestamp: number - sessionID: string - reasoningID: string - text: string - } -} - -export type EventSessionNextToolInputStarted = { - id: string - type: "session.next.tool.input.started" - properties: { - timestamp: number - sessionID: string - callID: string - name: string - } -} - -export type EventSessionNextToolInputDelta = { - id: string - type: "session.next.tool.input.delta" - properties: { - timestamp: number - sessionID: string - callID: string - delta: string - } -} - -export type EventSessionNextToolInputEnded = { - id: string - type: "session.next.tool.input.ended" - properties: { - timestamp: number - sessionID: string - callID: string - text: string - } -} - -export type EventSessionNextToolCalled = { - id: string - type: "session.next.tool.called" - properties: { - timestamp: number - sessionID: string - callID: string - tool: string - input: { - [key: string]: unknown - } - provider: { - executed: boolean - metadata?: { - [key: string]: unknown - } - } - } -} - export type ToolTextContent = { type: "text" - text: string -} - -export type ToolFileContent = { - type: "file" - uri: string - mime: string - name?: string -} - -export type EventSessionNextToolProgress = { - id: string - type: "session.next.tool.progress" - properties: { - timestamp: number - sessionID: string - callID: string - structured: { - [key: string]: unknown - } - content: Array - } -} - -export type EventSessionNextToolSuccess = { - id: string - type: "session.next.tool.success" - properties: { - timestamp: number - sessionID: string - callID: string - structured: { - [key: string]: unknown - } - content: Array - provider: { - executed: boolean - metadata?: { - [key: string]: unknown - } - } - } + text: string } -export type EventSessionNextToolFailed = { - id: string - type: "session.next.tool.failed" - properties: { - timestamp: number - sessionID: string - callID: string - error: SessionErrorUnknown - provider: { - executed: boolean - metadata?: { - [key: string]: unknown - } - } - } +export type ToolFileContent = { + type: "file" + uri: string + mime: string + name?: string } export type SessionNextRetryError = { @@ -3550,48 +3224,6 @@ export type SessionNextRetryError = { } } -export type EventSessionNextRetried = { - id: string - type: "session.next.retried" - properties: { - timestamp: number - sessionID: string - attempt: number - error: SessionNextRetryError - } -} - -export type EventSessionNextCompactionStarted = { - id: string - type: "session.next.compaction.started" - properties: { - timestamp: number - sessionID: string - reason: "auto" | "manual" - } -} - -export type EventSessionNextCompactionDelta = { - id: string - type: "session.next.compaction.delta" - properties: { - timestamp: number - sessionID: string - text: string - } -} - -export type EventSessionNextCompactionEnded = { - id: string - type: "session.next.compaction.ended" - properties: { - timestamp: number - sessionID: string - text: string - include?: string - } -} - export type SessionInfo = { id: string parentID?: string @@ -3604,6 +3236,16 @@ export type SessionInfo = { providerID: string variant: string } + cost: number + tokens: { + input: number + output: number + reasoning: number + cache: { + read: number + write: number + } + } time: { created: number updated: number @@ -3653,6 +3295,7 @@ export type SessionMessageUser = { text: string files?: Array agents?: Array + references?: Array type: "user" } @@ -3816,6 +3459,171 @@ export type SessionMessage = | SessionMessageAssistant | SessionMessageCompaction +export type ModelV2Info = { + id: string + apiID: string + providerID: string + family?: string + name: string + endpoint: + | { + type: "unknown" + } + | { + type: "openai/responses" + url: string + websocket?: boolean + } + | { + type: "openai/completions" + url: string + reasoning?: + | { + type: "reasoning_content" + } + | { + type: "reasoning_details" + } + } + | { + type: "anthropic/messages" + url: string + } + | { + type: "aisdk" + package: string + url?: string + } + capabilities: { + tools: boolean + input: Array + output: Array + } + options: { + headers: { + [key: string]: string + } + body: { + [key: string]: unknown + } + aisdk: { + provider: { + [key: string]: unknown + } + request: { + [key: string]: unknown + } + } + variant?: string + } + variants: Array<{ + id: string + headers: { + [key: string]: string + } + body: { + [key: string]: unknown + } + aisdk: { + provider: { + [key: string]: unknown + } + request: { + [key: string]: unknown + } + } + }> + time: { + released: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN" + } + cost: Array<{ + tier?: { + type: "context" + size: number + } + input: number + output: number + cache: { + read: number + write: number + } + }> + status: "alpha" | "beta" | "deprecated" | "active" + enabled: boolean + limit: { + context: number + input?: number + output: number + } +} + +export type ProviderV2Info = { + id: string + name: string + enabled: + | false + | { + via: "env" + name: string + } + | { + via: "auth" + service: string + } + | { + via: "custom" + data: { + [key: string]: unknown + } + } + env: Array + endpoint: + | { + type: "unknown" + } + | { + type: "openai/responses" + url: string + websocket?: boolean + } + | { + type: "openai/completions" + url: string + reasoning?: + | { + type: "reasoning_content" + } + | { + type: "reasoning_details" + } + } + | { + type: "anthropic/messages" + url: string + } + | { + type: "aisdk" + package: string + url?: string + } + options: { + headers: { + [key: string]: string + } + body: { + [key: string]: unknown + } + aisdk: { + provider: { + [key: string]: unknown + } + request: { + [key: string]: unknown + } + } + } +} + export type EventTuiToastShow1 = { id: string type: "tui.toast.show" @@ -4333,9 +4141,9 @@ export type WorktreeRemoveData = { export type WorktreeRemoveErrors = { /** - * Bad request + * WorktreeError */ - 400: BadRequestError + 400: WorktreeError } export type WorktreeRemoveError = WorktreeRemoveErrors[keyof WorktreeRemoveErrors] @@ -4359,6 +4167,15 @@ export type WorktreeListData = { url: "/experimental/worktree" } +export type WorktreeListErrors = { + /** + * WorktreeError + */ + 400: WorktreeError +} + +export type WorktreeListError = WorktreeListErrors[keyof WorktreeListErrors] + export type WorktreeListResponses = { /** * List of worktree directories @@ -4380,9 +4197,9 @@ export type WorktreeCreateData = { export type WorktreeCreateErrors = { /** - * Bad request + * WorktreeError */ - 400: BadRequestError + 400: WorktreeError } export type WorktreeCreateError = WorktreeCreateErrors[keyof WorktreeCreateErrors] @@ -4408,9 +4225,9 @@ export type WorktreeResetData = { export type WorktreeResetErrors = { /** - * Bad request + * WorktreeError */ - 400: BadRequestError + 400: WorktreeError } export type WorktreeResetError = WorktreeResetErrors[keyof WorktreeResetErrors] @@ -5785,9 +5602,9 @@ export type ProviderOauthAuthorizeData = { export type ProviderOauthAuthorizeErrors = { /** - * Bad request + * ProviderAuthError */ - 400: BadRequestError + 400: ProviderAuthError1 } export type ProviderOauthAuthorizeError = ProviderOauthAuthorizeErrors[keyof ProviderOauthAuthorizeErrors] @@ -5821,9 +5638,9 @@ export type ProviderOauthCallbackData = { export type ProviderOauthCallbackErrors = { /** - * Bad request + * ProviderAuthError */ - 400: BadRequestError + 400: ProviderAuthError1 } export type ProviderOauthCallbackError = ProviderOauthCallbackErrors[keyof ProviderOauthCallbackErrors] @@ -6058,7 +5875,7 @@ export type SessionChildrenErrors = { */ 400: BadRequestError /** - * Not found + * NotFoundError */ 404: NotFoundError } @@ -6092,7 +5909,7 @@ export type SessionTodoErrors = { */ 400: BadRequestError /** - * Not found + * NotFoundError */ 404: NotFoundError } @@ -6209,7 +6026,7 @@ export type SessionPromptErrors = { */ 400: BadRequestError /** - * Not found + * NotFoundError */ 404: NotFoundError } @@ -6247,7 +6064,7 @@ export type SessionDeleteMessageErrors = { */ 400: BadRequestError /** - * Not found + * NotFoundError */ 404: NotFoundError } @@ -6354,10 +6171,6 @@ export type SessionAbortErrors = { * Bad request */ 400: BadRequestError - /** - * Not found - */ - 404: NotFoundError } export type SessionAbortError = SessionAbortErrors[keyof SessionAbortErrors] @@ -6393,7 +6206,7 @@ export type SessionInitErrors = { */ 400: BadRequestError /** - * Not found + * NotFoundError */ 404: NotFoundError } @@ -6555,7 +6368,7 @@ export type SessionPromptAsyncErrors = { */ 400: BadRequestError /** - * Not found + * NotFoundError */ 404: NotFoundError } @@ -6605,7 +6418,7 @@ export type SessionCommandErrors = { */ 400: BadRequestError /** - * Not found + * NotFoundError */ 404: NotFoundError } @@ -6650,7 +6463,7 @@ export type SessionShellErrors = { */ 400: BadRequestError /** - * Not found + * NotFoundError */ 404: NotFoundError } @@ -6690,7 +6503,7 @@ export type SessionRevertErrors = { */ 400: BadRequestError /** - * Not found + * NotFoundError */ 404: NotFoundError } @@ -6724,7 +6537,7 @@ export type SessionUnrevertErrors = { */ 400: BadRequestError /** - * Not found + * NotFoundError */ 404: NotFoundError } @@ -6761,7 +6574,7 @@ export type PermissionRespondErrors = { */ 400: BadRequestError /** - * Not found + * NotFoundError */ 404: NotFoundError } @@ -6797,7 +6610,7 @@ export type PartDeleteErrors = { */ 400: BadRequestError /** - * Not found + * NotFoundError */ 404: NotFoundError } @@ -6833,7 +6646,7 @@ export type PartUpdateErrors = { */ 400: BadRequestError /** - * Not found + * NotFoundError */ 404: NotFoundError } @@ -7162,6 +6975,80 @@ export type V2SessionMessagesResponses = { export type V2SessionMessagesResponse2 = V2SessionMessagesResponses[keyof V2SessionMessagesResponses] +export type V2ModelListData = { + body?: never + path?: never + query?: { + instance?: { + directory?: string + workspace?: string + } + } + url: "/api/model" +} + +export type V2ModelListResponses = { + /** + * Success + */ + 200: Array +} + +export type V2ModelListResponse = V2ModelListResponses[keyof V2ModelListResponses] + +export type V2ProviderListData = { + body?: never + path?: never + query?: { + instance?: { + directory?: string + workspace?: string + } + } + url: "/api/provider" +} + +export type V2ProviderListResponses = { + /** + * Success + */ + 200: Array +} + +export type V2ProviderListResponse = V2ProviderListResponses[keyof V2ProviderListResponses] + +export type V2ProviderGetData = { + body?: never + path: { + providerID: string + } + query?: { + instance?: { + directory?: string + workspace?: string + } + } + url: "/api/provider/{providerID}" +} + +export type V2ProviderGetErrors = { + /** + * NotFoundError + */ + 404: NotFoundError +} + +export type V2ProviderGetError = V2ProviderGetErrors[keyof V2ProviderGetErrors] + +export type V2ProviderGetResponses = { + /** + * ProviderV2.Info + */ + 200: ProviderV2Info +} + +export type V2ProviderGetResponse = V2ProviderGetResponses[keyof V2ProviderGetResponses] + export type TuiAppendPromptData = { body?: { text: string @@ -7514,7 +7401,7 @@ export type ExperimentalWorkspaceCreateData = { body?: { id?: string type: string - branch: string | null + branch?: string | null extra?: unknown | null } path?: never diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index c383071c749..186b8a7444d 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -1074,6 +1074,16 @@ } } } + }, + "400": { + "description": "WorktreeError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorktreeError" + } + } + } } }, "description": "List all sandbox worktrees for the current project.", @@ -1118,11 +1128,11 @@ } }, "400": { - "description": "Bad request", + "description": "WorktreeError", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BadRequestError" + "$ref": "#/components/schemas/WorktreeError" } } } @@ -1180,11 +1190,11 @@ } }, "400": { - "description": "Bad request", + "description": "WorktreeError", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BadRequestError" + "$ref": "#/components/schemas/WorktreeError" } } } @@ -1244,11 +1254,11 @@ } }, "400": { - "description": "Bad request", + "description": "WorktreeError", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BadRequestError" + "$ref": "#/components/schemas/WorktreeError" } } } @@ -4678,11 +4688,11 @@ } }, "400": { - "description": "Bad request", + "description": "ProviderAuthError", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BadRequestError" + "$ref": "#/components/schemas/ProviderAuthError1" } } } @@ -4764,11 +4774,11 @@ } }, "400": { - "description": "Bad request", + "description": "ProviderAuthError", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BadRequestError" + "$ref": "#/components/schemas/ProviderAuthError1" } } } @@ -5367,7 +5377,7 @@ } }, "404": { - "description": "Not found", + "description": "NotFoundError", "content": { "application/json": { "schema": { @@ -5444,7 +5454,7 @@ } }, "404": { - "description": "Not found", + "description": "NotFoundError", "content": { "application/json": { "schema": { @@ -5701,7 +5711,7 @@ } }, "404": { - "description": "Not found", + "description": "NotFoundError", "content": { "application/json": { "schema": { @@ -5976,7 +5986,7 @@ } }, "404": { - "description": "Not found", + "description": "NotFoundError", "content": { "application/json": { "schema": { @@ -6137,16 +6147,6 @@ } } } - }, - "404": { - "description": "Not found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NotFoundError" - } - } - } } }, "description": "Abort an active session and stop any ongoing AI processing or command execution.", @@ -6213,7 +6213,7 @@ } }, "404": { - "description": "Not found", + "description": "NotFoundError", "content": { "application/json": { "schema": { @@ -6542,7 +6542,7 @@ } }, "404": { - "description": "Not found", + "description": "NotFoundError", "content": { "application/json": { "schema": { @@ -6725,7 +6725,7 @@ } }, "404": { - "description": "Not found", + "description": "NotFoundError", "content": { "application/json": { "schema": { @@ -6878,7 +6878,7 @@ } }, "404": { - "description": "Not found", + "description": "NotFoundError", "content": { "application/json": { "schema": { @@ -6987,7 +6987,7 @@ } }, "404": { - "description": "Not found", + "description": "NotFoundError", "content": { "application/json": { "schema": { @@ -7081,7 +7081,7 @@ } }, "404": { - "description": "Not found", + "description": "NotFoundError", "content": { "application/json": { "schema": { @@ -7164,7 +7164,7 @@ } }, "404": { - "description": "Not found", + "description": "NotFoundError", "content": { "application/json": { "schema": { @@ -7274,7 +7274,7 @@ } }, "404": { - "description": "Not found", + "description": "NotFoundError", "content": { "application/json": { "schema": { @@ -7362,7 +7362,7 @@ } }, "404": { - "description": "Not found", + "description": "NotFoundError", "content": { "application/json": { "schema": { @@ -8209,6 +8209,171 @@ ] } }, + "/api/model": { + "get": { + "tags": ["v2 models"], + "operationId": "v2.model.list", + "parameters": [ + { + "name": "instance", + "in": "query", + "schema": { + "type": "object", + "properties": { + "directory": { + "type": "string" + }, + "workspace": { + "type": "string" + } + }, + "additionalProperties": false + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ModelV2Info" + } + } + } + } + } + }, + "description": "Retrieve available v2 models ordered by release date.", + "summary": "List v2 models", + "x-codeSamples": [ + { + "lang": "js", + "source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.v2.model.list({\n ...\n})" + } + ] + } + }, + "/api/provider": { + "get": { + "tags": ["v2 providers"], + "operationId": "v2.provider.list", + "parameters": [ + { + "name": "instance", + "in": "query", + "schema": { + "type": "object", + "properties": { + "directory": { + "type": "string" + }, + "workspace": { + "type": "string" + } + }, + "additionalProperties": false + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProviderV2Info" + } + } + } + } + } + }, + "description": "Retrieve active v2 AI providers so clients can show provider availability and configuration.", + "summary": "List v2 providers", + "x-codeSamples": [ + { + "lang": "js", + "source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.v2.provider.list({\n ...\n})" + } + ] + } + }, + "/api/provider/{providerID}": { + "get": { + "tags": ["v2 providers"], + "operationId": "v2.provider.get", + "parameters": [ + { + "name": "providerID", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "instance", + "in": "query", + "schema": { + "type": "object", + "properties": { + "directory": { + "type": "string" + }, + "workspace": { + "type": "string" + } + }, + "additionalProperties": false + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "responses": { + "200": { + "description": "ProviderV2.Info", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProviderV2Info" + } + } + } + }, + "404": { + "description": "NotFoundError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundError" + } + } + } + } + }, + "description": "Retrieve a single v2 AI provider so clients can inspect its availability and endpoint settings.", + "summary": "Get v2 provider", + "x-codeSamples": [ + { + "lang": "js", + "source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.v2.provider.get({\n ...\n})" + } + ] + } + }, "/tui/append-prompt": { "post": { "tags": ["tui"], @@ -9143,7 +9308,7 @@ ] } }, - "required": ["type", "branch"], + "required": ["type"], "additionalProperties": false } } @@ -14179,9 +14344,6 @@ "schemas": { "Event": { "anyOf": [ - { - "$ref": "#/components/schemas/EventServerInstanceDisposed" - }, { "$ref": "#/components/schemas/EventServerConnected" }, @@ -14192,37 +14354,46 @@ "$ref": "#/components/schemas/EventGlobalConfigUpdated" }, { - "$ref": "#/components/schemas/EventFileEdited" + "$ref": "#/components/schemas/Event.tui.prompt.append" }, { - "$ref": "#/components/schemas/EventFileWatcherUpdated" + "$ref": "#/components/schemas/Event.tui.command.execute" }, { - "$ref": "#/components/schemas/EventQuestionAsked" + "$ref": "#/components/schemas/EventTuiToastShow1" }, { - "$ref": "#/components/schemas/EventQuestionReplied" + "$ref": "#/components/schemas/Event.tui.session.select" }, { - "$ref": "#/components/schemas/EventQuestionRejected" + "$ref": "#/components/schemas/EventKilocodeAgent_managerStart" }, { - "$ref": "#/components/schemas/EventLspClientDiagnostics" + "$ref": "#/components/schemas/EventIndexingStatus" }, { - "$ref": "#/components/schemas/EventLspUpdated" + "$ref": "#/components/schemas/EventServerInstanceDisposed" }, { - "$ref": "#/components/schemas/Event.tui.prompt.append" + "$ref": "#/components/schemas/EventFileEdited" }, { - "$ref": "#/components/schemas/Event.tui.command.execute" + "$ref": "#/components/schemas/EventFileWatcherUpdated" }, { - "$ref": "#/components/schemas/EventTuiToastShow1" + "$ref": "#/components/schemas/EventQuestionAsked" }, { - "$ref": "#/components/schemas/Event.tui.session.select" + "$ref": "#/components/schemas/EventQuestionReplied" + }, + { + "$ref": "#/components/schemas/EventQuestionRejected" + }, + { + "$ref": "#/components/schemas/EventLspClientDiagnostics" + }, + { + "$ref": "#/components/schemas/EventLspUpdated" }, { "$ref": "#/components/schemas/EventMcpToolsChanged" @@ -14269,12 +14440,6 @@ { "$ref": "#/components/schemas/EventSessionError" }, - { - "$ref": "#/components/schemas/EventInstallationUpdated" - }, - { - "$ref": "#/components/schemas/EventInstallationUpdate-available" - }, { "$ref": "#/components/schemas/EventTodoUpdated" }, @@ -14302,9 +14467,6 @@ { "$ref": "#/components/schemas/EventProjectUpdated" }, - { - "$ref": "#/components/schemas/EventKilocodeAgent_managerStart" - }, { "$ref": "#/components/schemas/EventVcsBranchUpdated" }, @@ -14339,128 +14501,32 @@ "$ref": "#/components/schemas/EventPtyDeleted" }, { - "$ref": "#/components/schemas/EventMessageUpdated" - }, - { - "$ref": "#/components/schemas/EventMessageRemoved" - }, - { - "$ref": "#/components/schemas/EventMessagePartUpdated" - }, - { - "$ref": "#/components/schemas/EventMessagePartRemoved" - }, - { - "$ref": "#/components/schemas/EventSessionCreated" - }, - { - "$ref": "#/components/schemas/EventSessionUpdated" + "$ref": "#/components/schemas/EventInstallationUpdated" }, { - "$ref": "#/components/schemas/EventSessionDeleted" + "$ref": "#/components/schemas/EventInstallationUpdate-available" + } + ] + }, + "OAuth": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["oauth"] }, - { - "$ref": "#/components/schemas/EventSessionNextAgentSwitched" + "refresh": { + "type": "string" }, - { - "$ref": "#/components/schemas/EventSessionNextModelSwitched" + "access": { + "type": "string" }, - { - "$ref": "#/components/schemas/EventSessionNextPrompted" + "expires": { + "type": "integer", + "minimum": 0 }, - { - "$ref": "#/components/schemas/EventSessionNextSynthetic" - }, - { - "$ref": "#/components/schemas/EventSessionNextShellStarted" - }, - { - "$ref": "#/components/schemas/EventSessionNextShellEnded" - }, - { - "$ref": "#/components/schemas/EventSessionNextStepStarted" - }, - { - "$ref": "#/components/schemas/EventSessionNextStepEnded" - }, - { - "$ref": "#/components/schemas/EventSessionNextStepFailed" - }, - { - "$ref": "#/components/schemas/EventSessionNextTextStarted" - }, - { - "$ref": "#/components/schemas/EventSessionNextTextDelta" - }, - { - "$ref": "#/components/schemas/EventSessionNextTextEnded" - }, - { - "$ref": "#/components/schemas/EventSessionNextReasoningStarted" - }, - { - "$ref": "#/components/schemas/EventSessionNextReasoningDelta" - }, - { - "$ref": "#/components/schemas/EventSessionNextReasoningEnded" - }, - { - "$ref": "#/components/schemas/EventSessionNextToolInputStarted" - }, - { - "$ref": "#/components/schemas/EventSessionNextToolInputDelta" - }, - { - "$ref": "#/components/schemas/EventSessionNextToolInputEnded" - }, - { - "$ref": "#/components/schemas/EventSessionNextToolCalled" - }, - { - "$ref": "#/components/schemas/EventSessionNextToolProgress" - }, - { - "$ref": "#/components/schemas/EventSessionNextToolSuccess" - }, - { - "$ref": "#/components/schemas/EventSessionNextToolFailed" - }, - { - "$ref": "#/components/schemas/EventSessionNextRetried" - }, - { - "$ref": "#/components/schemas/EventSessionNextCompactionStarted" - }, - { - "$ref": "#/components/schemas/EventSessionNextCompactionDelta" - }, - { - "$ref": "#/components/schemas/EventSessionNextCompactionEnded" - }, - { - "$ref": "#/components/schemas/EventIndexingStatus" - } - ] - }, - "OAuth": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": ["oauth"] - }, - "refresh": { - "type": "string" - }, - "access": { - "type": "string" - }, - "expires": { - "type": "integer", - "minimum": 0 - }, - "accountId": { - "type": "string" + "accountId": { + "type": "string" }, "enterpriseUrl": { "type": "string" @@ -14519,6 +14585,170 @@ } ] }, + "Event.tui.prompt.append": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["tui.prompt.append"] + }, + "properties": { + "type": "object", + "properties": { + "text": { + "type": "string" + } + }, + "required": ["text"], + "additionalProperties": false + } + }, + "required": ["id", "type", "properties"], + "additionalProperties": false + }, + "Event.tui.command.execute": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["tui.command.execute"] + }, + "properties": { + "type": "object", + "properties": { + "command": { + "anyOf": [ + { + "type": "string", + "enum": [ + "session.list", + "session.new", + "session.share", + "session.interrupt", + "session.compact", + "session.page.up", + "session.page.down", + "session.line.up", + "session.line.down", + "session.half.page.up", + "session.half.page.down", + "session.first", + "session.last", + "prompt.clear", + "prompt.submit", + "agent.cycle" + ] + }, + { + "type": "string" + } + ] + } + }, + "required": ["command"], + "additionalProperties": false + } + }, + "required": ["id", "type", "properties"], + "additionalProperties": false + }, + "Event.tui.toast.show": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["tui.toast.show"] + }, + "properties": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "message": { + "type": "string" + }, + "variant": { + "type": "string", + "enum": ["info", "success", "warning", "error"] + }, + "duration": { + "type": "integer", + "exclusiveMinimum": 0 + } + }, + "required": ["message", "variant"], + "additionalProperties": false + } + }, + "required": ["id", "type", "properties"], + "additionalProperties": false + }, + "Event.tui.session.select": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["tui.session.select"] + }, + "properties": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "pattern": "^ses", + "description": "Session ID to navigate to" + } + }, + "required": ["sessionID"], + "additionalProperties": false + } + }, + "required": ["id", "type", "properties"], + "additionalProperties": false + }, + "IndexingStatusState": { + "type": "string", + "enum": ["Disabled", "In Progress", "Complete", "Error", "Standby"] + }, + "IndexingStatus": { + "type": "object", + "properties": { + "state": { + "$ref": "#/components/schemas/IndexingStatusState" + }, + "message": { + "type": "string" + }, + "processedFiles": { + "type": "integer", + "minimum": 0 + }, + "totalFiles": { + "type": "integer", + "minimum": 0 + }, + "percent": { + "type": "integer", + "minimum": 0, + "maximum": 100 + } + }, + "required": ["state", "message", "processedFiles", "totalFiles", "percent"], + "additionalProperties": false + }, "QuestionOption": { "type": "object", "properties": { @@ -14661,158 +14891,24 @@ "required": ["sessionID", "requestID"], "additionalProperties": false }, - "Event.tui.prompt.append": { + "SessionNetworkWait": { "type": "object", "properties": { "id": { - "type": "string" + "type": "string", + "pattern": "^que" }, - "type": { + "sessionID": { "type": "string", - "enum": ["tui.prompt.append"] + "pattern": "^ses" }, - "properties": { - "type": "object", - "properties": { - "text": { - "type": "string" - } - }, - "required": ["text"], - "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, - "Event.tui.command.execute": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["tui.command.execute"] - }, - "properties": { - "type": "object", - "properties": { - "command": { - "anyOf": [ - { - "type": "string", - "enum": [ - "session.list", - "session.new", - "session.share", - "session.interrupt", - "session.compact", - "session.page.up", - "session.page.down", - "session.line.up", - "session.line.down", - "session.half.page.up", - "session.half.page.down", - "session.first", - "session.last", - "prompt.clear", - "prompt.submit", - "agent.cycle" - ] - }, - { - "type": "string" - } - ] - } - }, - "required": ["command"], - "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, - "Event.tui.toast.show": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["tui.toast.show"] - }, - "properties": { - "type": "object", - "properties": { - "title": { - "type": "string" - }, - "message": { - "type": "string" - }, - "variant": { - "type": "string", - "enum": ["info", "success", "warning", "error"] - }, - "duration": { - "type": "integer", - "exclusiveMinimum": 0 - } - }, - "required": ["message", "variant"], - "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, - "Event.tui.session.select": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["tui.session.select"] - }, - "properties": { - "type": "object", - "properties": { - "sessionID": { - "type": "string", - "pattern": "^ses", - "description": "Session ID to navigate to" - } - }, - "required": ["sessionID"], - "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, - "SessionNetworkWait": { - "type": "object", - "properties": { - "id": { - "type": "string", - "pattern": "^que" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "message": { - "type": "string" - }, - "restored": { - "type": "boolean" - }, - "time": { + "message": { + "type": "string" + }, + "restored": { + "type": "boolean" + }, + "time": { "type": "object", "properties": { "created": { @@ -16616,6 +16712,38 @@ "required": ["additions", "deletions", "files"], "additionalProperties": false }, + "cost": { + "type": "number" + }, + "tokens": { + "type": "object", + "properties": { + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "reasoning": { + "type": "number" + }, + "cache": { + "type": "object", + "properties": { + "read": { + "type": "number" + }, + "write": { + "type": "number" + } + }, + "required": ["read", "write"], + "additionalProperties": false + } + }, + "required": ["input", "output", "reasoning", "cache"], + "additionalProperties": false + }, "share": { "type": "object", "properties": { @@ -16718,39 +16846,15 @@ "items": { "$ref": "#/components/schemas/PromptAgentAttachment" } - } - }, - "required": ["text"], - "additionalProperties": false - }, - "IndexingStatusState": { - "type": "string", - "enum": ["Disabled", "In Progress", "Complete", "Error", "Standby"] - }, - "IndexingStatus": { - "type": "object", - "properties": { - "state": { - "$ref": "#/components/schemas/IndexingStatusState" - }, - "message": { - "type": "string" - }, - "processedFiles": { - "type": "integer", - "minimum": 0 - }, - "totalFiles": { - "type": "integer", - "minimum": 0 }, - "percent": { - "type": "integer", - "minimum": 0, - "maximum": 100 + "references": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PromptReferenceAttachment" + } } }, - "required": ["state", "message", "processedFiles", "totalFiles", "percent"], + "required": ["text"], "additionalProperties": false }, "GlobalEvent": { @@ -16767,9 +16871,6 @@ }, "payload": { "anyOf": [ - { - "$ref": "#/components/schemas/EventServerInstanceDisposed" - }, { "$ref": "#/components/schemas/EventServerConnected" }, @@ -16780,37 +16881,46 @@ "$ref": "#/components/schemas/EventGlobalConfigUpdated" }, { - "$ref": "#/components/schemas/EventFileEdited" + "$ref": "#/components/schemas/Event.tui.prompt.append" }, { - "$ref": "#/components/schemas/EventFileWatcherUpdated" + "$ref": "#/components/schemas/Event.tui.command.execute" }, { - "$ref": "#/components/schemas/EventQuestionAsked" + "$ref": "#/components/schemas/Event.tui.toast.show" }, { - "$ref": "#/components/schemas/EventQuestionReplied" + "$ref": "#/components/schemas/Event.tui.session.select" }, { - "$ref": "#/components/schemas/EventQuestionRejected" + "$ref": "#/components/schemas/EventKilocodeAgent_managerStart" }, { - "$ref": "#/components/schemas/EventLspClientDiagnostics" + "$ref": "#/components/schemas/EventIndexingStatus" }, { - "$ref": "#/components/schemas/EventLspUpdated" + "$ref": "#/components/schemas/EventServerInstanceDisposed" }, { - "$ref": "#/components/schemas/Event.tui.prompt.append" + "$ref": "#/components/schemas/EventFileEdited" }, { - "$ref": "#/components/schemas/Event.tui.command.execute" + "$ref": "#/components/schemas/EventFileWatcherUpdated" }, { - "$ref": "#/components/schemas/Event.tui.toast.show" + "$ref": "#/components/schemas/EventQuestionAsked" }, { - "$ref": "#/components/schemas/Event.tui.session.select" + "$ref": "#/components/schemas/EventQuestionReplied" + }, + { + "$ref": "#/components/schemas/EventQuestionRejected" + }, + { + "$ref": "#/components/schemas/EventLspClientDiagnostics" + }, + { + "$ref": "#/components/schemas/EventLspUpdated" }, { "$ref": "#/components/schemas/EventMcpToolsChanged" @@ -16857,12 +16967,6 @@ { "$ref": "#/components/schemas/EventSessionError" }, - { - "$ref": "#/components/schemas/EventInstallationUpdated" - }, - { - "$ref": "#/components/schemas/EventInstallationUpdate-available" - }, { "$ref": "#/components/schemas/EventTodoUpdated" }, @@ -16890,9 +16994,6 @@ { "$ref": "#/components/schemas/EventProjectUpdated" }, - { - "$ref": "#/components/schemas/EventKilocodeAgent_managerStart" - }, { "$ref": "#/components/schemas/EventVcsBranchUpdated" }, @@ -16927,136 +17028,40 @@ "$ref": "#/components/schemas/EventPtyDeleted" }, { - "$ref": "#/components/schemas/EventMessageUpdated" + "$ref": "#/components/schemas/EventInstallationUpdated" }, { - "$ref": "#/components/schemas/EventMessageRemoved" + "$ref": "#/components/schemas/EventInstallationUpdate-available" }, { - "$ref": "#/components/schemas/EventMessagePartUpdated" + "$ref": "#/components/schemas/SyncEventMessageUpdated" }, { - "$ref": "#/components/schemas/EventMessagePartRemoved" + "$ref": "#/components/schemas/SyncEventMessageRemoved" }, { - "$ref": "#/components/schemas/EventSessionCreated" + "$ref": "#/components/schemas/SyncEventMessagePartUpdated" }, { - "$ref": "#/components/schemas/EventSessionUpdated" + "$ref": "#/components/schemas/SyncEventMessagePartRemoved" }, { - "$ref": "#/components/schemas/EventSessionDeleted" + "$ref": "#/components/schemas/SyncEventSessionCreated" }, { - "$ref": "#/components/schemas/EventSessionNextAgentSwitched" + "$ref": "#/components/schemas/SyncEventSessionUpdated" }, { - "$ref": "#/components/schemas/EventSessionNextModelSwitched" + "$ref": "#/components/schemas/SyncEventSessionDeleted" }, { - "$ref": "#/components/schemas/EventSessionNextPrompted" + "$ref": "#/components/schemas/SyncEventSessionNextAgentSwitched" }, { - "$ref": "#/components/schemas/EventSessionNextSynthetic" + "$ref": "#/components/schemas/SyncEventSessionNextModelSwitched" }, { - "$ref": "#/components/schemas/EventSessionNextShellStarted" - }, - { - "$ref": "#/components/schemas/EventSessionNextShellEnded" - }, - { - "$ref": "#/components/schemas/EventSessionNextStepStarted" - }, - { - "$ref": "#/components/schemas/EventSessionNextStepEnded" - }, - { - "$ref": "#/components/schemas/EventSessionNextStepFailed" - }, - { - "$ref": "#/components/schemas/EventSessionNextTextStarted" - }, - { - "$ref": "#/components/schemas/EventSessionNextTextDelta" - }, - { - "$ref": "#/components/schemas/EventSessionNextTextEnded" - }, - { - "$ref": "#/components/schemas/EventSessionNextReasoningStarted" - }, - { - "$ref": "#/components/schemas/EventSessionNextReasoningDelta" - }, - { - "$ref": "#/components/schemas/EventSessionNextReasoningEnded" - }, - { - "$ref": "#/components/schemas/EventSessionNextToolInputStarted" - }, - { - "$ref": "#/components/schemas/EventSessionNextToolInputDelta" - }, - { - "$ref": "#/components/schemas/EventSessionNextToolInputEnded" - }, - { - "$ref": "#/components/schemas/EventSessionNextToolCalled" - }, - { - "$ref": "#/components/schemas/EventSessionNextToolProgress" - }, - { - "$ref": "#/components/schemas/EventSessionNextToolSuccess" - }, - { - "$ref": "#/components/schemas/EventSessionNextToolFailed" - }, - { - "$ref": "#/components/schemas/EventSessionNextRetried" - }, - { - "$ref": "#/components/schemas/EventSessionNextCompactionStarted" - }, - { - "$ref": "#/components/schemas/EventSessionNextCompactionDelta" - }, - { - "$ref": "#/components/schemas/EventSessionNextCompactionEnded" - }, - { - "$ref": "#/components/schemas/EventIndexingStatus" - }, - { - "$ref": "#/components/schemas/SyncEventMessageUpdated" - }, - { - "$ref": "#/components/schemas/SyncEventMessageRemoved" - }, - { - "$ref": "#/components/schemas/SyncEventMessagePartUpdated" - }, - { - "$ref": "#/components/schemas/SyncEventMessagePartRemoved" - }, - { - "$ref": "#/components/schemas/SyncEventSessionCreated" - }, - { - "$ref": "#/components/schemas/SyncEventSessionUpdated" - }, - { - "$ref": "#/components/schemas/SyncEventSessionDeleted" - }, - { - "$ref": "#/components/schemas/SyncEventSessionNextAgentSwitched" - }, - { - "$ref": "#/components/schemas/SyncEventSessionNextModelSwitched" - }, - { - "$ref": "#/components/schemas/SyncEventSessionNextPrompted" + "$ref": "#/components/schemas/SyncEventSessionNextPrompted" }, { "$ref": "#/components/schemas/SyncEventSessionNextSynthetic" @@ -17458,9 +17463,6 @@ "websearch": { "$ref": "#/components/schemas/PermissionActionConfig" }, - "codesearch": { - "$ref": "#/components/schemas/PermissionActionConfig" - }, "repo_clone": { "$ref": "#/components/schemas/PermissionRuleConfig" }, @@ -18561,6 +18563,49 @@ "required": ["read", "write"], "additionalProperties": false }, + "tiers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "cache": { + "type": "object", + "properties": { + "read": { + "type": "number" + }, + "write": { + "type": "number" + } + }, + "required": ["read", "write"], + "additionalProperties": false + }, + "tier": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["context"] + }, + "size": { + "type": "number" + } + }, + "required": ["type", "size"], + "additionalProperties": false + } + }, + "required": ["input", "output", "cache", "tier"], + "additionalProperties": false + } + }, "experimentalOver200K": { "type": "object", "properties": { @@ -18764,6 +18809,35 @@ "type": "string" } }, + "WorktreeError": { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "WorktreeNotGitError", + "WorktreeNameGenerationFailedError", + "WorktreeCreateFailedError", + "WorktreeStartCommandFailedError", + "WorktreeRemoveFailedError", + "WorktreeResetFailedError", + "WorktreeListFailedError" + ] + }, + "data": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": ["message"], + "additionalProperties": false + } + }, + "required": ["name", "data"], + "additionalProperties": false + }, "WorktreeCreateInput": { "type": "object", "properties": { @@ -18790,7 +18864,7 @@ "type": "string" } }, - "required": ["name", "branch", "directory"], + "required": ["name", "directory"], "additionalProperties": false }, "WorktreeRemoveInput": { @@ -18919,6 +18993,38 @@ "required": ["additions", "deletions", "files"], "additionalProperties": false }, + "cost": { + "type": "number" + }, + "tokens": { + "type": "object", + "properties": { + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "reasoning": { + "type": "number" + }, + "cache": { + "type": "object", + "properties": { + "read": { + "type": "number" + }, + "write": { + "type": "number" + } + }, + "required": ["read", "write"], + "additionalProperties": false + } + }, + "required": ["input", "output", "reasoning", "cache"], + "additionalProperties": false + }, "share": { "type": "object", "properties": { @@ -19679,6 +19785,41 @@ "required": ["url", "method", "instructions"], "additionalProperties": false }, + "ProviderAuthError1": { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "BadRequest", + "ProviderAuthOauthMissing", + "ProviderAuthOauthCodeMissing", + "ProviderAuthOauthCallbackFailed", + "ProviderAuthValidationFailed" + ] + }, + "data": { + "type": "object", + "properties": { + "providerID": { + "type": "string" + }, + "field": { + "type": "string" + }, + "message": { + "type": "string" + }, + "kind": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "required": ["name", "data"], + "additionalProperties": false + }, "TextPartInput": { "type": "object", "properties": { @@ -20064,7 +20205,7 @@ ] } }, - "required": ["id", "type", "name", "branch", "directory", "extra", "projectID", "timeUsed"], + "required": ["id", "type", "name", "projectID", "timeUsed"], "additionalProperties": false }, "WorkspaceWarpError": { @@ -20976,6 +21117,52 @@ } ] }, + "cost": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "tokens": { + "anyOf": [ + { + "type": "object", + "properties": { + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "reasoning": { + "type": "number" + }, + "cache": { + "type": "object", + "properties": { + "read": { + "type": "number" + }, + "write": { + "type": "number" + } + }, + "required": ["read", "write"], + "additionalProperties": false + } + }, + "required": ["input", "output", "reasoning", "cache"], + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, "share": { "type": "object", "properties": { @@ -22446,30 +22633,6 @@ "required": ["type", "name", "id", "seq", "aggregateID", "data"], "additionalProperties": false }, - "EventServerInstanceDisposed": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["server.instance.disposed"] - }, - "properties": { - "type": "object", - "properties": { - "directory": { - "type": "string" - } - }, - "required": ["directory"], - "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, "EventServerConnected": { "type": "object", "properties": { @@ -22524,7 +22687,7 @@ "required": ["id", "type", "properties"], "additionalProperties": false }, - "EventFileEdited": { + "EventKilocodeAgent_managerStart": { "type": "object", "properties": { "id": { @@ -22532,7 +22695,110 @@ }, "type": { "type": "string", - "enum": ["file.edited"] + "enum": ["kilocode.agent_manager.start"] + }, + "properties": { + "type": "object", + "properties": { + "requestID": { + "type": "string" + }, + "sessionID": { + "type": "string", + "pattern": "^ses" + }, + "mode": { + "type": "string", + "enum": ["worktree", "local"] + }, + "versions": { + "type": "boolean" + }, + "tasks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "prompt": { + "type": "string" + }, + "name": { + "type": "string" + }, + "branchName": { + "type": "string" + } + }, + "additionalProperties": false + }, + "minItems": 1, + "maxItems": 20 + } + }, + "required": ["requestID", "sessionID", "mode", "tasks"], + "additionalProperties": false + } + }, + "required": ["id", "type", "properties"], + "additionalProperties": false + }, + "EventIndexingStatus": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["indexing.status"] + }, + "properties": { + "type": "object", + "properties": { + "status": { + "$ref": "#/components/schemas/IndexingStatus" + } + }, + "required": ["status"], + "additionalProperties": false + } + }, + "required": ["id", "type", "properties"], + "additionalProperties": false + }, + "EventServerInstanceDisposed": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["server.instance.disposed"] + }, + "properties": { + "type": "object", + "properties": { + "directory": { + "type": "string" + } + }, + "required": ["directory"], + "additionalProperties": false + } + }, + "required": ["id", "type", "properties"], + "additionalProperties": false + }, + "EventFileEdited": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["file.edited"] }, "properties": { "type": "object", @@ -23105,54 +23371,6 @@ "required": ["id", "type", "properties"], "additionalProperties": false }, - "EventInstallationUpdated": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["installation.updated"] - }, - "properties": { - "type": "object", - "properties": { - "version": { - "type": "string" - } - }, - "required": ["version"], - "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, - "EventInstallationUpdate-available": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["installation.update-available"] - }, - "properties": { - "type": "object", - "properties": { - "version": { - "type": "string" - } - }, - "required": ["version"], - "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, "EventTodoUpdated": { "type": "object", "properties": { @@ -23411,61 +23629,6 @@ "required": ["id", "type", "properties"], "additionalProperties": false }, - "EventKilocodeAgent_managerStart": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["kilocode.agent_manager.start"] - }, - "properties": { - "type": "object", - "properties": { - "requestID": { - "type": "string" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "mode": { - "type": "string", - "enum": ["worktree", "local"] - }, - "versions": { - "type": "boolean" - }, - "tasks": { - "type": "array", - "items": { - "type": "object", - "properties": { - "prompt": { - "type": "string" - }, - "name": { - "type": "string" - }, - "branchName": { - "type": "string" - } - }, - "additionalProperties": false - }, - "minItems": 1, - "maxItems": 20 - } - }, - "required": ["requestID", "sessionID", "mode", "tasks"], - "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, "EventVcsBranchUpdated": { "type": "object", "properties": { @@ -23613,7 +23776,7 @@ "type": "string" } }, - "required": ["name", "branch"], + "required": ["name"], "additionalProperties": false } }, @@ -23746,7 +23909,7 @@ "required": ["id", "type", "properties"], "additionalProperties": false }, - "EventMessageUpdated": { + "EventInstallationUpdated": { "type": "object", "properties": { "id": { @@ -23754,27 +23917,23 @@ }, "type": { "type": "string", - "enum": ["message.updated"] + "enum": ["installation.updated"] }, "properties": { "type": "object", "properties": { - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "info": { - "$ref": "#/components/schemas/Message" + "version": { + "type": "string" } }, - "required": ["sessionID", "info"], + "required": ["version"], "additionalProperties": false } }, "required": ["id", "type", "properties"], "additionalProperties": false }, - "EventMessageRemoved": { + "EventInstallationUpdate-available": { "type": "object", "properties": { "id": { @@ -23782,270 +23941,143 @@ }, "type": { "type": "string", - "enum": ["message.removed"] + "enum": ["installation.update-available"] }, "properties": { "type": "object", "properties": { - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "messageID": { - "type": "string", - "pattern": "^msg" + "version": { + "type": "string" } }, - "required": ["sessionID", "messageID"], + "required": ["version"], "additionalProperties": false } }, "required": ["id", "type", "properties"], "additionalProperties": false }, - "EventMessagePartUpdated": { + "PromptSource": { "type": "object", "properties": { - "id": { + "start": { + "type": "number" + }, + "end": { + "type": "number" + }, + "text": { + "type": "string" + } + }, + "required": ["start", "end", "text"], + "additionalProperties": false + }, + "PromptFileAttachment": { + "type": "object", + "properties": { + "uri": { "type": "string" }, - "type": { - "type": "string", - "enum": ["message.part.updated"] + "mime": { + "type": "string" }, - "properties": { - "type": "object", - "properties": { - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "part": { - "$ref": "#/components/schemas/Part" - }, - "time": { - "type": "integer", - "minimum": 0 - } - }, - "required": ["sessionID", "part", "time"], - "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, - "EventMessagePartRemoved": { - "type": "object", - "properties": { - "id": { + "name": { "type": "string" }, - "type": { - "type": "string", - "enum": ["message.part.removed"] + "description": { + "type": "string" }, - "properties": { - "type": "object", - "properties": { - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "messageID": { - "type": "string", - "pattern": "^msg" - }, - "partID": { - "type": "string", - "pattern": "^prt" - } - }, - "required": ["sessionID", "messageID", "partID"], - "additionalProperties": false + "source": { + "$ref": "#/components/schemas/PromptSource" } }, - "required": ["id", "type", "properties"], + "required": ["uri", "mime"], "additionalProperties": false }, - "EventSessionCreated": { + "PromptAgentAttachment": { "type": "object", "properties": { - "id": { + "name": { "type": "string" }, - "type": { - "type": "string", - "enum": ["session.created"] - }, - "properties": { - "type": "object", - "properties": { - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "info": { - "$ref": "#/components/schemas/Session" - } - }, - "required": ["sessionID", "info"], - "additionalProperties": false + "source": { + "$ref": "#/components/schemas/PromptSource" } }, - "required": ["id", "type", "properties"], + "required": ["name"], "additionalProperties": false }, - "EventSessionUpdated": { + "PromptReferenceAttachment": { "type": "object", "properties": { - "id": { + "name": { "type": "string" }, - "type": { + "kind": { "type": "string", - "enum": ["session.updated"] + "enum": ["local", "git", "invalid"] }, - "properties": { - "type": "object", - "properties": { - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "info": { - "$ref": "#/components/schemas/Session" - } - }, - "required": ["sessionID", "info"], - "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, - "EventSessionDeleted": { - "type": "object", - "properties": { - "id": { + "uri": { "type": "string" }, - "type": { - "type": "string", - "enum": ["session.deleted"] + "repository": { + "type": "string" }, - "properties": { - "type": "object", - "properties": { - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "info": { - "$ref": "#/components/schemas/Session" - } - }, - "required": ["sessionID", "info"], - "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, - "EventSessionNextAgentSwitched": { - "type": "object", - "properties": { - "id": { + "branch": { "type": "string" }, - "type": { - "type": "string", - "enum": ["session.next.agent.switched"] + "target": { + "type": "string" }, - "properties": { - "type": "object", - "properties": { - "timestamp": { - "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "agent": { - "type": "string" - } - }, - "required": ["timestamp", "sessionID", "agent"], - "additionalProperties": false + "targetUri": { + "type": "string" + }, + "problem": { + "type": "string" + }, + "source": { + "$ref": "#/components/schemas/PromptSource" } }, - "required": ["id", "type", "properties"], + "required": ["name", "kind"], "additionalProperties": false }, - "EventSessionNextModelSwitched": { + "SessionErrorUnknown": { "type": "object", "properties": { - "id": { - "type": "string" - }, "type": { "type": "string", - "enum": ["session.next.model.switched"] + "enum": ["unknown"] }, - "properties": { - "type": "object", - "properties": { - "timestamp": { - "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "model": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "providerID": { - "type": "string" - }, - "variant": { - "type": "string" - } - }, - "required": ["id", "providerID", "variant"], - "additionalProperties": false - } - }, - "required": ["timestamp", "sessionID", "model"], - "additionalProperties": false + "message": { + "type": "string" } }, - "required": ["id", "type", "properties"], + "required": ["type", "message"], "additionalProperties": false }, - "PromptSource": { + "ToolTextContent": { "type": "object", "properties": { - "start": { - "type": "number" - }, - "end": { - "type": "number" + "type": { + "type": "string", + "enum": ["text"] }, "text": { "type": "string" } }, - "required": ["start", "end", "text"], + "required": ["type", "text"], "additionalProperties": false }, - "PromptFileAttachment": { + "ToolFileContent": { "type": "object", "properties": { + "type": { + "type": "string", + "enum": ["file"] + }, "uri": { "type": "string" }, @@ -24054,1677 +24086,1246 @@ }, "name": { "type": "string" - }, - "description": { - "type": "string" - }, - "source": { - "$ref": "#/components/schemas/PromptSource" } }, - "required": ["uri", "mime"], + "required": ["type", "uri", "mime"], "additionalProperties": false }, - "PromptAgentAttachment": { + "SessionNextRetry_error": { "type": "object", "properties": { - "name": { + "message": { "type": "string" }, - "source": { - "$ref": "#/components/schemas/PromptSource" + "statusCode": { + "type": "number" + }, + "isRetryable": { + "type": "boolean" + }, + "responseHeaders": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "responseBody": { + "type": "string" + }, + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + } } }, - "required": ["name"], + "required": ["message", "isRetryable"], "additionalProperties": false }, - "EventSessionNextPrompted": { + "SessionInfo": { "type": "object", "properties": { "id": { + "type": "string", + "pattern": "^ses" + }, + "parentID": { + "type": "string", + "pattern": "^ses" + }, + "projectID": { "type": "string" }, - "type": { + "workspaceID": { "type": "string", - "enum": ["session.next.prompted"] + "pattern": "^wrk" }, - "properties": { + "path": { + "type": "string" + }, + "agent": { + "type": "string" + }, + "model": { "type": "object", "properties": { - "timestamp": { + "id": { + "type": "string" + }, + "providerID": { + "type": "string" + }, + "variant": { + "type": "string" + } + }, + "required": ["id", "providerID", "variant"], + "additionalProperties": false + }, + "cost": { + "type": "number" + }, + "tokens": { + "type": "object", + "properties": { + "input": { "type": "number" }, - "sessionID": { - "type": "string", - "pattern": "^ses" + "output": { + "type": "number" }, - "prompt": { - "$ref": "#/components/schemas/Prompt" + "reasoning": { + "type": "number" + }, + "cache": { + "type": "object", + "properties": { + "read": { + "type": "number" + }, + "write": { + "type": "number" + } + }, + "required": ["read", "write"], + "additionalProperties": false } }, - "required": ["timestamp", "sessionID", "prompt"], + "required": ["input", "output", "reasoning", "cache"], + "additionalProperties": false + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + }, + "updated": { + "type": "number" + }, + "archived": { + "type": "number" + } + }, + "required": ["created", "updated"], "additionalProperties": false + }, + "title": { + "type": "string" } }, - "required": ["id", "type", "properties"], + "required": ["id", "projectID", "cost", "tokens", "time", "title"], "additionalProperties": false }, - "EventSessionNextSynthetic": { + "SessionDelivery": { + "type": "string", + "enum": ["immediate", "deferred"] + }, + "SessionMessageAgentSwitched": { "type": "object", "properties": { "id": { "type": "string" }, - "type": { - "type": "string", - "enum": ["session.next.synthetic"] + "metadata": { + "type": "object" }, - "properties": { + "time": { "type": "object", "properties": { - "timestamp": { + "created": { "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "text": { - "type": "string" } }, - "required": ["timestamp", "sessionID", "text"], + "required": ["created"], "additionalProperties": false + }, + "type": { + "type": "string", + "enum": ["agent-switched"] + }, + "agent": { + "type": "string" } }, - "required": ["id", "type", "properties"], + "required": ["id", "time", "type", "agent"], "additionalProperties": false }, - "EventSessionNextShellStarted": { + "SessionMessageModelSwitched": { "type": "object", "properties": { "id": { "type": "string" }, + "metadata": { + "type": "object" + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + } + }, + "required": ["created"], + "additionalProperties": false + }, "type": { "type": "string", - "enum": ["session.next.shell.started"] + "enum": ["model-switched"] }, - "properties": { + "model": { "type": "object", "properties": { - "timestamp": { - "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" + "id": { + "type": "string" }, - "callID": { + "providerID": { "type": "string" }, - "command": { + "variant": { "type": "string" } }, - "required": ["timestamp", "sessionID", "callID", "command"], + "required": ["id", "providerID", "variant"], "additionalProperties": false } }, - "required": ["id", "type", "properties"], + "required": ["id", "time", "type", "model"], "additionalProperties": false }, - "EventSessionNextShellEnded": { + "SessionMessageUser": { "type": "object", "properties": { "id": { "type": "string" }, - "type": { - "type": "string", - "enum": ["session.next.shell.ended"] + "metadata": { + "type": "object" }, - "properties": { + "time": { "type": "object", "properties": { - "timestamp": { + "created": { "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "callID": { - "type": "string" - }, - "output": { - "type": "string" } }, - "required": ["timestamp", "sessionID", "callID", "output"], + "required": ["created"], "additionalProperties": false + }, + "text": { + "type": "string" + }, + "files": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PromptFileAttachment" + } + }, + "agents": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PromptAgentAttachment" + } + }, + "references": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PromptReferenceAttachment" + } + }, + "type": { + "type": "string", + "enum": ["user"] } }, - "required": ["id", "type", "properties"], + "required": ["id", "time", "text", "type"], "additionalProperties": false }, - "EventSessionNextStepStarted": { + "SessionMessageSynthetic": { "type": "object", "properties": { "id": { "type": "string" }, - "type": { - "type": "string", - "enum": ["session.next.step.started"] + "metadata": { + "type": "object" }, - "properties": { + "time": { "type": "object", "properties": { - "timestamp": { + "created": { "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "agent": { - "type": "string" - }, - "model": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "providerID": { - "type": "string" - }, - "variant": { - "type": "string" - } - }, - "required": ["id", "providerID", "variant"], - "additionalProperties": false - }, - "snapshot": { - "type": "string" } }, - "required": ["timestamp", "sessionID", "agent", "model"], + "required": ["created"], "additionalProperties": false + }, + "sessionID": { + "type": "string", + "pattern": "^ses" + }, + "text": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["synthetic"] } }, - "required": ["id", "type", "properties"], + "required": ["id", "time", "sessionID", "text", "type"], "additionalProperties": false }, - "EventSessionNextStepEnded": { + "SessionMessageShell": { "type": "object", "properties": { "id": { "type": "string" }, - "type": { - "type": "string", - "enum": ["session.next.step.ended"] + "metadata": { + "type": "object" }, - "properties": { + "time": { "type": "object", "properties": { - "timestamp": { + "created": { "type": "number" }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "finish": { - "type": "string" - }, - "cost": { + "completed": { "type": "number" - }, - "tokens": { - "type": "object", - "properties": { - "input": { - "type": "number" - }, - "output": { - "type": "number" - }, - "reasoning": { - "type": "number" - }, - "cache": { - "type": "object", - "properties": { - "read": { - "type": "number" - }, - "write": { - "type": "number" - } - }, - "required": ["read", "write"], - "additionalProperties": false - } - }, - "required": ["input", "output", "reasoning", "cache"], - "additionalProperties": false - }, - "snapshot": { - "type": "string" } }, - "required": ["timestamp", "sessionID", "finish", "cost", "tokens"], + "required": ["created"], "additionalProperties": false + }, + "type": { + "type": "string", + "enum": ["shell"] + }, + "callID": { + "type": "string" + }, + "command": { + "type": "string" + }, + "output": { + "type": "string" } }, - "required": ["id", "type", "properties"], + "required": ["id", "time", "type", "callID", "command", "output"], "additionalProperties": false }, - "SessionErrorUnknown": { + "SessionMessageAssistantText": { "type": "object", "properties": { "type": { "type": "string", - "enum": ["unknown"] + "enum": ["text"] }, - "message": { + "text": { "type": "string" } }, - "required": ["type", "message"], + "required": ["type", "text"], "additionalProperties": false }, - "EventSessionNextStepFailed": { + "SessionMessageAssistantReasoning": { "type": "object", "properties": { - "id": { - "type": "string" - }, "type": { "type": "string", - "enum": ["session.next.step.failed"] + "enum": ["reasoning"] }, - "properties": { - "type": "object", - "properties": { - "timestamp": { - "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "error": { - "$ref": "#/components/schemas/SessionErrorUnknown" - } - }, - "required": ["timestamp", "sessionID", "error"], - "additionalProperties": false + "id": { + "type": "string" + }, + "text": { + "type": "string" } }, - "required": ["id", "type", "properties"], + "required": ["type", "id", "text"], "additionalProperties": false }, - "EventSessionNextTextStarted": { + "SessionMessageToolStatePending": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "type": { + "status": { "type": "string", - "enum": ["session.next.text.started"] + "enum": ["pending"] }, - "properties": { - "type": "object", - "properties": { - "timestamp": { - "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - } - }, - "required": ["timestamp", "sessionID"], - "additionalProperties": false + "input": { + "type": "string" } }, - "required": ["id", "type", "properties"], + "required": ["status", "input"], "additionalProperties": false }, - "EventSessionNextTextDelta": { + "SessionMessageToolStateRunning": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "type": { + "status": { "type": "string", - "enum": ["session.next.text.delta"] + "enum": ["running"] }, - "properties": { - "type": "object", - "properties": { - "timestamp": { - "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "delta": { - "type": "string" - } - }, - "required": ["timestamp", "sessionID", "delta"], - "additionalProperties": false + "input": { + "type": "object" + }, + "structured": { + "type": "object" + }, + "content": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/ToolTextContent" + }, + { + "$ref": "#/components/schemas/ToolFileContent" + } + ] + } } }, - "required": ["id", "type", "properties"], + "required": ["status", "input", "structured", "content"], "additionalProperties": false }, - "EventSessionNextTextEnded": { + "SessionMessageToolStateCompleted": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "type": { + "status": { "type": "string", - "enum": ["session.next.text.ended"] + "enum": ["completed"] }, - "properties": { - "type": "object", - "properties": { - "timestamp": { - "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "text": { - "type": "string" - } - }, - "required": ["timestamp", "sessionID", "text"], - "additionalProperties": false + "input": { + "type": "object" + }, + "attachments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PromptFileAttachment" + } + }, + "content": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/ToolTextContent" + }, + { + "$ref": "#/components/schemas/ToolFileContent" + } + ] + } + }, + "structured": { + "type": "object" } }, - "required": ["id", "type", "properties"], + "required": ["status", "input", "content", "structured"], "additionalProperties": false }, - "EventSessionNextReasoningStarted": { + "SessionMessageToolStateError": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "type": { + "status": { "type": "string", - "enum": ["session.next.reasoning.started"] + "enum": ["error"] }, - "properties": { - "type": "object", - "properties": { - "timestamp": { - "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "reasoningID": { - "type": "string" - } - }, - "required": ["timestamp", "sessionID", "reasoningID"], - "additionalProperties": false + "input": { + "type": "object" + }, + "content": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/ToolTextContent" + }, + { + "$ref": "#/components/schemas/ToolFileContent" + } + ] + } + }, + "structured": { + "type": "object" + }, + "error": { + "$ref": "#/components/schemas/SessionErrorUnknown" } }, - "required": ["id", "type", "properties"], + "required": ["status", "input", "content", "structured", "error"], "additionalProperties": false }, - "EventSessionNextReasoningDelta": { + "SessionMessageAssistantTool": { "type": "object", "properties": { + "type": { + "type": "string", + "enum": ["tool"] + }, "id": { "type": "string" }, - "type": { - "type": "string", - "enum": ["session.next.reasoning.delta"] + "name": { + "type": "string" }, - "properties": { + "provider": { "type": "object", "properties": { - "timestamp": { - "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "reasoningID": { - "type": "string" + "executed": { + "type": "boolean" }, - "delta": { - "type": "string" + "metadata": { + "type": "object" } }, - "required": ["timestamp", "sessionID", "reasoningID", "delta"], + "required": ["executed"], "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, - "EventSessionNextReasoningEnded": { - "type": "object", - "properties": { - "id": { - "type": "string" }, - "type": { - "type": "string", - "enum": ["session.next.reasoning.ended"] + "state": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionMessageToolStatePending" + }, + { + "$ref": "#/components/schemas/SessionMessageToolStateRunning" + }, + { + "$ref": "#/components/schemas/SessionMessageToolStateCompleted" + }, + { + "$ref": "#/components/schemas/SessionMessageToolStateError" + } + ] }, - "properties": { + "time": { "type": "object", "properties": { - "timestamp": { + "created": { "type": "number" }, - "sessionID": { - "type": "string", - "pattern": "^ses" + "ran": { + "type": "number" }, - "reasoningID": { - "type": "string" + "completed": { + "type": "number" }, - "text": { - "type": "string" + "pruned": { + "type": "number" } }, - "required": ["timestamp", "sessionID", "reasoningID", "text"], + "required": ["created"], "additionalProperties": false } }, - "required": ["id", "type", "properties"], + "required": ["type", "id", "name", "state", "time"], "additionalProperties": false }, - "EventSessionNextToolInputStarted": { + "SessionMessageAssistant": { "type": "object", "properties": { "id": { "type": "string" }, - "type": { - "type": "string", - "enum": ["session.next.tool.input.started"] + "metadata": { + "type": "object" }, - "properties": { + "time": { "type": "object", "properties": { - "timestamp": { + "created": { "type": "number" }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "callID": { - "type": "string" - }, - "name": { - "type": "string" + "completed": { + "type": "number" } }, - "required": ["timestamp", "sessionID", "callID", "name"], + "required": ["created"], "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, - "EventSessionNextToolInputDelta": { - "type": "object", - "properties": { - "id": { - "type": "string" }, "type": { "type": "string", - "enum": ["session.next.tool.input.delta"] + "enum": ["assistant"] }, - "properties": { + "agent": { + "type": "string" + }, + "model": { "type": "object", "properties": { - "timestamp": { - "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" + "id": { + "type": "string" }, - "callID": { + "providerID": { "type": "string" }, - "delta": { + "variant": { "type": "string" } }, - "required": ["timestamp", "sessionID", "callID", "delta"], + "required": ["id", "providerID", "variant"], "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, - "EventSessionNextToolInputEnded": { - "type": "object", - "properties": { - "id": { - "type": "string" }, - "type": { - "type": "string", - "enum": ["session.next.tool.input.ended"] + "content": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionMessageAssistantText" + }, + { + "$ref": "#/components/schemas/SessionMessageAssistantReasoning" + }, + { + "$ref": "#/components/schemas/SessionMessageAssistantTool" + } + ] + } }, - "properties": { + "snapshot": { "type": "object", "properties": { - "timestamp": { - "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "callID": { + "start": { "type": "string" }, - "text": { + "end": { "type": "string" } }, - "required": ["timestamp", "sessionID", "callID", "text"], "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, - "EventSessionNextToolCalled": { - "type": "object", - "properties": { - "id": { + }, + "finish": { "type": "string" }, - "type": { - "type": "string", - "enum": ["session.next.tool.called"] + "cost": { + "type": "number" }, - "properties": { + "tokens": { "type": "object", "properties": { - "timestamp": { + "input": { "type": "number" }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "callID": { - "type": "string" - }, - "tool": { - "type": "string" + "output": { + "type": "number" }, - "input": { - "type": "object" + "reasoning": { + "type": "number" }, - "provider": { + "cache": { "type": "object", "properties": { - "executed": { - "type": "boolean" + "read": { + "type": "number" }, - "metadata": { - "type": "object" + "write": { + "type": "number" } }, - "required": ["executed"], + "required": ["read", "write"], "additionalProperties": false } }, - "required": ["timestamp", "sessionID", "callID", "tool", "input", "provider"], + "required": ["input", "output", "reasoning", "cache"], "additionalProperties": false + }, + "error": { + "$ref": "#/components/schemas/SessionErrorUnknown" } }, - "required": ["id", "type", "properties"], + "required": ["id", "time", "type", "agent", "model", "content"], "additionalProperties": false }, - "ToolTextContent": { + "SessionMessageCompaction": { "type": "object", "properties": { "type": { "type": "string", - "enum": ["text"] + "enum": ["compaction"] }, - "text": { - "type": "string" - } - }, - "required": ["type", "text"], - "additionalProperties": false - }, - "ToolFileContent": { - "type": "object", - "properties": { - "type": { + "reason": { "type": "string", - "enum": ["file"] + "enum": ["auto", "manual"] }, - "uri": { + "summary": { "type": "string" }, - "mime": { + "include": { "type": "string" }, - "name": { - "type": "string" - } - }, - "required": ["type", "uri", "mime"], - "additionalProperties": false - }, - "EventSessionNextToolProgress": { - "type": "object", - "properties": { "id": { "type": "string" }, - "type": { - "type": "string", - "enum": ["session.next.tool.progress"] + "metadata": { + "type": "object" }, - "properties": { + "time": { "type": "object", "properties": { - "timestamp": { - "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "callID": { - "type": "string" - }, - "structured": { - "type": "object" - }, - "content": { - "type": "array", - "items": { - "anyOf": [ - { - "$ref": "#/components/schemas/ToolTextContent" - }, - { - "$ref": "#/components/schemas/ToolFileContent" - } - ] - } - } - }, - "required": ["timestamp", "sessionID", "callID", "structured", "content"], - "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, - "EventSessionNextToolSuccess": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["session.next.tool.success"] - }, - "properties": { - "type": "object", - "properties": { - "timestamp": { - "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "callID": { - "type": "string" - }, - "structured": { - "type": "object" - }, - "content": { - "type": "array", - "items": { - "anyOf": [ - { - "$ref": "#/components/schemas/ToolTextContent" - }, - { - "$ref": "#/components/schemas/ToolFileContent" - } - ] - } - }, - "provider": { - "type": "object", - "properties": { - "executed": { - "type": "boolean" - }, - "metadata": { - "type": "object" - } - }, - "required": ["executed"], - "additionalProperties": false - } - }, - "required": ["timestamp", "sessionID", "callID", "structured", "content", "provider"], - "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, - "EventSessionNextToolFailed": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["session.next.tool.failed"] - }, - "properties": { - "type": "object", - "properties": { - "timestamp": { - "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "callID": { - "type": "string" - }, - "error": { - "$ref": "#/components/schemas/SessionErrorUnknown" - }, - "provider": { - "type": "object", - "properties": { - "executed": { - "type": "boolean" - }, - "metadata": { - "type": "object" - } - }, - "required": ["executed"], - "additionalProperties": false - } - }, - "required": ["timestamp", "sessionID", "callID", "error", "provider"], - "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, - "SessionNextRetry_error": { - "type": "object", - "properties": { - "message": { - "type": "string" - }, - "statusCode": { - "type": "number" - }, - "isRetryable": { - "type": "boolean" - }, - "responseHeaders": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "responseBody": { - "type": "string" - }, - "metadata": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "required": ["message", "isRetryable"], - "additionalProperties": false - }, - "EventSessionNextRetried": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["session.next.retried"] - }, - "properties": { - "type": "object", - "properties": { - "timestamp": { - "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "attempt": { - "type": "number" - }, - "error": { - "$ref": "#/components/schemas/SessionNextRetry_error" - } - }, - "required": ["timestamp", "sessionID", "attempt", "error"], - "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, - "EventSessionNextCompactionStarted": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["session.next.compaction.started"] - }, - "properties": { - "type": "object", - "properties": { - "timestamp": { - "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "reason": { - "type": "string", - "enum": ["auto", "manual"] - } - }, - "required": ["timestamp", "sessionID", "reason"], - "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, - "EventSessionNextCompactionDelta": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["session.next.compaction.delta"] - }, - "properties": { - "type": "object", - "properties": { - "timestamp": { - "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "text": { - "type": "string" - } - }, - "required": ["timestamp", "sessionID", "text"], - "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, - "EventSessionNextCompactionEnded": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["session.next.compaction.ended"] - }, - "properties": { - "type": "object", - "properties": { - "timestamp": { - "type": "number" - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "text": { - "type": "string" - }, - "include": { - "type": "string" - } - }, - "required": ["timestamp", "sessionID", "text"], - "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, - "EventIndexingStatus": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["indexing.status"] - }, - "properties": { - "type": "object", - "properties": { - "status": { - "$ref": "#/components/schemas/IndexingStatus" - } - }, - "required": ["status"], - "additionalProperties": false - } - }, - "required": ["id", "type", "properties"], - "additionalProperties": false - }, - "SessionInfo": { - "type": "object", - "properties": { - "id": { - "type": "string", - "pattern": "^ses" - }, - "parentID": { - "type": "string", - "pattern": "^ses" - }, - "projectID": { - "type": "string" - }, - "workspaceID": { - "type": "string", - "pattern": "^wrk" - }, - "path": { - "type": "string" - }, - "agent": { - "type": "string" - }, - "model": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "providerID": { - "type": "string" - }, - "variant": { - "type": "string" - } - }, - "required": ["id", "providerID", "variant"], - "additionalProperties": false - }, - "time": { - "type": "object", - "properties": { - "created": { - "type": "number" - }, - "updated": { - "type": "number" - }, - "archived": { - "type": "number" - } - }, - "required": ["created", "updated"], - "additionalProperties": false - }, - "title": { - "type": "string" - } - }, - "required": ["id", "projectID", "time", "title"], - "additionalProperties": false - }, - "SessionDelivery": { - "type": "string", - "enum": ["immediate", "deferred"] - }, - "SessionMessageAgentSwitched": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "metadata": { - "type": "object" - }, - "time": { - "type": "object", - "properties": { - "created": { - "type": "number" - } - }, - "required": ["created"], - "additionalProperties": false - }, - "type": { - "type": "string", - "enum": ["agent-switched"] - }, - "agent": { - "type": "string" - } - }, - "required": ["id", "time", "type", "agent"], - "additionalProperties": false - }, - "SessionMessageModelSwitched": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "metadata": { - "type": "object" - }, - "time": { - "type": "object", - "properties": { - "created": { - "type": "number" - } - }, - "required": ["created"], - "additionalProperties": false - }, - "type": { - "type": "string", - "enum": ["model-switched"] - }, - "model": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "providerID": { - "type": "string" - }, - "variant": { - "type": "string" - } - }, - "required": ["id", "providerID", "variant"], - "additionalProperties": false - } - }, - "required": ["id", "time", "type", "model"], - "additionalProperties": false - }, - "SessionMessageUser": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "metadata": { - "type": "object" - }, - "time": { - "type": "object", - "properties": { - "created": { - "type": "number" - } - }, - "required": ["created"], - "additionalProperties": false - }, - "text": { - "type": "string" - }, - "files": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PromptFileAttachment" - } - }, - "agents": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PromptAgentAttachment" - } - }, - "type": { - "type": "string", - "enum": ["user"] - } - }, - "required": ["id", "time", "text", "type"], - "additionalProperties": false - }, - "SessionMessageSynthetic": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "metadata": { - "type": "object" - }, - "time": { - "type": "object", - "properties": { - "created": { - "type": "number" - } - }, - "required": ["created"], - "additionalProperties": false - }, - "sessionID": { - "type": "string", - "pattern": "^ses" - }, - "text": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["synthetic"] - } - }, - "required": ["id", "time", "sessionID", "text", "type"], - "additionalProperties": false - }, - "SessionMessageShell": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "metadata": { - "type": "object" - }, - "time": { - "type": "object", - "properties": { - "created": { - "type": "number" - }, - "completed": { + "created": { "type": "number" } }, "required": ["created"], "additionalProperties": false - }, - "type": { - "type": "string", - "enum": ["shell"] - }, - "callID": { - "type": "string" - }, - "command": { - "type": "string" - }, - "output": { - "type": "string" - } - }, - "required": ["id", "time", "type", "callID", "command", "output"], - "additionalProperties": false - }, - "SessionMessageAssistantText": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": ["text"] - }, - "text": { - "type": "string" - } - }, - "required": ["type", "text"], - "additionalProperties": false - }, - "SessionMessageAssistantReasoning": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": ["reasoning"] - }, - "id": { - "type": "string" - }, - "text": { - "type": "string" - } - }, - "required": ["type", "id", "text"], - "additionalProperties": false - }, - "SessionMessageToolStatePending": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": ["pending"] - }, - "input": { - "type": "string" - } - }, - "required": ["status", "input"], - "additionalProperties": false - }, - "SessionMessageToolStateRunning": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": ["running"] - }, - "input": { - "type": "object" - }, - "structured": { - "type": "object" - }, - "content": { - "type": "array", - "items": { - "anyOf": [ - { - "$ref": "#/components/schemas/ToolTextContent" - }, - { - "$ref": "#/components/schemas/ToolFileContent" - } - ] - } } }, - "required": ["status", "input", "structured", "content"], + "required": ["type", "reason", "summary", "id", "time"], "additionalProperties": false }, - "SessionMessageToolStateCompleted": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": ["completed"] - }, - "input": { - "type": "object" - }, - "attachments": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PromptFileAttachment" - } + "SessionMessage": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionMessageAgentSwitched" }, - "content": { - "type": "array", - "items": { - "anyOf": [ - { - "$ref": "#/components/schemas/ToolTextContent" - }, - { - "$ref": "#/components/schemas/ToolFileContent" - } - ] - } + { + "$ref": "#/components/schemas/SessionMessageModelSwitched" }, - "structured": { - "type": "object" - } - }, - "required": ["status", "input", "content", "structured"], - "additionalProperties": false - }, - "SessionMessageToolStateError": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": ["error"] + { + "$ref": "#/components/schemas/SessionMessageUser" }, - "input": { - "type": "object" + { + "$ref": "#/components/schemas/SessionMessageSynthetic" }, - "content": { - "type": "array", - "items": { - "anyOf": [ - { - "$ref": "#/components/schemas/ToolTextContent" - }, - { - "$ref": "#/components/schemas/ToolFileContent" - } - ] - } + { + "$ref": "#/components/schemas/SessionMessageShell" }, - "structured": { - "type": "object" + { + "$ref": "#/components/schemas/SessionMessageAssistant" }, - "error": { - "$ref": "#/components/schemas/SessionErrorUnknown" + { + "$ref": "#/components/schemas/SessionMessageCompaction" } - }, - "required": ["status", "input", "content", "structured", "error"], - "additionalProperties": false - }, - "SessionMessageAssistantTool": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": ["tool"] - }, + ] + }, + "ModelV2Info": { + "type": "object", + "properties": { "id": { "type": "string" }, - "name": { + "apiID": { "type": "string" }, - "provider": { - "type": "object", - "properties": { - "executed": { - "type": "boolean" - }, - "metadata": { - "type": "object" - } - }, - "required": ["executed"], - "additionalProperties": false + "providerID": { + "type": "string" }, - "state": { + "family": { + "type": "string" + }, + "name": { + "type": "string" + }, + "endpoint": { "anyOf": [ { - "$ref": "#/components/schemas/SessionMessageToolStatePending" + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["unknown"] + } + }, + "required": ["type"], + "additionalProperties": false }, { - "$ref": "#/components/schemas/SessionMessageToolStateRunning" + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["openai/responses"] + }, + "url": { + "type": "string" + }, + "websocket": { + "type": "boolean" + } + }, + "required": ["type", "url"], + "additionalProperties": false }, { - "$ref": "#/components/schemas/SessionMessageToolStateCompleted" + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["openai/completions"] + }, + "url": { + "type": "string" + }, + "reasoning": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["reasoning_content"] + } + }, + "required": ["type"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["reasoning_details"] + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["type", "url"], + "additionalProperties": false }, { - "$ref": "#/components/schemas/SessionMessageToolStateError" + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["anthropic/messages"] + }, + "url": { + "type": "string" + } + }, + "required": ["type", "url"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["aisdk"] + }, + "package": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": ["type", "package"], + "additionalProperties": false } ] }, - "time": { + "capabilities": { "type": "object", "properties": { - "created": { - "type": "number" - }, - "ran": { - "type": "number" + "tools": { + "type": "boolean" }, - "completed": { - "type": "number" + "input": { + "type": "array", + "items": { + "type": "string" + } }, - "pruned": { - "type": "number" + "output": { + "type": "array", + "items": { + "type": "string" + } } }, - "required": ["created"], + "required": ["tools", "input", "output"], "additionalProperties": false - } - }, - "required": ["type", "id", "name", "state", "time"], - "additionalProperties": false - }, - "SessionMessageAssistant": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "metadata": { - "type": "object" }, - "time": { + "options": { "type": "object", "properties": { - "created": { - "type": "number" + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } }, - "completed": { - "type": "number" - } - }, - "required": ["created"], - "additionalProperties": false - }, - "type": { - "type": "string", - "enum": ["assistant"] - }, - "agent": { - "type": "string" - }, - "model": { - "type": "object", - "properties": { - "id": { - "type": "string" + "body": { + "type": "object" }, - "providerID": { - "type": "string" + "aisdk": { + "type": "object", + "properties": { + "provider": { + "type": "object" + }, + "request": { + "type": "object" + } + }, + "required": ["provider", "request"], + "additionalProperties": false }, "variant": { "type": "string" } }, - "required": ["id", "providerID", "variant"], + "required": ["headers", "body", "aisdk"], "additionalProperties": false }, - "content": { + "variants": { "type": "array", "items": { - "anyOf": [ - { - "$ref": "#/components/schemas/SessionMessageAssistantText" + "type": "object", + "properties": { + "id": { + "type": "string" }, - { - "$ref": "#/components/schemas/SessionMessageAssistantReasoning" + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } }, - { - "$ref": "#/components/schemas/SessionMessageAssistantTool" + "body": { + "type": "object" + }, + "aisdk": { + "type": "object", + "properties": { + "provider": { + "type": "object" + }, + "request": { + "type": "object" + } + }, + "required": ["provider", "request"], + "additionalProperties": false } - ] + }, + "required": ["id", "headers", "body", "aisdk"], + "additionalProperties": false } }, - "snapshot": { + "time": { "type": "object", "properties": { - "start": { - "type": "string" + "released": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": ["NaN"] + }, + { + "type": "string", + "enum": ["Infinity"] + }, + { + "type": "string", + "enum": ["-Infinity"] + }, + { + "type": "string", + "enum": ["Infinity", "-Infinity", "NaN"] + } + ] + } + }, + "required": ["released"], + "additionalProperties": false + }, + "cost": { + "type": "array", + "items": { + "type": "object", + "properties": { + "tier": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["context"] + }, + "size": { + "type": "integer" + } + }, + "required": ["type", "size"], + "additionalProperties": false + }, + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "cache": { + "type": "object", + "properties": { + "read": { + "type": "number" + }, + "write": { + "type": "number" + } + }, + "required": ["read", "write"], + "additionalProperties": false + } }, - "end": { - "type": "string" - } - }, - "additionalProperties": false + "required": ["input", "output", "cache"], + "additionalProperties": false + } }, - "finish": { - "type": "string" + "status": { + "type": "string", + "enum": ["alpha", "beta", "deprecated", "active"] }, - "cost": { - "type": "number" + "enabled": { + "type": "boolean" }, - "tokens": { + "limit": { "type": "object", "properties": { + "context": { + "type": "integer" + }, "input": { - "type": "number" + "type": "integer" }, "output": { - "type": "number" - }, - "reasoning": { - "type": "number" - }, - "cache": { - "type": "object", - "properties": { - "read": { - "type": "number" - }, - "write": { - "type": "number" - } - }, - "required": ["read", "write"], - "additionalProperties": false + "type": "integer" } }, - "required": ["input", "output", "reasoning", "cache"], + "required": ["context", "output"], "additionalProperties": false - }, - "error": { - "$ref": "#/components/schemas/SessionErrorUnknown" } }, - "required": ["id", "time", "type", "agent", "model", "content"], + "required": [ + "id", + "apiID", + "providerID", + "name", + "endpoint", + "capabilities", + "options", + "variants", + "time", + "cost", + "status", + "enabled", + "limit" + ], "additionalProperties": false }, - "SessionMessageCompaction": { + "ProviderV2Info": { "type": "object", "properties": { - "type": { - "type": "string", - "enum": ["compaction"] - }, - "reason": { - "type": "string", - "enum": ["auto", "manual"] - }, - "summary": { + "id": { "type": "string" }, - "include": { + "name": { "type": "string" }, - "id": { - "type": "string" + "enabled": { + "anyOf": [ + { + "type": "boolean", + "enum": [false] + }, + { + "type": "object", + "properties": { + "via": { + "type": "string", + "enum": ["env"] + }, + "name": { + "type": "string" + } + }, + "required": ["via", "name"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "via": { + "type": "string", + "enum": ["auth"] + }, + "service": { + "type": "string" + } + }, + "required": ["via", "service"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "via": { + "type": "string", + "enum": ["custom"] + }, + "data": { + "type": "object" + } + }, + "required": ["via", "data"], + "additionalProperties": false + } + ] }, - "metadata": { - "type": "object" + "env": { + "type": "array", + "items": { + "type": "string" + } }, - "time": { + "endpoint": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["unknown"] + } + }, + "required": ["type"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["openai/responses"] + }, + "url": { + "type": "string" + }, + "websocket": { + "type": "boolean" + } + }, + "required": ["type", "url"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["openai/completions"] + }, + "url": { + "type": "string" + }, + "reasoning": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["reasoning_content"] + } + }, + "required": ["type"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["reasoning_details"] + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["type", "url"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["anthropic/messages"] + }, + "url": { + "type": "string" + } + }, + "required": ["type", "url"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["aisdk"] + }, + "package": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": ["type", "package"], + "additionalProperties": false + } + ] + }, + "options": { "type": "object", "properties": { - "created": { - "type": "number" + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "body": { + "type": "object" + }, + "aisdk": { + "type": "object", + "properties": { + "provider": { + "type": "object" + }, + "request": { + "type": "object" + } + }, + "required": ["provider", "request"], + "additionalProperties": false } }, - "required": ["created"], + "required": ["headers", "body", "aisdk"], "additionalProperties": false } }, - "required": ["type", "reason", "summary", "id", "time"], + "required": ["id", "name", "enabled", "env", "endpoint", "options"], "additionalProperties": false }, - "SessionMessage": { - "anyOf": [ - { - "$ref": "#/components/schemas/SessionMessageAgentSwitched" - }, - { - "$ref": "#/components/schemas/SessionMessageModelSwitched" - }, - { - "$ref": "#/components/schemas/SessionMessageUser" - }, - { - "$ref": "#/components/schemas/SessionMessageSynthetic" - }, - { - "$ref": "#/components/schemas/SessionMessageShell" - }, - { - "$ref": "#/components/schemas/SessionMessageAssistant" - }, - { - "$ref": "#/components/schemas/SessionMessageCompaction" - } - ] - }, "EventTuiToastShow1": { "type": "object", "properties": { @@ -25855,6 +25456,14 @@ "name": "v2 messages", "description": "Experimental v2 message routes." }, + { + "name": "v2 models", + "description": "Experimental v2 model routes." + }, + { + "name": "v2 providers", + "description": "Experimental v2 provider routes." + }, { "name": "tui", "description": "Experimental HttpApi TUI routes." diff --git a/packages/storybook/.storybook/mocks/app/context/global-sync.ts b/packages/storybook/.storybook/mocks/app/context/global-sync.ts index 2eb134d37cd..92622c04acf 100644 --- a/packages/storybook/.storybook/mocks/app/context/global-sync.ts +++ b/packages/storybook/.storybook/mocks/app/context/global-sync.ts @@ -40,3 +40,16 @@ export function useGlobalSync() { }, } } + +export function useQueryOptions() { + return { + agents: (directory: string) => ({ + queryKey: [directory, "agents"], + queryFn: async () => [], + }), + providers: (directory: string | null) => ({ + queryKey: [directory, "providers"], + queryFn: async () => provider, + }), + } +} diff --git a/packages/ui/src/assets/audio/alert-01.mp3 b/packages/ui/src/assets/audio/alert-01.mp3 new file mode 100644 index 00000000000..45c3f6afddf Binary files /dev/null and b/packages/ui/src/assets/audio/alert-01.mp3 differ diff --git a/packages/ui/src/assets/audio/alert-02.mp3 b/packages/ui/src/assets/audio/alert-02.mp3 new file mode 100644 index 00000000000..2aa5cda6acc Binary files /dev/null and b/packages/ui/src/assets/audio/alert-02.mp3 differ diff --git a/packages/ui/src/assets/audio/alert-03.mp3 b/packages/ui/src/assets/audio/alert-03.mp3 new file mode 100644 index 00000000000..69ce3c0dbbb Binary files /dev/null and b/packages/ui/src/assets/audio/alert-03.mp3 differ diff --git a/packages/ui/src/assets/audio/alert-04.mp3 b/packages/ui/src/assets/audio/alert-04.mp3 new file mode 100644 index 00000000000..f4ed6520927 Binary files /dev/null and b/packages/ui/src/assets/audio/alert-04.mp3 differ diff --git a/packages/ui/src/assets/audio/alert-05.mp3 b/packages/ui/src/assets/audio/alert-05.mp3 new file mode 100644 index 00000000000..23be6b0f844 Binary files /dev/null and b/packages/ui/src/assets/audio/alert-05.mp3 differ diff --git a/packages/ui/src/assets/audio/alert-06.mp3 b/packages/ui/src/assets/audio/alert-06.mp3 new file mode 100644 index 00000000000..a0ced67c229 Binary files /dev/null and b/packages/ui/src/assets/audio/alert-06.mp3 differ diff --git a/packages/ui/src/assets/audio/alert-07.mp3 b/packages/ui/src/assets/audio/alert-07.mp3 new file mode 100644 index 00000000000..312a419c573 Binary files /dev/null and b/packages/ui/src/assets/audio/alert-07.mp3 differ diff --git a/packages/ui/src/assets/audio/alert-08.mp3 b/packages/ui/src/assets/audio/alert-08.mp3 new file mode 100644 index 00000000000..6474f935ca4 Binary files /dev/null and b/packages/ui/src/assets/audio/alert-08.mp3 differ diff --git a/packages/ui/src/assets/audio/alert-09.mp3 b/packages/ui/src/assets/audio/alert-09.mp3 new file mode 100644 index 00000000000..c26fb5bf634 Binary files /dev/null and b/packages/ui/src/assets/audio/alert-09.mp3 differ diff --git a/packages/ui/src/assets/audio/alert-10.mp3 b/packages/ui/src/assets/audio/alert-10.mp3 new file mode 100644 index 00000000000..139689fe373 Binary files /dev/null and b/packages/ui/src/assets/audio/alert-10.mp3 differ diff --git a/packages/ui/src/assets/audio/bip-bop-01.mp3 b/packages/ui/src/assets/audio/bip-bop-01.mp3 new file mode 100644 index 00000000000..f518059c54c Binary files /dev/null and b/packages/ui/src/assets/audio/bip-bop-01.mp3 differ diff --git a/packages/ui/src/assets/audio/bip-bop-02.mp3 b/packages/ui/src/assets/audio/bip-bop-02.mp3 new file mode 100644 index 00000000000..b25f80ada6f Binary files /dev/null and b/packages/ui/src/assets/audio/bip-bop-02.mp3 differ diff --git a/packages/ui/src/assets/audio/bip-bop-03.mp3 b/packages/ui/src/assets/audio/bip-bop-03.mp3 new file mode 100644 index 00000000000..adc68c91dc2 Binary files /dev/null and b/packages/ui/src/assets/audio/bip-bop-03.mp3 differ diff --git a/packages/ui/src/assets/audio/bip-bop-04.mp3 b/packages/ui/src/assets/audio/bip-bop-04.mp3 new file mode 100644 index 00000000000..4ef895b3bec Binary files /dev/null and b/packages/ui/src/assets/audio/bip-bop-04.mp3 differ diff --git a/packages/ui/src/assets/audio/bip-bop-05.mp3 b/packages/ui/src/assets/audio/bip-bop-05.mp3 new file mode 100644 index 00000000000..d6ec2e5a1fc Binary files /dev/null and b/packages/ui/src/assets/audio/bip-bop-05.mp3 differ diff --git a/packages/ui/src/assets/audio/bip-bop-06.mp3 b/packages/ui/src/assets/audio/bip-bop-06.mp3 new file mode 100644 index 00000000000..77f4ca99e63 Binary files /dev/null and b/packages/ui/src/assets/audio/bip-bop-06.mp3 differ diff --git a/packages/ui/src/assets/audio/bip-bop-07.mp3 b/packages/ui/src/assets/audio/bip-bop-07.mp3 new file mode 100644 index 00000000000..c41f8c52616 Binary files /dev/null and b/packages/ui/src/assets/audio/bip-bop-07.mp3 differ diff --git a/packages/ui/src/assets/audio/bip-bop-08.mp3 b/packages/ui/src/assets/audio/bip-bop-08.mp3 new file mode 100644 index 00000000000..24af5b0a238 Binary files /dev/null and b/packages/ui/src/assets/audio/bip-bop-08.mp3 differ diff --git a/packages/ui/src/assets/audio/bip-bop-09.mp3 b/packages/ui/src/assets/audio/bip-bop-09.mp3 new file mode 100644 index 00000000000..8eca42c0ebd Binary files /dev/null and b/packages/ui/src/assets/audio/bip-bop-09.mp3 differ diff --git a/packages/ui/src/assets/audio/bip-bop-10.mp3 b/packages/ui/src/assets/audio/bip-bop-10.mp3 new file mode 100644 index 00000000000..056730a1f13 Binary files /dev/null and b/packages/ui/src/assets/audio/bip-bop-10.mp3 differ diff --git a/packages/ui/src/assets/audio/nope-01.mp3 b/packages/ui/src/assets/audio/nope-01.mp3 new file mode 100644 index 00000000000..3ec93f1ffc1 Binary files /dev/null and b/packages/ui/src/assets/audio/nope-01.mp3 differ diff --git a/packages/ui/src/assets/audio/nope-02.mp3 b/packages/ui/src/assets/audio/nope-02.mp3 new file mode 100644 index 00000000000..8cdf890a182 Binary files /dev/null and b/packages/ui/src/assets/audio/nope-02.mp3 differ diff --git a/packages/ui/src/assets/audio/nope-03.mp3 b/packages/ui/src/assets/audio/nope-03.mp3 new file mode 100644 index 00000000000..42442317c37 Binary files /dev/null and b/packages/ui/src/assets/audio/nope-03.mp3 differ diff --git a/packages/ui/src/assets/audio/nope-04.mp3 b/packages/ui/src/assets/audio/nope-04.mp3 new file mode 100644 index 00000000000..8ac496a5c4c Binary files /dev/null and b/packages/ui/src/assets/audio/nope-04.mp3 differ diff --git a/packages/ui/src/assets/audio/nope-05.mp3 b/packages/ui/src/assets/audio/nope-05.mp3 new file mode 100644 index 00000000000..087efb6394f Binary files /dev/null and b/packages/ui/src/assets/audio/nope-05.mp3 differ diff --git a/packages/ui/src/assets/audio/nope-06.mp3 b/packages/ui/src/assets/audio/nope-06.mp3 new file mode 100644 index 00000000000..ed22c1cba2c Binary files /dev/null and b/packages/ui/src/assets/audio/nope-06.mp3 differ diff --git a/packages/ui/src/assets/audio/nope-07.mp3 b/packages/ui/src/assets/audio/nope-07.mp3 new file mode 100644 index 00000000000..4b6dd462c80 Binary files /dev/null and b/packages/ui/src/assets/audio/nope-07.mp3 differ diff --git a/packages/ui/src/assets/audio/nope-08.mp3 b/packages/ui/src/assets/audio/nope-08.mp3 new file mode 100644 index 00000000000..e54577e4357 Binary files /dev/null and b/packages/ui/src/assets/audio/nope-08.mp3 differ diff --git a/packages/ui/src/assets/audio/nope-09.mp3 b/packages/ui/src/assets/audio/nope-09.mp3 new file mode 100644 index 00000000000..26a4ac806d0 Binary files /dev/null and b/packages/ui/src/assets/audio/nope-09.mp3 differ diff --git a/packages/ui/src/assets/audio/nope-10.mp3 b/packages/ui/src/assets/audio/nope-10.mp3 new file mode 100644 index 00000000000..0720993519c Binary files /dev/null and b/packages/ui/src/assets/audio/nope-10.mp3 differ diff --git a/packages/ui/src/assets/audio/nope-11.mp3 b/packages/ui/src/assets/audio/nope-11.mp3 new file mode 100644 index 00000000000..483deefd606 Binary files /dev/null and b/packages/ui/src/assets/audio/nope-11.mp3 differ diff --git a/packages/ui/src/assets/audio/nope-12.mp3 b/packages/ui/src/assets/audio/nope-12.mp3 new file mode 100644 index 00000000000..4b62e62c174 Binary files /dev/null and b/packages/ui/src/assets/audio/nope-12.mp3 differ diff --git a/packages/ui/src/assets/audio/staplebops-01.mp3 b/packages/ui/src/assets/audio/staplebops-01.mp3 new file mode 100644 index 00000000000..b3b34a32fe8 Binary files /dev/null and b/packages/ui/src/assets/audio/staplebops-01.mp3 differ diff --git a/packages/ui/src/assets/audio/staplebops-02.mp3 b/packages/ui/src/assets/audio/staplebops-02.mp3 new file mode 100644 index 00000000000..276429104ef Binary files /dev/null and b/packages/ui/src/assets/audio/staplebops-02.mp3 differ diff --git a/packages/ui/src/assets/audio/staplebops-03.mp3 b/packages/ui/src/assets/audio/staplebops-03.mp3 new file mode 100644 index 00000000000..465017e1058 Binary files /dev/null and b/packages/ui/src/assets/audio/staplebops-03.mp3 differ diff --git a/packages/ui/src/assets/audio/staplebops-04.mp3 b/packages/ui/src/assets/audio/staplebops-04.mp3 new file mode 100644 index 00000000000..18181770ff2 Binary files /dev/null and b/packages/ui/src/assets/audio/staplebops-04.mp3 differ diff --git a/packages/ui/src/assets/audio/staplebops-05.mp3 b/packages/ui/src/assets/audio/staplebops-05.mp3 new file mode 100644 index 00000000000..8046720b0cf Binary files /dev/null and b/packages/ui/src/assets/audio/staplebops-05.mp3 differ diff --git a/packages/ui/src/assets/audio/staplebops-06.mp3 b/packages/ui/src/assets/audio/staplebops-06.mp3 new file mode 100644 index 00000000000..93a71695a7e Binary files /dev/null and b/packages/ui/src/assets/audio/staplebops-06.mp3 differ diff --git a/packages/ui/src/assets/audio/staplebops-07.mp3 b/packages/ui/src/assets/audio/staplebops-07.mp3 new file mode 100644 index 00000000000..e6be5b5c8a7 Binary files /dev/null and b/packages/ui/src/assets/audio/staplebops-07.mp3 differ diff --git a/packages/ui/src/assets/audio/yup-01.mp3 b/packages/ui/src/assets/audio/yup-01.mp3 new file mode 100644 index 00000000000..d4c8dcbee53 Binary files /dev/null and b/packages/ui/src/assets/audio/yup-01.mp3 differ diff --git a/packages/ui/src/assets/audio/yup-02.mp3 b/packages/ui/src/assets/audio/yup-02.mp3 new file mode 100644 index 00000000000..09d4dc6c8be Binary files /dev/null and b/packages/ui/src/assets/audio/yup-02.mp3 differ diff --git a/packages/ui/src/assets/audio/yup-03.mp3 b/packages/ui/src/assets/audio/yup-03.mp3 new file mode 100644 index 00000000000..0406e15edad Binary files /dev/null and b/packages/ui/src/assets/audio/yup-03.mp3 differ diff --git a/packages/ui/src/assets/audio/yup-04.mp3 b/packages/ui/src/assets/audio/yup-04.mp3 new file mode 100644 index 00000000000..533ead91367 Binary files /dev/null and b/packages/ui/src/assets/audio/yup-04.mp3 differ diff --git a/packages/ui/src/assets/audio/yup-05.mp3 b/packages/ui/src/assets/audio/yup-05.mp3 new file mode 100644 index 00000000000..ec7c13d0051 Binary files /dev/null and b/packages/ui/src/assets/audio/yup-05.mp3 differ diff --git a/packages/ui/src/assets/audio/yup-06.mp3 b/packages/ui/src/assets/audio/yup-06.mp3 new file mode 100644 index 00000000000..f611da8e82d Binary files /dev/null and b/packages/ui/src/assets/audio/yup-06.mp3 differ diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index f7db04e664f..dce0a7b74e6 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -1,4 +1,4 @@ -import { splitProps, type ComponentProps } from "solid-js" +import { onMount, splitProps, type ComponentProps } from "solid-js" const icons = { "align-right": ``, @@ -113,6 +113,42 @@ const icons = { "arrow-undo-down": ``, } +const spriteID = "opencode-icon-sprite" +const symbol = (name: keyof typeof icons) => `opencode-icon-${name}` +let spriteInserted = false + +function viewBox(name: keyof typeof icons) { + return name === "magnifying-glass" || name === "arrow-undo-down" ? "0 0 16 16" : "0 0 20 20" +} + +function ensureSprite() { + if (spriteInserted) return + if (typeof document === "undefined") return + if (document.getElementById(spriteID)) { + spriteInserted = true + return + } + const body = document.body as HTMLElement | null + if (!body) return + + const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg") + svg.id = spriteID + svg.setAttribute("aria-hidden", "true") + svg.setAttribute("width", "0") + svg.setAttribute("height", "0") + svg.style.position = "absolute" + svg.style.overflow = "hidden" + svg.style.display = "none" + svg.innerHTML = Object.entries(icons) + .map(([name, path]) => { + const key = name as keyof typeof icons + return `${path}` + }) + .join("") + body.insertBefore(svg, body.firstChild) + spriteInserted = true +} + export interface IconProps extends ComponentProps<"svg"> { name: keyof typeof icons size?: "small" | "normal" | "medium" | "large" @@ -120,7 +156,8 @@ export interface IconProps extends ComponentProps<"svg"> { export function Icon(props: IconProps) { const [local, others] = splitProps(props, ["name", "size", "class", "classList"]) - const viewBox = () => (local.name === "magnifying-glass" ? "0 0 16 16" : "0 0 20 20") + onMount(ensureSprite) + return (
+ > + +
) } diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index ac9566c85f0..d65f97f2137 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -265,6 +265,7 @@ function getDirectory(path: string | undefined) { } import type { IconProps } from "./icon" +import { normalize } from "./session-diff" export type ToolInfo = { icon: IconProps["name"] @@ -1481,7 +1482,7 @@ PART_MAPPING["text"] = function TextPartDisplay(props) { const streaming = createMemo( () => props.message.role === "assistant" && typeof (props.message as AssistantMessage).time.completed !== "number", ) - const text = () => (part().text ?? "").trim() + const text = () => (data.store.part_text_accum_delta?.[part().id] ?? part().text ?? "").trim() // kilocode_change start // Synthetic text parts (e.g. "Initializing snapshot…" from the slow-repo guard) // are transient status indicators, not assistant output — they must never @@ -1555,11 +1556,12 @@ PART_MAPPING["text"] = function TextPartDisplay(props) { } PART_MAPPING["reasoning"] = function ReasoningPartDisplay(props) { + const data = useData() const part = () => props.part as ReasoningPart const streaming = createMemo( () => props.message.role === "assistant" && typeof (props.message as AssistantMessage).time.completed !== "number", ) - const text = () => part().text.trim() + const text = () => (data.store.part_text_accum_delta?.[part().id] ?? part().text).trim() return ( @@ -1773,9 +1775,13 @@ ToolRegistry.register({ const title = createMemo(() => agent().name ?? i18n.t("ui.tool.agent.default")) const tone = createMemo(() => agent().color) const subtitle = createMemo(() => { - const value = props.input.description - if (typeof value === "string" && value) return value - return childSessionId() + const value = + typeof props.input.description === "string" && props.input.description + ? props.input.description + : childSessionId() + if (!value) return value + if (props.metadata.background === true) return `${value} (background)` + return value }) const running = createMemo(() => props.status === "pending" || props.status === "running") @@ -1915,6 +1921,31 @@ ToolRegistry.register({ const path = createMemo(() => props.metadata?.filediff?.file || props.input.filePath || "") const filename = () => getFilename(props.input.filePath ?? "") const pending = () => props.status === "pending" || props.status === "running" + + const fileCompProps = createMemo(() => { + try { + if (props.metadata?.filediff) { + const diff = normalize({ + ...props.metadata?.filediff, + status: "modified", + }) + const fileDiff = diff.fileDiff + if (fileDiff) return { fileDiff, hunkSeparators: fileDiff.isPartial ? "simple" : "line-info-basic" } + } + } catch {} + + return { + before: { + name: props.metadata?.filediff?.file || props.input.filePath, + contents: props.metadata?.filediff?.before || props.input.oldString || "", + }, + after: { + name: props.metadata?.filediff?.file || props.input.filePath, + contents: props.metadata?.filediff?.after || props.input.newString || "", + }, + } + }) + return (
- +
@@ -2148,7 +2168,12 @@ ToolRegistry.register({
- +
diff --git a/packages/ui/src/components/session-diff.ts b/packages/ui/src/components/session-diff.ts index 4ebddf6e975..72f20e02fe2 100644 --- a/packages/ui/src/components/session-diff.ts +++ b/packages/ui/src/components/session-diff.ts @@ -1,4 +1,4 @@ -import { parseDiffFromFile, type FileDiffMetadata } from "@pierre/diffs" +import { parseDiffFromFile, parsePatchFiles, type FileDiffMetadata } from "@pierre/diffs" import { formatPatch, parsePatch, structuredPatch } from "diff" import type { SnapshotFileDiff, VcsFileDiff } from "@kilocode/sdk/v2" @@ -20,6 +20,7 @@ export type DiffText = { before: string after: string patch: string + patchIsPartial: boolean } export type ViewDiff = { @@ -43,6 +44,8 @@ export function contents(diff: ReviewDiff): DiffText { const afterLines: Array<{ text: string; newline: boolean }> = [] let previous: "-" | "+" | " " | undefined + const patchIsPartial = patch.hunks.every((h) => h.oldStart > 1) + for (const hunk of patch.hunks) { for (const line of hunk.lines) { if (line.startsWith("\\")) { @@ -76,9 +79,10 @@ export function contents(diff: ReviewDiff): DiffText { before: beforeLines.map((line) => line.text + (line.newline ? "\n" : "")).join(""), after: afterLines.map((line) => line.text + (line.newline ? "\n" : "")).join(""), patch: diff.patch, + patchIsPartial, } } catch { - return { before: "", after: "", patch: diff.patch } + return { before: "", after: "", patch: diff.patch, patchIsPartial: false } } } return { @@ -95,21 +99,26 @@ export function contents(diff: ReviewDiff): DiffText { { context: Number.MAX_SAFE_INTEGER }, ), ), + patchIsPartial: false, } } // kilocode_change end -function file(file: string, patch: string, before: string, after: string) { +function file(file: string, patch: string, before: string, after: string, partial = false) { const hit = cache.get(patch) if (hit) return hit - const value = parseDiffFromFile({ name: file, contents: before }, { name: file, contents: after }) + let value: FileDiffMetadata | undefined + if (partial) value = parsePatchFiles(patch)[0]?.files[0] + if (value === undefined) value = parseDiffFromFile({ name: file, contents: before }, { name: file, contents: after }) + cache.set(patch, value) return value } export function normalize(diff: ReviewDiff): ViewDiff { const next = contents(diff) // kilocode_change + const fileDiff = file(diff.file, next.patch, next.before, next.after, next.patchIsPartial) return { file: diff.file, // kilocode_change patch: next.patch, // kilocode_change @@ -118,7 +127,7 @@ export function normalize(diff: ReviewDiff): ViewDiff { additions: diff.additions, deletions: diff.deletions, status: diff.status, - fileDiff: file(diff.file, next.patch, next.before, next.after), + fileDiff, } } diff --git a/packages/ui/src/context/data.tsx b/packages/ui/src/context/data.tsx index c5d913c2ee6..dbb2ccb908e 100644 --- a/packages/ui/src/context/data.tsx +++ b/packages/ui/src/context/data.tsx @@ -24,6 +24,9 @@ type Data = { part: { [messageID: string]: Part[] } + part_text_accum_delta?: { + [partID: string]: string + } } export type NavigateToSessionFn = (sessionID: string) => void diff --git a/patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch b/patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch index 0a34ba7ed75..aa4577e10e7 100644 --- a/patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch +++ b/patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch @@ -1,11 +1,282 @@ diff --git a/photon_rs.js b/photon_rs.js -index 8f4144d..b83e9a9 100644 +index 8f4144d..29a964a 100644 --- a/photon_rs.js +++ b/photon_rs.js -@@ -4509,7 +4509,8 @@ module.exports.__wbindgen_init_externref_table = function() { - ; +@@ -1,6 +1,7 @@ + + let imports = {}; +-imports['__wbindgen_placeholder__'] = module.exports; ++const __wbindgen_placeholder__ = {}; ++imports['__wbindgen_placeholder__'] = __wbindgen_placeholder__; + let wasm; + const { TextEncoder, TextDecoder } = require(`util`); + +@@ -4272,12 +4273,12 @@ class Rgba { + } + module.exports.Rgba = Rgba; + +-module.exports.__wbg_new_abda76e883ba8a5f = function() { ++__wbindgen_placeholder__.__wbg_new_abda76e883ba8a5f = function() { + const ret = new Error(); + return ret; + }; + +-module.exports.__wbg_stack_658279fe44541cf6 = function(arg0, arg1) { ++__wbindgen_placeholder__.__wbg_stack_658279fe44541cf6 = function(arg0, arg1) { + const ret = arg1.stack; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; +@@ -4285,7 +4286,7 @@ module.exports.__wbg_stack_658279fe44541cf6 = function(arg0, arg1) { + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }; + +-module.exports.__wbg_error_f851667af71bcfc6 = function(arg0, arg1) { ++__wbindgen_placeholder__.__wbg_error_f851667af71bcfc6 = function(arg0, arg1) { + let deferred0_0; + let deferred0_1; + try { +@@ -4297,7 +4298,7 @@ module.exports.__wbg_error_f851667af71bcfc6 = function(arg0, arg1) { + } + }; + +-module.exports.__wbg_instanceof_Window_c4b70662a0d2c5ec = function(arg0) { ++__wbindgen_placeholder__.__wbg_instanceof_Window_c4b70662a0d2c5ec = function(arg0) { + let result; + try { + result = arg0 instanceof Window; +@@ -4308,42 +4309,42 @@ module.exports.__wbg_instanceof_Window_c4b70662a0d2c5ec = function(arg0) { + return ret; + }; + +-module.exports.__wbg_document_e5c1786dea6542e4 = function(arg0) { ++__wbindgen_placeholder__.__wbg_document_e5c1786dea6542e4 = function(arg0) { + const ret = arg0.document; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + +-module.exports.__wbg_body_e70ae6abd01ae584 = function(arg0) { ++__wbindgen_placeholder__.__wbg_body_e70ae6abd01ae584 = function(arg0) { + const ret = arg0.body; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + +-module.exports.__wbg_createElement_5d4c76f218b78145 = function() { return handleError(function (arg0, arg1, arg2) { ++__wbindgen_placeholder__.__wbg_createElement_5d4c76f218b78145 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = arg0.createElement(getStringFromWasm0(arg1, arg2)); + return ret; + }, arguments) }; + +-module.exports.__wbg_width_4c6f0048d64cf86b = function(arg0) { ++__wbindgen_placeholder__.__wbg_width_4c6f0048d64cf86b = function(arg0) { + const ret = arg0.width; + return ret; + }; + +-module.exports.__wbg_height_21f0d3fd8f753394 = function(arg0) { ++__wbindgen_placeholder__.__wbg_height_21f0d3fd8f753394 = function(arg0) { + const ret = arg0.height; + return ret; + }; + +-module.exports.__wbg_width_79e0847ed5883b03 = function(arg0) { ++__wbindgen_placeholder__.__wbg_width_79e0847ed5883b03 = function(arg0) { + const ret = arg0.width; + return ret; + }; + +-module.exports.__wbg_height_e4e4e4779f8feac0 = function(arg0) { ++__wbindgen_placeholder__.__wbg_height_e4e4e4779f8feac0 = function(arg0) { + const ret = arg0.height; + return ret; + }; + +-module.exports.__wbg_data_fda507064d127f5b = function(arg0, arg1) { ++__wbindgen_placeholder__.__wbg_data_fda507064d127f5b = function(arg0, arg1) { + const ret = arg1.data; + const ptr1 = passArray8ToWasm0(ret, wasm.__wbindgen_malloc); + const len1 = WASM_VECTOR_LEN; +@@ -4351,12 +4352,12 @@ module.exports.__wbg_data_fda507064d127f5b = function(arg0, arg1) { + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }; + +-module.exports.__wbg_newwithu8clampedarrayandsh_1fddccb3a94a5e05 = function() { return handleError(function (arg0, arg1, arg2, arg3) { ++__wbindgen_placeholder__.__wbg_newwithu8clampedarrayandsh_1fddccb3a94a5e05 = function() { return handleError(function (arg0, arg1, arg2, arg3) { + const ret = new ImageData(getClampedArrayU8FromWasm0(arg0, arg1), arg2 >>> 0, arg3 >>> 0); + return ret; + }, arguments) }; + +-module.exports.__wbg_instanceof_CanvasRenderingContext2d_3abbe7ec7af32cae = function(arg0) { ++__wbindgen_placeholder__.__wbg_instanceof_CanvasRenderingContext2d_3abbe7ec7af32cae = function(arg0) { + let result; + try { + result = arg0 instanceof CanvasRenderingContext2D; +@@ -4367,24 +4368,24 @@ module.exports.__wbg_instanceof_CanvasRenderingContext2d_3abbe7ec7af32cae = fun + return ret; + }; + +-module.exports.__wbg_drawImage_fede06db74e39a60 = function() { return handleError(function (arg0, arg1, arg2, arg3) { ++__wbindgen_placeholder__.__wbg_drawImage_fede06db74e39a60 = function() { return handleError(function (arg0, arg1, arg2, arg3) { + arg0.drawImage(arg1, arg2, arg3); + }, arguments) }; + +-module.exports.__wbg_drawImage_f395c8e43c79a909 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) { ++__wbindgen_placeholder__.__wbg_drawImage_f395c8e43c79a909 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) { + arg0.drawImage(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); + }, arguments) }; + +-module.exports.__wbg_getImageData_5e1c242046e6b59e = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { ++__wbindgen_placeholder__.__wbg_getImageData_5e1c242046e6b59e = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { + const ret = arg0.getImageData(arg1, arg2, arg3, arg4); + return ret; + }, arguments) }; + +-module.exports.__wbg_putImageData_a8b3e177ee06d521 = function() { return handleError(function (arg0, arg1, arg2, arg3) { ++__wbindgen_placeholder__.__wbg_putImageData_a8b3e177ee06d521 = function() { return handleError(function (arg0, arg1, arg2, arg3) { + arg0.putImageData(arg1, arg2, arg3); + }, arguments) }; + +-module.exports.__wbg_instanceof_HtmlCanvasElement_25d964a0dde6717e = function(arg0) { ++__wbindgen_placeholder__.__wbg_instanceof_HtmlCanvasElement_25d964a0dde6717e = function(arg0) { + let result; + try { + result = arg0 instanceof HTMLCanvasElement; +@@ -4395,93 +4396,93 @@ module.exports.__wbg_instanceof_HtmlCanvasElement_25d964a0dde6717e = function(a + return ret; + }; + +-module.exports.__wbg_width_dc225e55343b745e = function(arg0) { ++__wbindgen_placeholder__.__wbg_width_dc225e55343b745e = function(arg0) { + const ret = arg0.width; + return ret; + }; + +-module.exports.__wbg_setwidth_488780db69b08846 = function(arg0, arg1) { ++__wbindgen_placeholder__.__wbg_setwidth_488780db69b08846 = function(arg0, arg1) { + arg0.width = arg1 >>> 0; + }; + +-module.exports.__wbg_height_3a8bec2f3fe71b26 = function(arg0) { ++__wbindgen_placeholder__.__wbg_height_3a8bec2f3fe71b26 = function(arg0) { + const ret = arg0.height; + return ret; + }; + +-module.exports.__wbg_setheight_1761808c18403921 = function(arg0, arg1) { ++__wbindgen_placeholder__.__wbg_setheight_1761808c18403921 = function(arg0, arg1) { + arg0.height = arg1 >>> 0; + }; + +-module.exports.__wbg_getContext_fc99dbd3a9a7e318 = function() { return handleError(function (arg0, arg1, arg2) { ++__wbindgen_placeholder__.__wbg_getContext_fc99dbd3a9a7e318 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = arg0.getContext(getStringFromWasm0(arg1, arg2)); + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }, arguments) }; + +-module.exports.__wbg_settextContent_f82a86a8df347e1c = function(arg0, arg1, arg2) { ++__wbindgen_placeholder__.__wbg_settextContent_f82a86a8df347e1c = function(arg0, arg1, arg2) { + arg0.textContent = arg1 === 0 ? undefined : getStringFromWasm0(arg1, arg2); + }; + +-module.exports.__wbg_appendChild_fa3b00dade9fc4cf = function() { return handleError(function (arg0, arg1) { ++__wbindgen_placeholder__.__wbg_appendChild_fa3b00dade9fc4cf = function() { return handleError(function (arg0, arg1) { + const ret = arg0.appendChild(arg1); + return ret; + }, arguments) }; + +-module.exports.__wbg_newnoargs_e643855c6572a4a8 = function(arg0, arg1) { ++__wbindgen_placeholder__.__wbg_newnoargs_e643855c6572a4a8 = function(arg0, arg1) { + const ret = new Function(getStringFromWasm0(arg0, arg1)); + return ret; + }; + +-module.exports.__wbg_call_f96b398515635514 = function() { return handleError(function (arg0, arg1) { ++__wbindgen_placeholder__.__wbg_call_f96b398515635514 = function() { return handleError(function (arg0, arg1) { + const ret = arg0.call(arg1); + return ret; + }, arguments) }; + +-module.exports.__wbg_self_b9aad7f1c618bfaf = function() { return handleError(function () { ++__wbindgen_placeholder__.__wbg_self_b9aad7f1c618bfaf = function() { return handleError(function () { + const ret = self.self; + return ret; + }, arguments) }; + +-module.exports.__wbg_window_55e469842c98b086 = function() { return handleError(function () { ++__wbindgen_placeholder__.__wbg_window_55e469842c98b086 = function() { return handleError(function () { + const ret = window.window; + return ret; + }, arguments) }; + +-module.exports.__wbg_globalThis_d0957e302752547e = function() { return handleError(function () { ++__wbindgen_placeholder__.__wbg_globalThis_d0957e302752547e = function() { return handleError(function () { + const ret = globalThis.globalThis; + return ret; + }, arguments) }; + +-module.exports.__wbg_global_ae2f87312b8987fb = function() { return handleError(function () { ++__wbindgen_placeholder__.__wbg_global_ae2f87312b8987fb = function() { return handleError(function () { + const ret = global.global; + return ret; + }, arguments) }; + +-module.exports.__wbindgen_is_undefined = function(arg0) { ++__wbindgen_placeholder__.__wbindgen_is_undefined = function(arg0) { + const ret = arg0 === undefined; + return ret; + }; + +-module.exports.__wbg_buffer_fcbfb6d88b2732e9 = function(arg0) { ++__wbindgen_placeholder__.__wbg_buffer_fcbfb6d88b2732e9 = function(arg0) { + const ret = arg0.buffer; + return ret; + }; + +-module.exports.__wbg_new_bc5d9aad3f9ac80e = function(arg0) { ++__wbindgen_placeholder__.__wbg_new_bc5d9aad3f9ac80e = function(arg0) { + const ret = new Uint8Array(arg0); + return ret; + }; + +-module.exports.__wbg_set_4b3aa8445ac1e91c = function(arg0, arg1, arg2) { ++__wbindgen_placeholder__.__wbg_set_4b3aa8445ac1e91c = function(arg0, arg1, arg2) { + arg0.set(arg1, arg2 >>> 0); + }; + +-module.exports.__wbg_length_d9c4ded7e708c6a1 = function(arg0) { ++__wbindgen_placeholder__.__wbg_length_d9c4ded7e708c6a1 = function(arg0) { + const ret = arg0.length; + return ret; + }; + +-module.exports.__wbindgen_debug_string = function(arg0, arg1) { ++__wbindgen_placeholder__.__wbindgen_debug_string = function(arg0, arg1) { + const ret = debugString(arg1); + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; +@@ -4489,16 +4490,16 @@ module.exports.__wbindgen_debug_string = function(arg0, arg1) { + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }; + +-module.exports.__wbindgen_throw = function(arg0, arg1) { ++__wbindgen_placeholder__.__wbindgen_throw = function(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); + }; + +-module.exports.__wbindgen_memory = function() { ++__wbindgen_placeholder__.__wbindgen_memory = function() { + const ret = wasm.memory; + return ret; }; +-module.exports.__wbindgen_init_externref_table = function() { ++__wbindgen_placeholder__.__wbindgen_init_externref_table = function() { + const table = wasm.__wbindgen_export_2; + const offset = table.grow(4); + table.set(0, undefined); +@@ -4509,7 +4510,8 @@ module.exports.__wbindgen_init_externref_table = function() { + ; + }; + -const path = require('path').join(__dirname, 'photon_rs_bg.wasm'); +// Allow Kilo's Bun compiled binary to point photon-node at its embedded wasm asset. +const path = globalThis.__KILOCODE_PHOTON_WASM_PATH || require('path').join(__dirname, 'photon_rs_bg.wasm'); diff --git a/script/check-opencode-promise-facades.ts b/script/check-opencode-promise-facades.ts index 44e0742f0c4..01d5f294602 100644 --- a/script/check-opencode-promise-facades.ts +++ b/script/check-opencode-promise-facades.ts @@ -27,30 +27,19 @@ const allow: Record = { "cli/cmd/tui/config/tui.ts": "separately tracked TUI config facade", "installation/index.ts": "existing installation facade outside #10655", "session/compaction.ts": "existing compaction facade outside #10655", - "sync/index.ts": "sync event runtime boundary", } const testAllow: Record = { - "config/agent-color.test.ts": { count: 2, reason: "existing runtime integration test" }, - "config/tui.test.ts": { count: 3, reason: "existing runtime integration test" }, - "control-plane/workspace.test.ts": { count: 11, reason: "existing runtime integration test" }, - "effect/app-runtime-logger.test.ts": { count: 6, reason: "tests AppRuntime behavior" }, + "control-plane/workspace.test.ts": { count: 3, reason: "existing runtime integration test" }, "kilocode/config-resilience.test.ts": { count: 4, reason: "existing runtime integration test" }, "kilocode/config-validation.test.ts": { count: 2, reason: "existing runtime integration test" }, "kilocode/plan-followup.test.ts": { count: 4, reason: "existing runtime integration test" }, "kilocode/server/config-overlay.test.ts": { count: 3, reason: "server config cache integration test" }, "kilocode/session/platform-attribution.test.ts": { count: 5, reason: "existing runtime integration test" }, "kilocode/session-prompt-queue.test.ts": { count: 5, reason: "prompt queue legacy instance bridge regression" }, - "kilocode/session/session.test.ts": { count: 4, reason: "existing runtime integration test" }, - "mcp/headers.test.ts": { count: 4, reason: "existing runtime integration test" }, - "mcp/oauth-browser.test.ts": { count: 4, reason: "existing runtime integration test" }, - "permission-task.test.ts": { count: 2, reason: "existing runtime integration test" }, - "project/vcs.test.ts": { count: 14, reason: "existing runtime integration test" }, + "kilocode/session/session.test.ts": { count: 5, reason: "existing runtime integration test" }, "provider/amazon-bedrock.test.ts": { count: 2, reason: "existing runtime integration test" }, "provider/provider.test.ts": { count: 3, reason: "existing runtime integration test" }, - "pty/pty-output-isolation.test.ts": { count: 4, reason: "existing runtime integration test" }, - "pty/pty-session.test.ts": { count: 3, reason: "existing runtime integration test" }, - "pty/pty-shell.test.ts": { count: 4, reason: "existing runtime integration test" }, "session/llm.test.ts": { count: 2, reason: "existing runtime integration test" }, "tool/recall.test.ts": { count: 10, reason: "existing runtime integration test" }, } diff --git a/specs/v2/api.html b/specs/v2/api.html new file mode 100644 index 00000000000..147d24f58bb --- /dev/null +++ b/specs/v2/api.html @@ -0,0 +1,1161 @@ + + + + + + opencode v2 API + + + +
+
+
+
opencode v2
+

API map

+
+
+

+ A single /api route surface for simple clients and multi-directory frontends. The important + design question is not route nesting; it is where runtime context comes from. +

+
+ Server scoped + Request context + Session pinned +
+
+
+ +
+
+ Everything has one canonical route. Some routes are server-scoped; runtime routes use context; session item + routes use the session. +

+ Server-scoped routes manage the whole server: projects, workspace lifecycle, and auth accounts. Runtime + context is for anything resolved from an active directory, including config, provider capabilities, tools, + files, and VCS. +

+
+
+
+ +
+
+

Context Model

+ + API context resolution + + Non-session routes resolve from request context, session item routes resolve from session storage. + + + + + + + + + Non-session route + /api/file, /api/vcs/status + + + Request context + query params or default runtime + + + Runtime context + directory + workspaceID? + + + Session item route + /api/session/:id/prompt + + + Session row + contains pinned context + + + Runtime context + directory + workspaceID? + +
+ +
+

Request-context calls

+

+ These calls operate against a directory, optionally through a workspace. Simple clients omit context and use + the default runtime. +

+
GET /api/fs/tree?path=.&directory=/repo/app&workspace=ws_123
+
+ +
+

Session-pinned calls

+

+ These calls never take request context. The session is already pinned to the directory and workspace it was + created in. +

+
POST /api/session/ses_123/prompt
+
+// server resolves
+sessionID -> { directory, workspaceID? }
+
+
+ +
+
+

Operation Inventory

+

+ The SDK is the source of truth. HTTP routes are mounts for RPC-style operations. + server operations do not use runtime context. + request operations use request/default runtime context from + directory and workspace query parameters. + session operations use pinned session context and should not accept + context input. +

+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OperationInputContextHTTP mountPurpose
agent.list{}requestGET /api/agentAvailable agents.
auth.activate{ accountID: AccountID }serverPOST /api/auth/:accountID/activateSet the account as active for its service.
auth.create + { serviceID: ServiceID credential: | { type: "oauth", refresh: string, access: string, expires: + number } | { type: "api", key: string, metadata?: Record<string, string> } description?: + string active?: boolean } + serverPOST /api/authCreate an auth account.
auth.delete{ accountID: AccountID }serverDELETE /api/auth/:accountIDRemove an auth account.
auth.get{ accountID: AccountID }serverGET /api/auth/:accountIDGet one auth account.
auth.list{ serviceID?: ServiceID }serverGET /api/authList saved auth accounts. Response includes active account mapping.
auth.update + { accountID: AccountID description?: string credential?: | { type: "oauth", refresh: string, + access: string, expires: number } | { type: "api", key: string, metadata?: Record<string, + string> } } + serverPATCH /api/auth/:accountIDUpdate account description or credential.
catalog.model.get{ providerID: ProviderID modelID: ModelID }serverGET /api/catalog/model/:providerID/:modelIDGet one catalog model.
catalog.model.list{}serverGET /api/catalog/modelList flattened catalog models.
command.list{}requestGET /api/commandAvailable commands.
config.get{}requestGET /api/configResolved config.
config.update{ config: Config }requestPATCH /api/configUpdate config.
event.subscribe{}requestGET /api/eventServer-sent events for the resolved runtime context.
formatter.status{}requestGET /api/formatterFormatter status.
fs.file{ path: string }requestGET /api/fs/fileRead one file.
fs.grep{ pattern: string include?: string limit?: number }requestPOST /api/fs/grepSearch file contents.
fs.search{ query: string type?: "file" | "directory" limit?: number }requestPOST /api/fs/searchSearch paths by name.
fs.tree{ path: string }requestGET /api/fs/treeBrowse a directory.
lsp.status{}requestGET /api/lspLSP status.
mcp.prompt.list{}requestGET /api/mcp/promptList MCP prompts.
mcp.prompt.render + { server: string name: string arguments?: Record<string, string> } + requestPOST /api/mcp/prompt/renderRender one MCP prompt.
mcp.resource.list{}requestGET /api/mcp/resourceList MCP resources.
mcp.resource.read{ server: string uri: string }requestGET /api/mcp/resource/readRead one MCP resource.
mcp.server.create + { name: string config: | { type: "local", command: string, arguments?: string[], environment?: + Record<string, string> } | { type: "remote", url: string, headers?: Record<string, + string>, oauth?: boolean | object } } + requestPOST /api/mcp/serverAdd an MCP server to runtime config.
mcp.server.list{}requestGET /api/mcp/serverList MCP servers with status and auth state.
mcp.server.oauth.callback{ name: string code: string }requestPOST /api/mcp/server/:name/oauth/callbackComplete MCP OAuth.
mcp.server.oauth.delete{ name: string }requestDELETE /api/mcp/server/:name/oauthRemove MCP OAuth credentials.
mcp.server.oauth.start{ name: string }requestPOST /api/mcp/server/:name/oauthStart MCP OAuth.
permission.list{}requestGET /api/permissionPending permission requests.
permission.reply{ permissionID: PermissionID response: PermissionReply }requestPOST /api/permission/:permissionID/replyReply to a permission request.
project.get{ projectID: ProjectID }serverGET /api/project/:projectIDGet project metadata.
project.list{}serverGET /api/projectList projects known to this server.
project.update + { projectID: ProjectID name?: string icon?: string commands?: Array<{ name: string command: + string }> } + serverPATCH /api/project/:projectIDUpdate project metadata.
provider.list{}requestGET /api/providerProvider inventory for the runtime context.
pty.create{ command?: string cwd?: string shell?: string }requestPOST /api/ptyCreate PTY in the runtime context.
pty.delete{ ptyID: PtyID }requestDELETE /api/pty/:ptyIDDelete PTY.
pty.get{ ptyID: PtyID }requestGET /api/pty/:ptyIDGet PTY info.
pty.list{}requestGET /api/ptyList PTYs for the runtime.
pty.update + { ptyID: PtyID title?: string size?: { columns: number, rows: number } } + requestPATCH /api/pty/:ptyIDUpdate PTY.
question.list{}requestGET /api/questionPending user questions.
question.reject{ questionID: QuestionID }requestPOST /api/question/:questionID/rejectReject a question.
question.reply{ questionID: QuestionID response: QuestionResponse }requestPOST /api/question/:questionID/replyReply to a question.
session.compact{ sessionID: SessionID }sessionPOST /api/session/:sessionID/compactCompact the session conversation.
session.context{ sessionID: SessionID }sessionGET /api/session/:sessionID/contextReturn active context messages after the last compaction.
session.create + { title?: string agent?: string model?: { providerID: ProviderID, modelID: ModelID } permission?: + PermissionRule[] } + requestPOST /api/sessionCreate a session pinned to resolved runtime context.
session.delete{ sessionID: SessionID }sessionDELETE /api/session/:sessionIDDelete a session.
session.diff{ sessionID: SessionID }sessionGET /api/session/:sessionID/diffReturn session diff summary.
session.get{ sessionID: SessionID }sessionGET /api/session/:sessionIDGet one session.
session.list + { limit?: number order?: "asc" | "desc" path?: string roots?: boolean start?: number search?: + string cursor?: string } + requestGET /api/sessionList sessions for the current runtime context by default.
session.message.list + { sessionID: SessionID limit?: number order?: "asc" | "desc" cursor?: string } + sessionGET /api/session/:sessionID/messagePage through session messages.
session.prompt + { sessionID: SessionID prompt: Prompt delivery?: "immediate" | "deferred" } + sessionPOST /api/session/:sessionID/promptCreate a user message and queue the agent loop.
session.todo{ sessionID: SessionID }sessionGET /api/session/:sessionID/todoReturn todos associated with the session.
session.update + { sessionID: SessionID title?: string archived?: number permission?: PermissionRule[] } + sessionPATCH /api/session/:sessionIDUpdate title, archival state, or session metadata.
session.wait{ sessionID: SessionID }sessionPOST /api/session/:sessionID/waitWait until the session is idle.
skill.list{}requestGET /api/skillAvailable skills.
vcs.diff{ format?: "json" | "patch" mode?: "worktree" | "default" }requestGET /api/vcs/diffDiff for the runtime directory.
vcs.get{}requestGET /api/vcsVCS metadata.
vcs.patch{ patch: string }requestPOST /api/vcs/patchApply a patch to the runtime directory.
vcs.status{}requestGET /api/vcs/statusChanged files.
workspace.create + { projectID?: ProjectID name?: string directory?: string type: string metadata?: Record<string, + unknown> } + serverPOST /api/workspaceCreate or register a workspace.
workspace.delete{ workspaceID: WorkspaceID }serverDELETE /api/workspace/:workspaceIDRemove a workspace registration.
workspace.get{ workspaceID: WorkspaceID }serverGET /api/workspace/:workspaceIDGet workspace metadata.
workspace.list{ projectID?: ProjectID }serverGET /api/workspaceList workspaces, optionally filtered by project.
workspace.status{}serverGET /api/workspace/statusConnection/lifecycle status for all workspaces. Needs team discussion.
workspace.sync{}serverPOST /api/workspace/syncSync workspace metadata from adapters. Needs team discussion.
workspace.update + { workspaceID: WorkspaceID name?: string metadata?: Record<string, unknown> archived?: + boolean } + serverPATCH /api/workspace/:workspaceIDUpdate workspace metadata or lifecycle state.
workspace.warp + { workspaceID?: WorkspaceID sessionID: SessionID copyChanges: boolean } + serverPOST /api/workspace/warpMove a session into or out of a workspace. Needs team discussion.
+
+
+ +
+
+

Event Envelope

+

+ Every event uses the same envelope. Resource identity belongs in payload. Runtime identity + belongs in context. +

+
+
type ApiEvent<Payload> = {
+  id: string
+  type: string
+  time: number
+  context: {
+    directory: string
+    workspaceID?: string
+  }
+  payload: Payload
+}
+
{
+  "id": "evt_01",
+  "type": "message.part.delta",
+  "time": 1760000000000,
+  "context": {
+    "directory": "/repo/app",
+    "workspaceID": "ws_123"
+  },
+  "payload": {
+    "sessionID": "ses_123",
+    "messageID": "msg_456",
+    "partID": "part_789",
+    "field": "text",
+    "delta": "hello"
+  }
+}
+
+
+
+ +
+
+

Frontend Sync Store

+

+ A frontend can keep one giant store like the current TUI. Runtime data is partitioned by + contextKey. Durable entities such as sessions and messages are keyed by their own IDs. +

+
type RuntimeContext = {
+  directory: string
+  workspaceID?: string
+}
+
+type ContextKey = string
+type SessionID = string
+type MessageID = string
+
+type SyncStore = {
+  status: "loading" | "partial" | "complete"
+
+  shared: {
+    provider: Provider[]
+    provider_default: Record<string, string>
+    provider_next: ProviderListResponse
+    provider_auth: Record<string, ProviderAuthMethod[]>
+    console_state: ConsoleState
+  }
+
+  contexts: Record<
+    ContextKey,
+    {
+      context: RuntimeContext
+
+      config: Config
+      agent: Agent[]
+      command: Command[]
+      lsp: LspStatus[]
+      formatter: FormatterStatus[]
+      vcs: VcsInfo | undefined
+      mcp: Record<string, McpStatus>
+      mcp_resource: Record<string, McpResource>
+
+      session: SessionID[]
+      session_status: Record<SessionID, SessionStatus>
+    }
+  >
+
+  session: Record<SessionID, Session & { context: RuntimeContext }>
+  session_diff: Record<SessionID, Snapshot.FileDiff[]>
+  todo: Record<SessionID, Todo[]>
+  permission: Record<SessionID, PermissionRequest[]>
+  question: Record<SessionID, QuestionRequest[]>
+
+  message: Record<SessionID, Message[]>
+  part: Record<MessageID, Part[]>
+}
+
+function contextKey(context: RuntimeContext) {
+  return `${context.workspaceID ?? "local"}:${context.directory}`
+}
+
+
+
+ + diff --git a/specs/v2/provider-model.md b/specs/v2/provider-model.md new file mode 100644 index 00000000000..fb4598b58fd --- /dev/null +++ b/specs/v2/provider-model.md @@ -0,0 +1,330 @@ +# Provider and Model Catalog + +## Provider Schema + +```ts +export const ID = Schema.String.pipe( + Schema.brand("ProviderV2.ID"), + withStatics((schema) => ({ + opencode: schema.make("opencode"), + anthropic: schema.make("anthropic"), + openai: schema.make("openai"), + google: schema.make("google"), + googleVertex: schema.make("google-vertex"), + githubCopilot: schema.make("github-copilot"), + amazonBedrock: schema.make("amazon-bedrock"), + azure: schema.make("azure"), + openrouter: schema.make("openrouter"), + mistral: schema.make("mistral"), + gitlab: schema.make("gitlab"), + })), +) +export type ID = typeof ID.Type + +const OpenAIResponses = Schema.Struct({ + type: Schema.Literal("openai/responses"), + url: Schema.String, + websocket: Schema.optional(Schema.Boolean), +}) + +const OpenAICompletions = Schema.Struct({ + type: Schema.Literal("openai/completions"), + url: Schema.String, + reasoning: Schema.Union([ + Schema.Struct({ + type: Schema.Literal("reasoning_content"), + }), + Schema.Struct({ + type: Schema.Literal("reasoning_details"), + }), + ]).pipe(Schema.optional), +}) +export type OpenAICompletions = typeof OpenAICompletions.Type + +const AISDK = Schema.Struct({ + type: Schema.Literal("aisdk"), + package: Schema.String, +}) + +const AnthropicMessages = Schema.Struct({ + type: Schema.Literal("anthropic/messages"), + url: Schema.String, +}) + +const UnknownEndpoint = Schema.Struct({ + type: Schema.Literal("unknown"), +}) + +export const Endpoint = Schema.Union([ + UnknownEndpoint, + OpenAIResponses, + OpenAICompletions, + AnthropicMessages, + AISDK, +]).pipe(Schema.toTaggedUnion("type")) +export type Endpoint = typeof Endpoint.Type + +export const Options = Schema.Struct({ + headers: Schema.Record(Schema.String, Schema.String), + body: Schema.Record(Schema.String, Schema.Any), +}) +export type Options = typeof Options.Type + +export class Info extends Schema.Class("ProviderV2.Info")({ + id: ID, + name: Schema.String, + enabled: Schema.Boolean, + env: Schema.String.pipe(Schema.Array), + endpoint: Endpoint, + options: Options, +}) { + static empty(providerID: ID) { + return new Info({ + id: providerID, + name: providerID, + enabled: false, + env: [], + endpoint: { + type: "unknown", + }, + options: { + headers: {}, + body: {}, + }, + }) + } +} + +export class NotFound extends Schema.TaggedErrorClass("ProviderV2.NotFound")("ProviderV2.NotFound", { + providerID: ID, +}) {} +``` + +## Model Schema + +```ts +export const ID = Schema.String.pipe(Schema.brand("ModelV2.ID")) +export type ID = typeof ID.Type + +export const VariantID = Schema.String.pipe(Schema.brand("VariantID")) +export type VariantID = typeof VariantID.Type + +export const Family = Schema.String.pipe(Schema.brand("Family")) +export type Family = typeof Family.Type + +export const Capabilities = Schema.Struct({ + tools: Schema.Boolean, + input: Schema.String.pipe(Schema.Array), + output: Schema.String.pipe(Schema.Array), +}) +export type Capabilities = typeof Capabilities.Type + +export const Variant = Schema.Struct({ + id: VariantID, + ...ProviderV2.Options.fields, +}) +export type Variant = typeof Variant.Type + +export const Cost = Schema.Struct({ + tier: Schema.Struct({ + type: Schema.Literal("context"), + size: Schema.Int, + }).pipe(Schema.optional), + input: Schema.Finite, + output: Schema.Finite, + cache: Schema.Struct({ + read: Schema.Finite, + write: Schema.Finite, + }), +}) +export type Cost = typeof Cost.Type + +export const Limit = Schema.Struct({ + context: Schema.Int, + input: Schema.Int.pipe(Schema.optional), + output: Schema.Int, +}) +export type Limit = typeof Limit.Type + +export const Ref = Schema.Struct({ + id: ID, + providerID: ProviderV2.ID, + variant: VariantID, +}) +export type Ref = typeof Ref.Type + +export class Info extends Schema.Class("ModelV2.Info")({ + id: ID, + providerID: ProviderV2.ID, + family: Family.pipe(Schema.optional), + name: Schema.String, + endpoint: ProviderV2.Endpoint, + options: Schema.Struct({ + ...ProviderV2.Options.fields, + variant: Schema.String.pipe(Schema.optional), + }), + capabilities: Capabilities, + variants: Variant.pipe(Schema.Array), + time: Schema.Struct({ + released: DateTimeUtcFromMillis, + }), + cost: Cost.pipe(Schema.Array), + status: Schema.Literals(["alpha", "beta", "deprecated", "active"]), + limit: Limit, +}) { + static empty(providerID: ProviderV2.ID, modelID: ID) { + return new Info({ + id: modelID, + providerID, + name: modelID, + endpoint: { + type: "unknown", + }, + capabilities: { + tools: false, + input: [], + output: [], + }, + options: { + headers: {}, + body: {}, + }, + variants: [], + time: { + released: DateTime.makeUnsafe(0), + }, + cost: [], + status: "active", + limit: { + context: 0, + output: 0, + }, + }) + } +} +``` + +## Catalog Interface + +```ts +export interface Interface { + readonly provider: { + readonly get: (providerID: ProviderV2.ID) => Effect.Effect> + readonly update: (providerID: ProviderV2.ID, fn: (provider: Draft) => void) => Effect.Effect + readonly remove: (providerID: ProviderV2.ID) => Effect.Effect + readonly all: () => Effect.Effect + readonly available: () => Effect.Effect + } + + readonly model: { + readonly get: (providerID: ProviderV2.ID, modelID: ModelV2.ID) => Effect.Effect> + readonly update: ( + providerID: ProviderV2.ID, + modelID: ModelV2.ID, + fn: (model: Draft) => void, + ) => Effect.Effect + readonly remove: (providerID: ProviderV2.ID, modelID: ModelV2.ID) => Effect.Effect + readonly all: () => Effect.Effect + readonly available: () => Effect.Effect + readonly default: () => Effect.Effect> + readonly small: (providerID: ProviderV2.ID) => Effect.Effect> + } +} +``` + +`ProviderV2.Info.enabled` is stored provider state. Provider plugins set this field after checking env, auth, config, or provider-specific availability. + +`ProviderV2.Endpoint` includes `{ type: "unknown" }`. `CatalogV2.model.get()` and `CatalogV2.model.all()` resolve `unknown` endpoints from the provider before returning models. + +Model storage is nested by provider because model ids are only unique within a provider. + +```ts +type ProviderRecord = { + provider: ProviderV2.Info + models: HashMap.HashMap +} + +let records = HashMap.empty() +``` + +`ModelV2.Info` does not have an `enabled` field. Model availability is derived by `CatalogV2.model.available()` from provider state and model status. + +```ts +const available = provider.enabled && model.status !== "deprecated" +``` + +## Plugin Interface + +```ts +export type Definition = Effect.Effect< + { + readonly order: number + readonly hooks: HookFunctions + }, + never, + R +> + +export interface Interface { + readonly add: (input: { id: ID; definition: Definition }) => Effect.Effect + + readonly remove: (id: ID) => Effect.Effect + + readonly trigger: (name: Name, input: HookInput) => Effect.Effect> +} +``` + +## Plugin Order + +```ts +export const Order = { + modelsDev: 0, + env: 10, + auth: 20, + provider: 30, + config: 40, + discovery: 50, +} as const +``` + +## Built-In Plugins + +```ts +export const ModelsDevPlugin: PluginV2.Definition + +export const EnvPlugin: PluginV2.Definition + +export const AuthPlugin: PluginV2.Definition + +export const ConfigPlugin: PluginV2.Definition + +export const AnthropicPlugin: PluginV2.Definition + +export const OpenRouterPlugin: PluginV2.Definition + +export const AmazonBedrockPlugin: PluginV2.Definition + +export const GoogleVertexPlugin: PluginV2.Definition + +export const GitLabPlugin: PluginV2.Definition + +export const GitLabDiscoveryPlugin: PluginV2.Definition +``` + +## Plugin Hooks + +```ts +export type Hooks = { + init: {} + + "provider.update": { + provider: Draft + cancel: boolean + } + + "model.update": { + model: Draft + cancel: boolean + } +} +``` diff --git a/specs/v2/todo.md b/specs/v2/todo.md index 77c650e55fc..ca38931f8f9 100644 --- a/specs/v2/todo.md +++ b/specs/v2/todo.md @@ -2,9 +2,11 @@ ok we need to work towards a launch of v2 so we can get out of this rebuild phase -## Kill Hono - Kit +## Post-Hono cleanup - Kit -Hono needs to go away so zod can go away. this is almost done +The opencode server has moved to the Effect HttpApi backend. Remaining work is +mostly cleanup: delete compatibility shims, shrink Zod surfaces, and simplify +test harnesses that used to compare Hono and HttpApi behavior. ## New Data Mode - Dax